diff options
Diffstat (limited to 'gcc/cobol')
47 files changed, 18557 insertions, 10911 deletions
diff --git a/gcc/cobol/ChangeLog b/gcc/cobol/ChangeLog index be421d2..67518ac 100644 --- a/gcc/cobol/ChangeLog +++ b/gcc/cobol/ChangeLog @@ -1,3 +1,2487 @@ +2025-12-03 Jakub Jelinek <jakub@redhat.com> + + * lang.opt: Make sure all option descriptions are terminated with + a dot. + (preprocess): Capitalize first letter of option description. + +2025-12-01 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/119329 + PR cobol/119331 + PR cobol/120786 + * Make-lang.in: Add cobol/messages.o to cobol1 sources. + * cbldiag.h (yywarn): Remove function. + (struct cbl_loc_t): Introduce new location type independent of Bison. + (enum cbl_diag_id_t): Enumerate diagnostic messages. + (cbl_message): New function. + (dialect_ok): Test for dialect, emit standard message. + (dialect_not_ok): Emit standard message if syntax excluded by dialect. + (dialect_error): Remove function. + (cbl_unimplementedw): Use cbl_diag_id_t. + (cbl_unimplemented): Whitespace. + * cdf.y: Update token values. + * cobol1.cc (enable_exceptions): Use cbl_message. + (cobol_warning): Declare function. + (cobol_langhook_handle_option): Add 44 new warning options. + (cobol_langhook_type_for_mode): Remove function. + * except.cc (cbl_enabled_exception_t::dump): Remove function. + * exceptg.h (class exception_turn_t): Use cbl_diag_id_t. + * gcobol.1: Document dialect syntax and new warning options. + * genapi.cc (parser_label_label): Remove unused warning. + * gengen.cc (gg_find_field_in_struct): Use cbl_internal_error. + (gg_printf): Same. + (gg_fprintf): Same. + (gg_define_function): Same. + (gg_get_function_decl): Same. + (gg_call_expr): Same. + (gg_call): Same. + * lang-specs.h: Add warning options. + * lang.opt: Add ISO dialect and options. + * lexio.cc (parse_replacing_term): Use cbl_message. + (parse_replacing_pair): Same. + (preprocess_filter_add): Same. + (cdftext::echo_input): Same. + (cdftext::lex_open): Same. + (cdftext::open_input): Same. + * messages.cc: New file implements cbl_message. + * parse.y: Use cbl_message. + * parse_ante.h (dialect_proscribed): Remove function. + (parser_move_carefully): Use dialect_ok. + (goodnight_gracie): Convert warning to debug message. + * scan.l: Use dialect_ok. + * scan_ante.h (scanner_parsing): Use cbl_diag_id_t. + (scanner_parsing_toggle): Same. + (scanner_parsing_pop): Same. + (verify_ws): Same. + (level_of): Same. + (typed_name): Same. + (integer_of): Same. + * scan_post.h (datetime_format_of): Use cbl_internal_error. + (prelex): Emit only debug messages. + * show_parse.h: Use cbl_internal_error. + * symbols.cc (symbols_update): Remove dialect test because parser's problem. + (cbl_field_t::internalize): Use cbl_message. + * symbols.h (enum cbl_dialect_t): Add ISO to cbl_dialect_t. + (cbl_dialect_str): Recognize ISO dialect. + (dialect_has): New function. + (cbl_diagnostic_kind): New function. + (cbl_dialect_kind): New function. + (struct cbl_alphabet_t): Emit only debug message. + * token_names.h: Regenerate. + * util.cc (gb4): Emit only debug message. + (current_token_location): Add overload to set token_location. + (yywarn): Remove function. + (cobol_fileline_set): Use cbl_message. + (cobol_parse_files): Same. + (cbl_message): New diagnostic message function uses cbl_diag_id_t. + (cbl_diagnostic_kind): New function. + (cbl_diagnostic_option): New function. + (cbl_unimplementedw): Use cbl_diag_id_t. + (dialect_error): Remove function. + * util.h (cbl_message): Remove obsolete prototype for cbl_message. + +2025-11-30 Andrew Pinski <andrew.pinski@oss.qualcomm.com> + + * lang.opt.urls: Regenerate. + +2025-11-17 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/122702 + * Make-lang.in: Move stored-char-length.cbl to libgcobol. + +2025-11-17 Jakub Jelinek <jakub@redhat.com> + + * symbols.cc (symbol_table_init): Avoid arithmetics or + bitwise operations between enumerators from different enums. + +2025-11-15 Jakub Jelinek <jakub@redhat.com> + + PR cobol/122691 + * lexio.cc (parse_replace_pairs): Replace parsed.stmt.p with + parsed.stmt.size() ? parsed.stmt.p : "" in the last argument to + dbgmsg. + +2025-11-13 James K. Lowden <jklowden@cobolworx.com> + + * cdf.y: Install literals in symbol table. + * genapi.cc (parser_alphabet): Use std::string for currency. + (initialize_the_data): Rely on constructor. + (parser_file_add): Better #pragma message. + (parser_exception_file): Return early if not generating code. + * parse.y: Allow library programs to act as functions. + * parse_ante.h (dialect_proscribed): Standardize message. + (intrinsic_call_2): Correct s/fund/func/ misspelling. + * scan.l: Comment. + * symbols.cc (symbols_update): Add unreachable assertion. + (symbol_field_parent_set): Reduce error to debug message. + (cdf_literalize): Declare. + (symbol_table_init): Insert CDF constants as literals. + * symbols.h (cbl_dialect_str): Provide string values for enum. + (is_working_storage): Remove function. + (struct cbl_field_data_t): Add manhandle_initial for Numeric Edited. + (struct cbl_field_t): Initialize name to zeros. + (struct cbl_section_t): Delete unused attr() function. + (symbol_unique_index): Declare. + * token_names.h: Regenerate. + * util.cc (cdf_literalize): Construct a cbl_field_t from a CDF literal. + (symbol_unique_index): Supply "globally" unique number for a program. + +2025-11-10 Sandra Loosemore <sloosemore@baylibre.com> + + PR other/122243 + * lang.opt.urls: Regenerated. + +2025-11-06 Robert Dubner <rdubner@symas.com> + James K. Lowden <jklowden@cobolworx.com> + + * Make-lang.in: Repair documentation generation. + * cdf.y: Changes to tokens. + * cobol1.cc (cobol_langhook_handle_option): Add comment. + * genapi.cc (function_pointer_from_name): Use data.original() for + function name. + (parser_initialize_programs): Likewise. + (cobol_compare): Make sure encodings of comparands are the same. + (move_tree): Change name of DEFAULT_SOURCE_ENCODING macro. + (parser_enter_program): Typo. + (psa_FldLiteralN): Break out dirty_to_binary() support routine. + (dirty_to_binary): Likewise. + (parser_alphabet): Rename 'alphabet' to 'collation_sequence'. + (parser_allocate): Change wsclear() to be uint32_t instead of char. + (parser_label_label): Formatting. + (parser_label_goto): Likewise. + (get_the_filename): Breakout get_the_filename(), which handles + encoding. + (parser_file_open): Likewise. + (set_up_delete_file_label): Implement DELETE FILE (Format 2). + (parser_file_delete_file): Likewise. + (parser_file_delete_on_exception): Likewise. + (parser_file_delete_not_exception): Likewise. + (parser_file_delete_end): Likewise. + (parser_call): Use data.original(). + (parser_entry): Use data.original(). + (mh_source_is_literalN): Convert from + sourceref.field->codeset.encoding. + (binary_initial_from_float128): Change to "binary_initial". + (binary_initial): Calculate in FIXED_WIDE_INT(128) instead of + REAL_VALUE_TYPE. + (digits_from_int128): New routine uses binary_initial. + (digits_from_float128): Removed. Kept as comment for reference. + (initial_from_initial): Use binary_initial. + (actually_create_the_static_field): Use correct encoding. + (parser_symbol_add): Likewise. + * genapi.h (parser_file_delete_file): Implement FILE DELETE. + (parser_file_delete_on_exception): Implement FILE DELETE. + (parser_file_delete_not_exception): Implement FILE DELETE. + (parser_file_delete_end): Implement FILE DELETE. + * genmath.cc: Include charmaps.h. + * genutil.cc (get_literal_string): Change name of + DEFAULT_SOURCE_ENCODING macro. + * parse.y: Token changes; numerous changes in support of encoding; + support for DELETE FILE. + * parse_ante.h (name_of): Use data.original(). + (class prog_descr_t): Support of locales. + (current_options): Formatting. + (current_encoding): Formatting. + (current_program_index): Formatting. + (current_section): Formatting. + (current_paragraph): Formatting. + (is_integer_literal): Use correct encoding. + (value_encoding_check): Handle encoding changes. + (alphabet_add): Likewise. + (data_division_ready): Likewise. + * scan.l: Use data.original(). + * show_parse.h: Use correct encoding. + * symbols.cc (elementize): Likewise. + (symbol_elem_cmp): Handle locale. + (struct symbol_elem_t): Likewise. + (symbol_locale): Likewise. + (field_str): Change DEFAULT_SOURCE_ENCODING macro name. + (symbols_alphabet_set): Formatting. + (symbols_update): Modify consistency checks. + (symbol_locale_add): Locale support. + (cbl_locale_t::cbl_locale_t): Locale support. + (cbl_alphabet_t::cbl_alphabet_t): New structure. + (cbl_alphabet_t::reencode): Formatting. + (cbl_alphabet_t::assign): Change name of collation_sequence. + (cbl_alphabet_t::also): Likewise. + (new_literal_add): Anticipate the need for four-byte characters. + (guess_encoding): Eliminate. + (cbl_field_t::internalize): Refine conversion of data.initial to + specified encoding. + * symbols.h (enum symbol_type_t): Add SymLocale. + (struct cbl_field_data_t): Incorporate data.orig. + (struct cbl_field_t): Likewise. + (struct cbl_delete_file_t): New structure. + (struct cbl_label_t): Incorporate cbl_delete_file_t. + (struct cbl_locale_t): Support for locale. + (hex_decode): Comment. + (struct cbl_alphabet_t): Incorporate locale; change variable name + to collation_sequence. + (struct symbol_elem_t): Incorporate locale. + (cbl_locale_of): Likewise. + (cbl_alphabet_of): Likewise. + (symbol_locale_add): Likewise. + (wsclear): Type is now uint32_t instead of char. + * util.cc (symbol_type_str): Incorporate locale. + (cbl_field_t::report_invalid_initial_value): Change test so that + pure PIC A() variables are limited to [a-zA-Z] and space. + (valid_move): Use DEFAULT_SOURCE_ENCODING macro. + (cobol_filename): Formatting. + +2025-10-26 Eric Botcazou <ebotcazou@adacore.com> + + * Make-lang.in ($(srcdir)/cobol/token_names.h): Silence recipe. + +2025-10-23 Robert Dubner <rdubner@symas.com> + + * genapi.cc (parser_alphabet): Alphabet encoding. + (parser_alphabet_use): Likewise. + (parser_xml_parse): Use correct debugging macro; encoding. + (parser_xml_on_exception): Likewise. + (parser_xml_not_exception): Likewise. + (parser_xml_end): Likewise. + (initialize_the_data): Encoding. + (parser_label_label): Debugging macros. + (parser_label_goto): Likewise. + (parser_file_add): Encoding. + (parser_intrinsic_call_1): Special handling for __gg__char. + (parser_intrinsic_call_2): Formatting. + * parse.y: Response from FUNCTION ORD is flagged "unsigned". + * symbols.cc (cbl_alphabet_t::reencode): Establish + low_char & high_char. + * symbols.h (struct cbl_alphabet_t): Likewise. + +2025-10-23 Robert Dubner <rdubner@symas.com> + James K. Lowden <jklowden@cobolworx.com> + + * Make-lang.in: Incorporate new token_names.h file. + * cdf.y: Modify tokens. + * gcobol.1: Document XML PARSE statement + * genapi.cc (parser_enter_program): Verify that every goto has a + matching label. + (parser_end_program): Likewise. + (parser_alphabet): Refine handling codeset encodings. + (parser_alphabet_use): Likewise. + (label_fetch): Moved from later in the source code. + (parser_xml_parse): New routine for XML PARSE. + (parser_xml_on_exception): Likewise. + (parser_xml_not_exception): Likewise. + (parser_xml_end): Likewise. + (parser_label_label): Verify goto/label matching. + (parser_label_goto): Likewise. + (parser_entry): Minor change to SHOW_PARSE report. + * genapi.h (parser_alphabet): Set parameter to const. + (parser_xml_parse): Declare new function. + (parser_xml_on_exception): Likewise. + (parser_xml_not_exception): Likewise. + (parser_xml_end): Likewise. + (parser_label_addr): Likewise. + * parse.y: label_pair_t structure; locale processing; new token + processing for alphabets and XML PARSE. + * parse_ante.h (name_of): Return field->name when initial is NULL. + (new_tempnumeric): Make signable_e optional. + (ast_save_locale): New function. + (data_division_ready): Warning for "no alphabet". + * scan.l: Repair interpretation of BINARY, COMP, COMP-4, and + COMP-5. + * scan_ante.h (struct bint_t): Likewise. + * scan_post.h (current_tokens_t::tokenset_t::tokenset_t): + Include token_names.h. + * symbols.cc (symbols_alphabet_set): Revert to prior alphabet + determination. + (symbol_table_init): New XML special registers. + (new_temporary): Make signable_e controllable, not fixed. + * symbols.h (__gg__encoding_iconv_valid): New declaration. + (enum cbl_label_type_t): New LblXml label type. + (struct cbl_xml_parse_t): + (struct cbl_label_t): Implement XML PARSE. + (new_temporary): Incorporate boolean for signable_e. + (symbol_elem_of): Change label field type handling. + (cbl_section_of): Likewise. + (cbl_field_of): Likewise. + (cbl_label_of): Likewise. + (cbl_special_name_of): Likewise. + (cbl_alphabet_of): Likewise. + (cbl_file_of): Likewise. + * token_names.h: New file. + * util.cc (gcc_location_set_impl): Improve location_t calculations + when entering and leaving COPYBOOKs. + +2025-10-19 Robert Dubner <rdubner@symas.com> + + * genapi.cc (move_tree): Formatting. + (parser_enter_file): Incorporate global __gg__entry_label. + (enter_program_common): Remove calls to alphabet overrides. + (parser_alphabet): Change cbl_alphabet_e handling. + (parser_alphabet_use): Likewise. + (initialize_the_data): Likewise. + (establish_using): Process passed parameters in a subroutine. + (parser_division): Remove in-line parameter processing; + call establish_using() instead. Check for __gg__entry_label. + (parser_file_add): Temporary workaround for charset encoding. + (parser_file_open): Likewise. + (create_and_call): Push/pop program state around call to external. + (parser_entry): Implement new ENTRY statement feature. + (mh_source_is_literalN): Formatting. + * genapi.h (parser_entry): New ENTRY statement. + * gengen.cc (gg_create_goto_pair): Formatting. + (gg_goto_label_decl): Remove. + * gengen.h (gg_goto_label_decl): Remove. + * genutil.cc (internal_codeset_is_ebcdic): Remove. + * genutil.h (internal_codeset_is_ebcdic): Remove. + * symbols.cc (symbols_alphabet_set): Restrict alphabet scan to + program. + * symbols.h (is_elementary): Use defined constants instead of + explicit 'A'and 'N' + +2025-10-10 Robert Dubner <rdubner@symas.com> + + * cdf.y: In support of the described changes. + * gcobol.1: Likewise. + * genapi.cc (level_88_helper): Likewise. + (get_level_88_domain): Likewise. + (get_class_condition_string): Likewise. + (initialize_variable_internal): Likewise. + (gg_default_qualification): Likewise. + (cobol_compare): Likewise. + (move_tree): Likewise. + (move_tree_to_field): Likewise. + (psa_FldBlob): Likewise. + (parser_accept_date_yymmdd): Likewise. + (parser_accept_date_yyyymmdd): Likewise. + (parser_accept_date_yyddd): Likewise. + (parser_accept_date_yyyyddd): Likewise. + (parser_accept_date_dow): Likewise. + (parser_accept_date_hhmmssff): Likewise. + (parser_alphabet): Likewise. + (parser_alphabet_use): Likewise. + (parser_display_internal): Likewise. + (parser_display): Likewise. + (is_valuable): Likewise. + (parser_division): Likewise. + (parser_relop_long): Likewise. + (parser_setop): Likewise. + (parser_set_conditional88): Likewise. + (parser_file_add): Likewise. + (parser_file_open): Likewise. + (create_and_call): Likewise. + (parser_call): Likewise. + (mh_identical): Likewise. + (mh_source_is_literalN): Likewise. + (picky_memcpy): Likewise. + (mh_numeric_display): Likewise. + (mh_source_is_group): Likewise. + (mh_source_is_literalA): Likewise. + (move_helper): Likewise. + (initial_from_initial): Likewise. + (actually_create_the_static_field): Likewise. + (psa_FldLiteralA): Likewise. + (parser_symbol_add): Likewise. + * genmath.cc (arithmetic_operation): Likewise. + * genutil.cc (get_binary_value): Likewise. + (get_literal_string): Likewise. + * genutil.h (EBCDIC_MINUS): Likewise. + (EBCDIC_PLUS): Likewise. + (EBCDIC_ZERO): Likewise. + (EBCDIC_NINE): Likewise. + * parse.y: Likewise. + * parse_ante.h (name_of): Likewise. + (class prog_descr_t): Likewise. + (current_encoding): Likewise. + (needs_picture): Likewise. + (is_callable): Likewise. + (field_attr_str): Likewise. + (value_encoding_check): Likewise. + (field_alloc): Likewise. + (file_add): Likewise. + * scan.l: Likewise. + * structs.cc (create_cblc_field_t): Likewise. + * symbols.cc (elementize): Likewise. + (cbl_field_attr_str): Likewise. + (is_variable_length): Likewise. + (field_str): Likewise. + (extend_66_capacity): Likewise. + (assert): Likewise. + (symbols_update): Likewise. + (symbol_field_parent_set): Likewise. + (add_token): Likewise. + (symbol_table_init): Likewise. + (symbol_field_add): Likewise. + (symbol_field_forward_add): Likewise. + (symbol_field_same_as): Likewise. + (cbl_alphabet_t::reencode): Likewise. + (new_temporary_impl): Likewise. + (parser_symbol_add2): Likewise. + (new_literal_add): Likewise. + (temporaries_t::literal): Likewise. + (new_literal): Likewise. + (standard_internal): Likewise. + (new_temporary): Likewise. + (cbl_field_t::holds_ascii): Likewise. + (cbl_field_t::is_ascii): Likewise. + (cbl_field_t::internalize): Likewise. + (symbol_label_add): Likewise. + (symbol_label_section_exists): Likewise. + (cbl_occurs_t::subscript_ok): Likewise. + (cbl_file_t::deforward): Likewise. + (has_value): Likewise. + * symbols.h (is_numeric): Likewise. + (__gg__encoding_iconv_name): Likewise. + (current_encoding): Likewise. + (struct cbl_field_t): Likewise. + (new_literal): Likewise. + (class temporaries_t): Likewise. + (struct function_descr_t): Likewise. + (hex_decode): Likewise. + (struct cbl_alphabet_t): Likewise. + (struct cbl_file_t): Likewise. + * symfind.cc (field_structure): Likewise. + (erase_symbol_map_fwds): Likewise. + (symbol_find): Likewise. + * token_names.h: Likewise. + * util.cc (cbl_field_type_str): Likewise. + (is_elementary): Likewise. + (symbol_field_type_update): Likewise. + (cbl_field_t::report_invalid_initial_value): Likewise. + (valid_move): Likewise. + (valid_picture): Likewise. + (type_capacity): Likewise. + (gcc_location_set_impl): Likewise. + (cbl_unimplementedw): Likewise. + +2025-09-05 Robert Dubner <rdubner@symas.com> + + * genapi.cc (trace1_init): Prepend two internal variables with + underscore. + (initialize_variable_internal): Use new register_e attribute. + (psa_global): Use "__ggsr__" prefix to identify special registers + (parser_symbol_add): Use new register_e attribute. + * symbols.cc (cbl_field_attr_str): Likewise. + (symbol_table_init): Likewise. + (is_register_field): Eliminated in favor of (attr & register_e). + * symbols.h (is_register_field): Likewise. + +2025-08-20 Robert Dubner <rdubner@symas.com> + + * genutil.cc (get_binary_value): Fix a comment. + * parse.y: udf_args_valid(): Fix loc calculation. + * symbols.cc (assert): extend_66_capacity(): Avoid assert(e < e2) in + -O0 build until symbol_table expansion is fixed. + +2025-08-15 Robert Dubner <rdubner@symas.com> + + * genapi.h (parser_call_exception_end): Remove obsolete comment. + * structs.cc (create_cbl_enabled_exception_t): + Remove cbl_enabled_exception_type_node; + remove create_cbl_enabled_exception_t(). + (create_our_type_nodes): Likewise. + * structs.h (GTY): Likewise. + +2025-08-13 Robert Dubner <rdubner@symas.com> + + * genutil.cc (get_binary_value): Use the new routine. + +2025-08-13 Robert Dubner <rdubner@symas.com> + + * genutil.cc (get_binary_value): Use the new routine. + +2025-08-12 Robert Dubner <rdubner@symas.com> + + * genapi.cc (compare_binary_binary): Formatting. + (cobol_compare): Formatting. + (mh_numeric_display): Rewrite "move ND to ND" algorithm. + (initial_from_initial): Proper initialization of EBCDIC ND variables. + * genmath.cc (fast_add): Delete comment. + * genutil.cc (get_binary_value): Modify for updated EBCDIC. + +2025-08-07 Robert Dubner <rdubner@symas.com> + + * cbldiag.h (location_dump): Source code formatting. + * parse.y: error_msg formatting. + * scan.l: Remove UTF-8 character from regex pattern. + * scan_ante.h (numstr_of): error_msg formatting. + * show_parse.h (class ANALYZE): Suppress cppcheck error. + * util.cc (cbl_field_t::report_invalid_initial_value): + error_msg formatting. + +2025-08-02 Jakub Jelinek <jakub@redhat.com> + + * parse.y (intrinsic): Use %td format specifier with no cast on + argument instead of %ld with cast to long. + * scan_ante.h (numstr_of): Likewise. + * util.cc (cbl_field_t::report_invalid_initial_value): Likewise. + +2025-08-01 Robert Dubner <rdubner@symas.com> + + PR cobol/119324 + * cbldiag.h (location_dump): Inline suppression of knownConditionTrueFalse. + * genapi.cc (parser_statement_begin): Combine two if() statements. + * genutil.cc (get_binary_value): File-level suppression of duplicateBreak. + * symbols.cc (symbol_elem_cmp): File-level suppression of duplicateBreak. + +2025-07-31 Robert Dubner <rdubner@symas.com> + + PR cobol/120244 + * genapi.cc (get_level_88_domain): Increase array size for final byte. + (psa_FldLiteralA): Use correct length in build_string_literal call. + * scan.l: Use a loop instead of std:transform to avoid EOF overrun. + * scan_ante.h (binary_integer_usage): Use a variable-length buffer. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * util.cc: Update for diagnostic_t becoming + enum class diagnostics::kind. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * util.cc: Update for renaming of diagnostic_option_id to + diagnostics::option_id. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * util.cc: Remove redundant #includes + +2025-07-24 Robert Dubner <rdubner@symas.com> + + PR cobol/119231 + * gcobolspec.cc: (lang_specific_driver): Pass OPT_static through. + Handle -static and -static-libgcobol properly. + +2025-07-23 Robert Dubner <rdubner@symas.com> + + * genapi.cc (leave_procedure): Adjust location_t for PERFORM. + (parser_perform_times): Likewise. + (internal_perform_through_times): Likewise. + (perform_outofline_before_until): Likewise. + (perform_outofline_after_until): Likewise. + (perform_outofline_testafter_varying): Likewise. + (perform_outofline_before_varying): Likewise. + +2025-07-21 Robert Dubner <rdubner@symas.com> + James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120402 + * Make-lang.in: Elminate commented-out scripting. + * cbldiag.h (_CBLDIAG_H): Change #if 0 to #if GCOBOL_GETENV + (warn_msg): Add printf attributes. + (location_dump): Add debugging message. + * cdf.y: Improved linemap tracking. + * genapi.cc (treeplet_fill_source): const attribute for formal parameter. + (insert_nop): Created to consolidate var_decl_nop writes. + (build_main_that_calls_something): Move generation to the end of executable. + (level_88_helper): Formatting. + (parser_call_targets_dump): Formatting. + (function_pointer_from_name): const attribute for formal parameter. + (parser_initialize_programs): const attribute for formal parameter. + (parser_statement_begin): Improved linemap handling. + (section_label): Improved linemap handling. + (paragraph_label): Improved linemap handling. + (pseudo_return_pop): Improved linemap handling. + (leave_procedure): Formatting. + (parser_enter_section): Improved linemap handling. + (parser_enter_paragraph): Improved linemap handling. + (parser_perform): Formatting. + (parser_leave_file): Move creation of main() to this routine. + (parser_enter_program): Move creation of main from here to leave_file. + (parser_accept): Formatting. const attribute for formal parameter. + (parser_accept_command_line): const attribute for formal parameter. + (parser_accept_command_line_count): const attribute for formal parameter. + (parser_accept_envar): Likewise. + (parser_set_envar): Likewise. + (parser_display): Likewise. + (get_exhibit_name): Implement EXHIBIT verb. + (parser_exhibit): Likewise. + (parser_sleep): const attribute for formal parameter. + (parser_division): Improved linemap handling. + (parser_classify): const attribute for formal parameter. + (create_iline_address_pairs): Improved linemap handling. + (parser_perform_start): Likewise. + (perform_inline_until): Likewise. + (perform_inline_testbefore_varying): Likewise. + (parser_perform_until): Likewise. + (parser_perform_inline_times): Likewise. + (parser_intrinsic_subst): const attribute for formal parameter. + (parser_file_merge): Formatting. + (create_and_call): Improved linemap handling. + (mh_identical): const attribute for formal parameter. + (mh_numeric_display): const attribute for formal parameter. + (mh_little_endian): Likewise. + (mh_source_is_group): Likewise. + (psa_FldLiteralA): Formatting. + * genapi.h (parser_accept): const attribute for formal parameter. + (parser_accept_envar): Likewise. + (parser_set_envar): Likewise. + (parser_accept_command_line): Likewise. + (parser_accept_command_line_count): Likewise. + (parser_add): Likewise. + (parser_classify): Likewise. + (parser_sleep): Likewise. + (parser_exhibit): Likewise. + (parser_display): Likewise. + (parser_initialize_programs): Likewise. + (parser_intrinsic_subst): Likewise. + * gengen.cc (gg_assign): Improved linemap handling. + (gg_add_field_to_structure): Likewise. + (gg_define_from_declaration): Likewise. + (gg_build_relational_expression): Likewise. + (gg_goto_label_decl): Likewise. + (gg_goto): Likewise. + (gg_printf): Likewise. + (gg_fprintf): Likewise. + (gg_memset): Likewise. + (gg_memchr): Likewise. + (gg_memcpy): Likewise. + (gg_memmove): Likewise. + (gg_strcpy): Likewise. + (gg_strcmp): Likewise. + (gg_strncmp): Likewise. + (gg_return): Likewise. + (chain_parameter_to_function): Likewise. + (gg_define_function): Likewise. + (gg_get_function_decl): Likewise. + (gg_call_expr): Likewise. + (gg_call): Likewise. + (gg_call_expr_list): Likewise. + (gg_exit): Likewise. + (gg_abort): Likewise. + (gg_strlen): Likewise. + (gg_strdup): Likewise. + (gg_malloc): Likewise. + (gg_realloc): Likewise. + (gg_free): Likewise. + (gg_set_current_line_number): Likewise. + (gg_get_current_line_number): Likewise. + (gg_insert_into_assembler): Likewise. + (token_location_override): Likewise. + (gg_token_location): Likewise. + * gengen.h (location_from_lineno): Likewise. + (gg_set_current_line_number): Likewise. + (gg_get_current_line_number): Likewise. + (gg_token_location): Likewise. + (current_token_location): Likewise. + (current_location_minus_one): Likewise. + (current_location_minus_one_clear): Likewise. + (token_location_override): Likewise. + * genmath.cc (fast_divide): const attribute for formal parameter. + * genutil.cc (get_and_check_refstart_and_reflen): Likewise. + (get_data_offset): Likewise. + (refer_refmod_length): Likewise. + (refer_offset): Likewise. + (refer_size): Likewise. + (refer_size_dest): Likewise. + (refer_size_source): Likewise. + (qualified_data_location): Likewise. + * genutil.h (refer_offset): Likewise. + (refer_size_source): Likewise. + (refer_size_dest): Likewise. + (qualified_data_location): Likewise. + * parse.y: EVALUATE token; Implement EXHIBIT verb; + Improved linemap handling. + * parse_ante.h (input_file_status_notify): Improved linemap handling. + (location_set): Likewise. + * scan.l: PICTURE string validation. + * scan_ante.h (class picture_t): PICTURE string validation. + (validate_picture): Likewise. + * symbols.cc (symbol_currency): Revised default currency handling. + * symbols.h (symbol_currency): Likewise. + * util.cc (location_from_lineno): Improved linemap handling. + (current_token_location): Improved linemap handling. + (current_location_minus_one): Improved linemap handling. + (current_location_minus_one_clear): Improved linemap handling. + (gcc_location_set_impl): Improved linemap handling. + (warn_msg): Improved linemap handling. + * util.h (cobol_lineno): Improved linemap handling. + +2025-07-15 Jakub Jelinek <jakub@redhat.com> + Jason Merrill <jason@redhat.com> + + PR c/44677 + * gcobolspec.cc (lang_specific_driver): Remove unused but set variable + n_cobol_files. + +2025-07-14 Robert Dubner <rdubner@symas.com> + + * cobol1.cc (cobol_langhook_handle_option): Eliminate cppcheck warnings. + * dts.h: Likewise. + * except.cc (cbl_enabled_exceptions_t::dump): Likewise. + * gcobolspec.cc (lang_specific_driver): Likewise. + * genapi.cc (parser_file_merge): Likewise. + * gengen.cc (gg_unique_in_function): Likewise. + (gg_declare_variable): Likewise. + (gg_peek_fn_decl): Likewise. + (gg_define_function): Likewise. + * genmath.cc (set_up_on_exception_label): Likewise. + (set_up_compute_error_label): Likewise. + (arithmetic_operation): Likewise. + (fast_divide): Likewise. + * genutil.cc (get_and_check_refstart_and_reflen): Likewise. + (get_depending_on_value_from_odo): Likewise. + (get_data_offset): Likewise. + (get_binary_value): Likewise. + (process_this_exception): Likewise. + (copy_little_endian_into_place): Likewise. + (refer_is_clean): Likewise. + (refer_fill_depends): Likewise. + * genutil.h (process_this_exception): Likewise. + (copy_little_endian_into_place): Likewise. + (refer_is_clean): Likewise. + * lexio.cc (check_push_pop_directive): Likewise. + (check_source_format_directive): Likewise. + (location_in): Likewise. + (lexer_input): Likewise. + (cdftext::lex_open): Likewise. + (lexio_dialect_mf): Likewise. + (valid_sequence_area): Likewise. + (cdftext::free_form_reference_format): Likewise. + (cdftext::segment_line): Likewise. + * lexio.h (struct span_t): Likewise. + * scan_ante.h (trim_location): Likewise. + * symbols.cc (symbol_elem_cmp): Likewise. + (symbol_alphabet): Likewise. + (end_of_group): Likewise. + (cbl_field_t::attr_str): Likewise. + (symbols_update): Likewise. + (symbol_typedef_add): Likewise. + (symbol_field_add): Likewise. + (new_temporary_impl): Likewise. + (symbol_label_section_exists): Likewise. + (symbol_program_callables): Likewise. + (file_status_status_of): Likewise. + * symfind.cc (is_data_field): Likewise. + (finalize_symbol_map2): Likewise. + (class in_scope): Likewise. + (symbol_match2): Likewise. + * util.cc (get_current_dir_name): Likewise. + (gb4): Likewise. + (class cdf_directives_t): Likewise. + (cbl_field_t::report_invalid_initial_value): Likewise. + (literal_subscript_oob): Likewise. + (cbl_refer_t::str): Likewise. + (date_time_fmt): Likewise. + (class unique_stack): Likewise. + (cobol_set_pp_option): Likewise. + (cobol_filename): Likewise. + (cobol_filename_restore): Likewise. + (gcc_location_set_impl): Likewise. + (ydferror): Likewise. + (error_msg_direct): Likewise. + (yyerror): Likewise. + (cbl_unimplemented_at): Likewise. + +2025-07-13 Robert Dubner <rdubner@symas.com> + + * Make-lang.in: Eliminate the .cc.o override. + * genapi.cc (level_88_helper): Eliminate cppcheck warning. + (get_level_88_domain): Likewise. + (get_class_condition_string): Likewise. + (parser_call_targets_dump): Likewise. + (parser_compile_ecs): Likewise. + (initialize_variable_internal): Likewise. + (move_tree): Likewise. + (combined_name): Likewise. + (assembler_label): Likewise. + (find_procedure): Likewise. + (parser_perform): Likewise. + (parser_perform_times): Likewise. + (internal_perform_through): Likewise. + (internal_perform_through_times): Likewise. + (psa_FldLiteralN): Likewise. + (psa_FldBlob): Likewise. + (parser_accept): Likewise. + (parser_accept_exception): Likewise. + (parser_accept_exception_end): Likewise. + (parser_accept_command_line): Likewise. + (parser_accept_envar): Likewise. + (parser_display_internal): Likewise. + (parser_display): Likewise. + (parser_assign): Likewise. + (parser_initialize_table): Likewise. + (parser_arith_error): Likewise. + (parser_arith_error_end): Likewise. + (parser_division): Likewise. + (label_fetch): Likewise. + (parser_label_label): Likewise. + (parser_label_goto): Likewise. + (parser_perform_start): Likewise. + (parser_perform_conditional): Likewise. + (parser_perform_conditional_end): Likewise. + (parser_perform_until): Likewise. + (parser_file_delete): Likewise. + (parser_intrinsic_subst): Likewise. + (create_lsearch_address_pairs): Likewise. + (parser_bsearch_start): Likewise. + (is_ascending_key): Likewise. + (parser_sort): Likewise. + (parser_file_sort): Likewise. + (parser_return_start): Likewise. + (parser_file_merge): Likewise. + (parser_string_overflow): Likewise. + (parser_unstring): Likewise. + (parser_string): Likewise. + (parser_call_exception): Likewise. + (create_and_call): Likewise. + (mh_identical): Likewise. + (move_helper): Likewise. + (binary_initial_from_float128): Likewise. + (initial_from_initial): Likewise. + (psa_FldLiteralA): Likewise. + (parser_local_add): Likewise. + (parser_symbol_add): Likewise. + * genapi.h (parser_display): Likewise. + * gengen.cc (gg_call_expr): Explict check for NULL_TREE. + (gg_call): Likewise. + * show_parse.h (SHOW_PARSE_LABEL_OK): Likewise. + (TRACE1_FIELD_VALUE): Likewise. + (CHECK_FIELD): Likewise. + (CHECK_FIELD2): Likewise. + (CHECK_LABEL): Likewise. + * util.cc (cbl_internal_error): Apply [[noreturn]] attribute. + * util.h (cbl_internal_error): Likewise. + +2025-07-11 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR cobol/120621 + * lexio.cc (parse_replace_pairs): Cast mfile.lineno() to fmt_size_t. + * parse.y (intrinsic): Print ptrdiff_t using %ld, cast arg to long. + * scan_ante.h (numstr_of): Print nx using %ld, cast arg to long. + * util.cc (cbl_field_t::report_invalid_initial_value): Print + ptrdiff_t using %ld, cast arg to long. + +2025-07-10 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120765 + * cdf.y: Extend grammar for new CDF syntax, relocate dictionary. + * cdfval.h (cdf_dictionary): Use new CDF dictionary. + * dts.h: Remove useless assignment, note incorrect behavior. + * except.cc: Remove obsolete EC state. + * gcobol.1: Document CDF in its own section. + * genapi.cc (parser_statement_begin): Use new EC state function. + (parser_file_merge): Same. + (parser_check_fatal_exception): Same. + * genutil.cc (get_and_check_refstart_and_reflen): Same. + (get_depending_on_value_from_odo): Same. + (get_data_offset): Same. + (process_this_exception): Same. + * lexio.cc (check_push_pop_directive): New function. + (check_source_format_directive): Restrict regex search to 1 line. + (cdftext::free_form_reference_format): Use new function. + * parse.y: Define new CDF tokens, use new CDF state. + * parse_ante.h (cdf_tokens): Use new CDF state. + (redefined_token): Same. + (class prog_descr_t): Remove obsolete CDF state. + (class program_stack_t): Same. + (current_call_convention): Same. + * scan.l: Recognize new CDF tokens. + * scan_post.h (is_cdf_token): Same. + * symbols.h (cdf_current_tokens): Change current_call_convention to return void. + * token_names.h: Regenerate. + * udf/stored-char-length.cbl: Use new PUSH/POP CDF functionality. + * util.cc (class cdf_directives_t): Define cdf_directives_t. + (current_call_convention): Same. + (cdf_current_tokens): Same. + (cdf_dictionary): Same. + (cdf_enabled_exceptions): Same. + (cdf_push): Same. + (cdf_push_call_convention): Same. + (cdf_push_current_tokens): Same. + (cdf_push_dictionary): Same. + (cdf_push_enabled_exceptions): Same. + (cdf_push_source_format): Same. + (cdf_pop): Same. + (cdf_pop_call_convention): Same. + (cdf_pop_current_tokens): Same. + (cdf_pop_dictionary): Same. + (cdf_pop_enabled_exceptions): Same. + (cdf_pop_source_format): Same. + * util.h (cdf_push): Declare cdf_directives_t. + (cdf_push_call_convention): Same. + (cdf_push_current_tokens): Same. + (cdf_push_dictionary): Same. + (cdf_push_enabled_exceptions): Same. + (cdf_push_source_format): Same. + (cdf_pop): Same. + (cdf_pop_call_convention): Same. + (cdf_pop_current_tokens): Same. + (cdf_pop_dictionary): Same. + (cdf_pop_source_format): Same. + (cdf_pop_enabled_exceptions): Same. + +2025-07-09 Robert Dubner <rdubner@symas.com> + James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120765 + PR cobol/119337 + PR cobol/120794 + * Make-lang.in: Take control of the .cc.o rule. + * cbldiag.h (error_msg_direct): New declaration. + (gcc_location_dump): Forward declaration. + (location_dump): Use gcc_location_dump. + * cdf.y: Change some tokens. + * gcobc: Change dialect handling. + * genapi.cc (parser_call_targets_dump): Temporarily remove from service. + (parser_compile_dcls): Combine temporary arrays. + (get_binary_value_from_float): Apply const to one parameter. + (depending_on_value): Localize a boolean variable. + (normal_normal_compare): Likewise. + (cobol_compare): Eliminate cppcheck warning. + (combined_name): Apply const to an input parameter. + (parser_perform): Apply const to a variable. + (parser_accept): Improve handling of special_name_t parameter and + the exception conditions. + (parser_display): Improve handling of speciat_name_t parameter; use the + os_filename[] string when appropriate. + (program_end_stuff): Rename shadowing variable. + (parser_division): Consolidate temporary char[] arrays. + (parser_file_start): Apply const to a parameter. + (inspect_replacing): Likewise. + (parser_program_hierarchy): Rename shadowing variable. + (mh_identical): Apply const to parameters. + (float_type_of): Likewise. + (picky_memcpy): Likewise. + (mh_numeric_display): Likewise. + (mh_little_endian): Likewise. + (mh_source_is_group): Apply static to a variable it. + (move_helper): Quiet a cppcheck warning. + * genapi.h (parser_accept): Add exceptions to declaration. + (parser_accept_under_discussion): Add declaration. + (parser_display): Change to std::vector; add exceptions to declaration. + * lexio.cc (cdf_source_format): Improve source code location handling. + (source_format_t::infer): Likewise. + (is_fixed_format): Likewise. + (is_reference_format): Likewise. + (left_margin): Likewise. + (right_margin): Likewise. + (cobol_set_indicator_column): Likewise. + (include_debug): Likewise. + (continues_at): Likewise. + (indicated): Likewise. + (check_source_format_directive): Likewise. + (cdftext::free_form_reference_format): Likewise. + * parse.y: Tokens; program and function names; DISPLAY and ACCEPT + handling. + * parse_ante.h (class tokenset_t): Removed. + (class current_tokens_t): Removed. + (field_of): Removed. + * scan.l: Token handling. + * scan_ante.h (level_found): Comment. + * scan_post.h (start_condition_str): Remove cast author_state:. + * symbols.cc (symbols_update): Change error message. + (symbol_table_init): Correct and reorder entries. + (symbol_unresolved_file_key): New function definition. + (cbl_file_key_t::deforward): Change error message. + * symbols.h (symbol_unresolved_file_key): New declaration. + (keyword_tok): New function. + (redefined_token): New function. + (class current_tokens_t): New class. + * symfind.cc (symbol_match): Revise error message. + * token_names.h: Reorder and change numbers in comments. + * util.cc (class cdf_directives_t): New class. + (cobol_set_indicator_column): New function. + (cdf_source_format): New function. + (gcc_location_set_impl): Improve column handling in token_location. + (gcc_location_dump): New function. + (class temp_loc_t): Modify constructor. + (error_msg_direct): New function. + * util.h (class source_format_t): New class. + +2025-07-01 James K. Lowden <jklowden@cobolworx.com> + + * Make-lang.in: Use && instead of semicolon between commands. + +2025-07-01 Robert Dubner <rdubner@symas.com> + + * parse.y: printf() of size_t is %zu, not %ld. + +2025-06-30 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120772 + PR cobol/120779 + PR cobol/120790 + PR cobol/120791 + PR cobol/120794 + * gcobc: Supply -fPIC for shared objects. + * genapi.cc (linemap_add): Delete empty macro. + (parser_enter_file): Do not call linemap_add. + (parser_leave_file): Same. + * gengen.cc (location_from_lineno): Remove function. + * lexio.cc (parse_replacing_term): Allow empty term. + (cdftext::process_file): Always append to output. + (cdftext::segment_line): Output #line directives. + * lexio.h (struct span_t): Count lines in span. + * parse.y: Revamp REPOSITORY, and minor syntax extensions. + * parse_ante.h (input_file_status_notify): Update linemap filename before location. + (intrinsic_token_of): Declare. + (parser_move_carefully): Support MOVE pointer. + * parse_util.h (intrinsic_token_of): New function. + * scan.l: New EOF logic, accept NOT=, own yylloc and yylineno. + * scan_ante.h (class enter_leave_t): Do not store newline count. + (cdf_location_set): Remove declaration. + (ydfltype_of): New function. + (update_location): Accept location parameter. + (reset_location): New function. + (YY_USER_ACTION): Use update_location(). + (YY_USER_INIT): Update CDF location. + (verify_ws): New function. + (wait_for_the_child): Removed. + * symbols.h (cobol_fileline_set): return line number. + * util.cc (valid_move): Use range-based for loop. + (struct input_file_t): Remove line_map pointer. + (class unique_stack): New peek() member function. + (cobol_lineno_save): Rename to overload cobol_lineno(). + (cobol_lineno): Replaces cobol_lineno_save(). + (cobol_filename): Return void. + (location_from_lineno): New function used by genapi.cc. + (cdf_location_set): Remove. + (matched_length): No change. + (cobol_fileline_set): Return line number. + (fisspace): Remove extra semicolon. + (fisprint): Same. + * util.h (cobol_filename_restore): Return void. + (cobol_lineno_save): Remove declaration. + (cobol_lineno): Declare. + +2025-06-29 Robert Dubner <rdubner@symas.com> + + * Make-lang.in: Incorporate gcobol.clean. + * except.cc (cbl_enabled_exceptions_t::dump): Update debug message. + * genapi.cc (gg_attribute_bit_get): Formatting. + (file_static_variable): Formatting. + (trace1_init): Formatting. + (build_main_that_calls_something): Normalize function_decl use. + (parser_call_target): Likewise. + (set_call_convention): Likewise. + (parser_call_target_convention): Likewise. + (parser_call_targets_dump): Likewise. + (function_handle_from_name): Likewise. + (function_pointer_from_name): Likewise. + (parser_initialize_programs): Likewise. + (parser_statement_begin): Formatting. + (parser_leave_file): Use function_decl FIFO. + (enter_program_common): Normalize function_decl use. + (parser_enter_program): Normalize function_decl use. + (tree_type_from_field_type): Normalize function_decl use. + (is_valuable): Comment. + (pe_stuff): Change name to program_end_stuff. + (program_end_stuff): Likewise. + (parser_exit): Likewise. + (parser_division): Normalize function_decl use. + (create_and_call): Normalize function_decl use. + (parser_call): Normalize function_decl use. + (parser_set_pointers): Normalize function_decl use. + (parser_program_hierarchy): Normalize function_decl use. + (psa_FldLiteralA): Defeat attempt to re-use literals. (Fails on some aarch64). + (parser_symbol_add): Error message formatting. + * genapi.h: Formatting. + * gengen.cc (struct cbl_translation_unit_t): Add function_decl FIFO. + (show_type): Rename to gg_show_type. + (gg_show_type): Correct an error message. + (gg_assign): Formatting; change error handling. + (gg_modify_function_type): Normalize function_decl use. + (gg_define_function_with_no_parameters): Fold into gg_defint_function(). + (function_decl_key): Normalize function_decl use. + (gg_peek_fn_decl): Normalize function_decl use. + (gg_build_fn_decl): Normalize function_decl use. + (gg_define_function): Normalize function_decl use. + (gg_tack_on_function_parameters): Remove. + (gg_finalize_function): Normalize function_decl use. + (gg_leaving_the_source_code_file): Normalize function_decl use. + (gg_call_expr_list): Normalize function_decl use. + (gg_trans_unit_var_decl): Normalize function_decl use. + (gg_insert_into_assemblerf): New function; formatting. + * gengen.h (struct gg_function_t): Eliminate "is_truly_nested" flag. + (gg_assign): Incorporate return value. + (gg_define_function): Normalize function_decl use. + (gg_define_function_with_no_parameters): Eliminate. + (gg_build_fn_decl): Normalize function_decl use. + (gg_peek_fn_decl): Normalize function_decl use. + (gg_modify_function_type): Normalize function_decl use. + (gg_call_expr_list): Normalize function_decl use. + (gg_get_function_decl): Normalize function_decl use. + (location_from_lineno): Prefix with "extern". + (gg_open): Likewise. + (gg_close): Likewise. + (gg_get_indirect_reference): Likewise. + (gg_insert_into_assembler): Likewise. + (gg_insert_into_assemblerf): Likewise. + (gg_show_type): New declaration. + (gg_leaving_the_source_code_file): New declaration. + * parse.y: Format debugging message. + * parse_ante.h: Normalize function_decl use. + +2025-06-20 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120621 + * genapi.cc (parser_compile_ecs): Cast argument to unsigned long. + (parser_compile_dcls): Same. + (parser_division): RAII. + (inspect_tally): Cast argument to unsigned long. + * lexio.cc (cdftext::lex_open): Cast pid_t to long. + * parse.y: hard-code values for old versions of Bison, and message format. + * scan_ante.h (wait_for_the_child): Cast pid_t to long. + +2025-06-18 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120621 + * cbldiag.h (yyerror): Add diagnostic attributes. + (yywarn): Same. + (error_msg): Same. + (yyerrorvl): Same. + (cbl_unimplementedw): Same. + (cbl_unimplemented): Same. + (cbl_unimplemented_at): Same. + * cdf-copy.cc (copybook_elem_t::open_file): Supply string argument. + * cdf.y: Use %<%>. + * cobol-system.h (if): Check GCC_VERSION. + (ATTRIBUTE_GCOBOL_DIAG): Define. + * except.cc (cbl_enabled_exception_t::dump): Remove extra %s. + * genapi.cc (get_class_condition_string): Use acceptable message. + (get_bytes_needed): Same. + (move_tree): Same. + (get_string_from): Same. + (internal_perform_through): Same. + (tree_type_from_field_type): Same. + (is_valuable): Same. + (parser_logop): Same. + (parser_relop): Same. + (parser_relop_long): Same. + (parser_if): Same. + (parser_setop): Same. + (parser_perform_conditional): Same. + (parser_file_add): Same. + (parser_file_open): Same. + (parser_file_close): Same. + (parser_file_read): Same. + (parser_file_write): Same. + (inspect_replacing): Same. + (parser_sort): Same. + (parser_file_sort): Same. + (parser_file_merge): Same. + (create_and_call): Same. + (parser_bitop): Same. + (parser_bitwise_op): Same. + (hijack_for_development): Same. + (mh_source_is_literalN): Same. + (mh_dest_is_float): Same. + (parser_symbol_add): Same. + * gengen.cc (show_type): Use acceptable message. + (gg_find_field_in_struct): Same. + (gg_declare_variable): Same. + (gg_printf): Same. + (gg_fprintf): Same. + (gg_tack_on_function_parameters): Same. + (gg_define_function): Same. + (gg_get_function_decl): Same. + (gg_finalize_function): Same. + (gg_call_expr): Same. + (gg_call): Same. + (gg_insert_into_assembler): Define new function. + (gg_insert_into_assemblerf): Use gg_insert_into_assembler(). + * gengen.h (gg_insert_into_assembler): Simpler function declaration. + (gg_insert_into_assemblerf): Declare new function. + * genmath.cc (parser_op): Use acceptable message. + * genutil.cc (get_binary_value): Use acceptable message. + * lexio.cc (parse_replacing_pair): Correct diagnostic arguments. + (preprocess_filter_add): Same. + (cdftext::open_input): Same. + * parse.y: Use acceptable messages. + * parse_ante.h (struct evaluate_elem_t): Use %<%>. + (is_callable): Same. + * parse_util.h (intrinsic_invalid_parameter): Use %qs. + * scan.l: Use dialect_error(). + * scan_ante.h (numstr_of): Use %qs. + (scanner_token): Quote COBOL tokens in messages. + (scanner_parsing): Correct diagnostic message. + (scanner_parsing_toggle): Quote COBOL tokens in messages. + (scanner_parsing_pop): Same. + (typed_name): Use %qs. + * scan_post.h (prelex): Quote COBOL tokens in message. + * show_parse.h (CHECK_FIELD): Use acceptable message format. + (CHECK_LABEL): Same. + * symbols.cc (symbol_field_same_as): Remove extra spaces. + (cbl_alphabet_t::assign): Use %<%>. + (cbl_field_t::internalize): Quote library name in message. + * symbols.h (struct os_locale_t): Constify codeset. + (class temporaries_t): Add copy constructor. + (struct cbl_alphabet_t): Use acceptable message. + * util.cc (symbol_type_str): Use cbl_internal_error. + (cbl_field_type_str): Same. + (is_elementary): Same. + (cbl_field_t::report_invalid_initial_value): Use %qs. + (class unique_stack): Avoid %m. + (ydferror): Declare function with attributes. + (error_msg): Same. + (cobol_fileline_set): Use %<%>. + (os_locale_t): Remove use of xstrdup. + (cobol_parse_files): Quote C names in message. + (dialect_error): Use %<%>. + * util.h (cbl_message): Add attributes. + (cbl_internal_error): Same. + (cbl_err): Same. + (cbl_errx): Same. + +2025-06-16 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120621 + * Make-lang.in: Use STRICT_WARN. + * cbldiag.h (location_dump): suppress shadowVariable. + * cdf-copy.cc (esc): Fix shadowVariable. + (copybook_elem_t::open_file): Do not use %m. + * cdf.y: suppress invalidPrintfArgType for target format. + * cdfval.h (struct cdfval_t): Suppress noExplicitConstructor. + * cobol1.cc (cobol_name_mangler): Use C++ cast. + * copybook.h (class copybook_elem_t): Same. + * dts.h: Fixes and suppressions due to cppcheck. + * except.cc (cbl_enabled_exceptions_t::status): Suppress useStlAlgorithm. + (cbl_enabled_exceptions_t::turn_on_off): Const parameter. + (class choose_declarative): Removed. + * genapi.cc (struct called_tree_t): Explicit constructor. + (parser_compile_ecs): Cast to void * for %p. + (parser_compile_dcls): Same. + (parser_statement_begin): Same. + (initialize_variable_internal): Use std::vector for subscripts. + (parser_initialize): Constification. + (get_string_from): Same. + (combined_name): Same. + (parser_perform): Same. + (psa_FldLiteralN): Same. + (is_figconst): Const parameter. + (is_figconst_t): Same. + (parser_exit): Same. + (parser_division): Const pointer. + (parser_perform_conditional): Whitespace. + (parser_set_conditional88): Const parameter. + (inspect_tally): Use std::vector. + (inspect_replacing): Same. + (parser_inspect): Same. + (parser_intrinsic_subst): Use std::vector (constuct elements). + (parser_intrinsic_call_1): Use std::vector for subscripts. + (is_ascending_key): Const pointer. + (parser_sort): Use std::vector. + (parser_file_sort): Same. + (parser_file_merge): Same. + (parser_unstring): Same. + (parser_string): Same. + (parser_call): Const pointer. + (parser_program_hierarchy): Use std::vector. + (conditional_abs): Const paraemeter. + (float_type_of): Same. + (initial_from_initial): Set value, quoted or not. + (parser_symbol_add): Remove redundant nested test. + * genapi.h (parser_add): Const parameters. + (parser_subtract): Same. + (parser_multiply): Same. + (parser_divide): Same. + (parser_perform): Same. + (parser_exit): Same. + (parser_initialize): Same. + (parser_set_conditional88): Same. + (parser_sort): Same. + (parser_file_sort): Same. + (parser_file_merge): Same. + (parser_string): Same. + (is_ascending_key): Same. + * genmath.cc (arithmetic_operation): Use std::vector. + (is_somebody_float): Const parameter. + (all_results_binary): Const parameter. + (fast_multiply): Remove redundant nested test. + (parser_add): Const parameter. + (parser_multiply): Remove redundant nested test. + (parser_divide): Const parameter. + (parser_subtract): Same. + * genutil.cc (get_depending_on_value): Use std::vector. + (get_data_offset): Same. + (tree_type_from_field): Const parameter. + (refer_has_depends): Const pointers. + (get_literal_string): RAII. + (refer_is_clean): Use std::vector. + (get_time_nanoseconds): Newline at EOF. + * genutil.h (tree_type_from_field): Remove declaration. + * inspect.h (struct cbx_inspect_qual_t): Use std::vector. + (struct cbl_inspect_qual_t): Same. + (struct cbx_inspect_match_t): Same. + (class cbl_inspect_match_t): Same. + (struct cbx_inspect_replace_t): Same. + (struct cbl_inspect_replace_t): Same. + (struct cbx_inspect_oper_t): Same. + (struct cbl_inspect_oper_t): Same. + (struct cbx_inspect_t): Same. + (struct cbl_inspect_t): Same. + (parser_inspect): Same. + * lexio.cc (indicated): Const pointer. + (remove_inline_comment): Scope reduction. + (maybe_add_space): Const pointer. + (recognize_replacements): C++ cast. + (check_source_format_directive): Same. + (struct replacing_term_t): Explicit constructor. + (parse_replace_pairs): Const reference. + (location_in): Const reference. + (parse_copy_directive): C++ cast. + (parse_replace_last_off): Const parameter. + (parse_replace_text): Const reference. + (parse_replace_directive): C++ cast. + (cdftext::lex_open): Const reference. + (cdftext::open_output): Scope reduction. + (cdftext::free_form_reference_format): Remove unused variable. + (cdftext::process_file): Simplify. + * lexio.h (struct bytespan_t): Use nullptr. + (struct filespan_t): Initialize icol in constructor. + (struct span_t): Suppress confused operatorEqRetRefThis. + (struct replace_t): Eliminate single-value constructor. + * parse.y: Many const cppcheck reports, and portable bit-shift. + * parse_ante.h (reject_refmod): Const parameter. + (require_pointer): Same. + (require_integer): Same. + (struct evaluate_elem_t): Explicit constructor. + (struct arith_t): Use std::vector. + (class eval_subject_t): Const parameter. + (dump_inspect_match): Declare. + (struct perform_t): Explicit constructor. + (list_add): Const parameter. + (class tokenset_t): Avoid negative array index. + (struct file_list_t): Explicit constructor. + (struct field_list_t): Same. + (struct refer_list_t): Same. + (struct refer_marked_list_t): Const parameter. + (struct refer_collection_t): Explicit constructor. + (struct ast_inspect_oper_t): Remove class. + (ast_inspect_oper_t): Same. + (struct ast_inspect_t): Same. + (struct ast_inspect_list_t): Same. + (ast_inspect): Add location. + (struct elem_list_t): Explicit constructor. + (struct unstring_tgt_t): Same. + (struct unstring_tgt_list_t): Same. + (struct unstring_into_t): Same. + (struct ffi_args_t): Same. + (struct file_sort_io_t): Same. + (merge_t): Same. + (struct vargs_t): Same. + (class prog_descr_t): Eliminate single-value constructor. + (class program_stack_t): Suppress useStlAlgorithm. + (struct rel_part_t): Eliminate single-value constructor. + (class log_expr_t): Explicit constructor. + (add_debugging_declarative): Rename local variable. + (intrinsic_call_2): Const parameter. + (invalid_key): Use std::find_if. + (parser_add2): Const parameter. + (parser_subtract2): Same. + (stringify): Same. + (unstringify): Same. + (anybody_redefines): Same. + (ast_call): Same. + * parse_util.h (class cname_cmp): Explicit constructor. + (intrinsic_inconsistent_parameter): Same. + * scan_ante.h (struct cdf_status_t): Eliminate single-value constructor. + (class enter_leave_t): Explicit constructor. + (update_location): Const pointer, explicit constructor. + (symbol_function_token): Const pointer. + (typed_name): Same. + * scan_post.h (datetime_format_of): Scope reduction. + * show_parse.h (class ANALYZE): Use std::vector, explicit consstructor. + * symbols.cc (symbol_table_extend): Scope reduction. + (cbl_ffi_arg_t::cbl_ffi_arg_t): Define default constructor. + (end_of_group): Const pointer. + (symbol_find_odo): Const parameter. + (rename_not_ok): Same. + (field_str): Use %u instead of %d. + (struct capacity_of): Const pointer. + (symbols_update): Same. + (symbol_field_parent_set): Same. + (symbol_file_add): Same. + (symbol_typedef_add): Same. + (symbol_field_add): Use new operator=(). + (symbol_field): Suppress CastIntegerToAddressAtReturn. + (symbol_register): Same. + (symbol_file): Suppress knownConditionTrueFalse. + (next_program): Const parameter. + (symbol_file_record): Same. + (class is_section): Explicit constructor. + (cbl_file_t::no_key): Remove. + (cbl_prog_hier_t::cbl_prog_hier_t): Use std::vector. + (symbol_label_add): Assert pointer is not NULL. + (symbol_label_section_exists): Const reference in lambda. + (expand_picture): Use C++ cast. + (symbol_program_callables): Const pointer. + (symbol_currency_add): Suppress nullPointerRedundantCheck. + (cbl_key_t): Use std::vector. + (cbl_occurs_t::field_add): Const parameter. + (cbl_occurs_t::index_add): Explicit constructor. + (class is_field_at): Same. + (cbl_file_key_t::deforward): Scope reduction. + (cbl_file_t::keys_str): Use allocated memory only. + (file_status_status_of): Const pointer. + (is_register_field): Const parameter. + * symbols.h (struct cbl_field_data_t): Eliminate single-value constructor. + (struct cbl_occurs_bounds_t): Same. + (struct cbl_refer_t): Use std::vector. + (valid_move): Const parameter. + (is_register_field): Same. + (struct cbl_key_t): Use std::vector. + (struct cbl_substitute_t): Eliminate single-value constructor. + (refer_of): Return const reference + (struct cbl_ffi_arg_t): Eliminate single-value constructor. + (class temporaries_t): Same. + (struct cbl_file_key_t): Define default constructor. + (struct cbl_file_lock_t): Define copy constructor and operator=(). + (struct cbl_file_t): Complete default constructor. + (struct symbol_elem_t): Explicit constructor. + (symbol_elem_of): Suppress cstyleCast. + (symbol_redefines): Const parameter. + (struct cbl_field_t): Same. + (cbl_section_of): Test for NULL pointer. + (cbl_field_of): Same. + (cbl_label_of): Same. + (cbl_special_name_of): Same. + (cbl_alphabet_of): Same. + (cbl_file_of): Same. + (is_figconst): Delete extra "struct" keyword. + (is_figconst_low): Same. + (is_figconst_zero): Same. + (is_figconst_space): Same. + (is_figconst_quote): Same. + (is_figconst_high): Same. + (is_space_value): Same. + (is_quoted): Same. + (symbol_index): Const parameter. + (struct cbl_prog_hier_t): Suppress noExplicitConstructor. + (struct cbl_perform_vary_t): Eliminate single-value constructor. + (is_signable): Const parameter. + (is_temporary): Same. + (rename_not_ok): Same. + (field_at): Test for NULL pointer. + (class procref_base_t): Eliminate single-value constructor. + * symfind.cc (is_data_field): Const pointer. + (finalize_symbol_map2): Same. + (class in_scope): Same. + (symbol_match2): Same. + * token_names.h: Suppress useInitializationList. + * util.cc (normalize_picture): Whitespace and remove extra "continue". + (redefine_field): Const pointer. + (cbl_field_t::report_invalid_initial_value): Same. + (literal_subscript_oob): Rename shadow variable. + (cbl_refer_t::subscripts_set): Use std::vector. + (cbl_refer_t::str): Same. + (cbl_refer_t::deref_str): Same. + (locally_unique): Use explicit constructor. + (ambiguous_reference): Same. + (class unique_stack): Use const reference. + (cobol_filename): Const pointer. + (verify_format): Scope reduction. + (class temp_loc_t): Do not derive from YYLTYPE. + (cobol_parse_files): Const pointer. + * util.h (as_voidp): Define convenient converter. + +2025-06-10 James K. Lowden <jklowden@cobolworx.com> + + * Make-lang.in: cobol.clean does not remove libgcobol files. + * cdf.y: Suppress 1 cppcheck false positive. + * cdfval.h (scanner_parsing): Partial via cppcheck for PR119324. + * gcobol.1: Fix groff errors. + * gcobolspec.cc (append_arg): Const parameter. + * parse_ante.h (intrinsic_call_2): Avoid NULL dereference. + +2025-06-06 Robert Dubner <rdubner@symas.com> + James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120328 + PR cobol/119695 + * Make-lang.in: Success with non-English locale. + * cbldiag.h (cbl_unimplemented_at): Comment: + * cdf-copy.cc (copybook_elem_t::open_file): Indentation. + * cdf.y: YYABORT on certain errors. + * cdfval.h (cdf_value): Const parameter. + * copybook.h (class copybook_elem_t): Initialization. + (class uppername_t): Explicit constructor. + * except.cc (ec_type_descr): Remove %04s. + (cbl_enabled_exceptions_t::dump): Remove %zu. + * exceptg.h (class exception_turn_t): Explicit constructor. + * genapi.cc (parser_perform_conditional): Remove %zu. + (set_exception_environment): Formatting. + (parser_statement_begin): Exception overhead. + (parser_perform_conditional): Formatting: + (parser_perform_conditional_end): Eliminate size_t. + (parser_check_fatal_exception): Exception overhead. + (parser_perform_conditional_end): Remove %zu. + * inspect.h (struct cbx_inspect_match_t): Const reference. + (struct cbx_inspect_t): Const parameter. + * lexio.cc (cdftext::process_file): Remove %zu. + * lexio.h (struct YYLTYPE): Remove unneeded struct. + (YYLTYPE_IS_DECLARED): Likewise. + (YYLTYPE_IS_TRIVIAL): Likewise. + * parse.y: Comment; change DOT. + * scan.l: Scan function names without swallowing whitespace. + * scan_ante.h (scanner_parsing): Remove %zu. + (scanner_parsing_pop): Remove %zu. + (binary_integer_usage): Remove %zu. + * scan_post.h (prelex): Correct post-CDF resumption. + (yylex): Clearer message. + * symbols.cc (symbol_table_extend): Explicit constructor. + (elementize): Const parameter. + (is_variable_length): Correct always-false. + (symbols_update): Remove unnecessary shadow variable. + (struct symbol_elem_t): Const parameter. + (symbol_alphabet_add): Const parameter. + (new_literal_add): Initialization. + * symbols.h (class cbl_domain_elem_t): Correct assignment. + (struct cbl_span_t): Improve constructor. + (struct cbl_refer_t): Initialization. + (struct cbl_alphabet_t): Rename shadow variable. + (struct cbl_file_key_t): Remove unused constructor. + (struct symbol_elem_t): Initialization. + (struct cbl_until_addresses_t): Use unsigned int, for messages. + (struct cbl_prog_hier_t): Initialization. + (struct cbl_perform_tgt_t): Repair constructor. + (struct cbl_label_t): Const parameter. + (symbol_typedef_add): Const parameter. + (symbol_field_add): Explicit constructor. + (symbol_label_add): Explicit constructor. + (symbol_program_add): Remove C-style "struct" use. + (symbol_special_add): Remove C-style "struct" use. + (symbol_alphabet_add): Const parameter. + (symbol_file_add): Remove C-style "struct" use. + (symbol_section_add): Remove C-style "struct" use. + * symfind.cc: Const parameter. + * util.cc (gb4): New function. + * util.h (gb4): New function. + * TODO: New file. + +2025-06-05 Robert Dubner <rdubner@symas.com> + + PR cobol/119975 + * genapi.cc (parser_intrinsic_call_0): Use get_time_nanoseconds(). + * genutil.cc (get_time_64): Rename to get_time_nanoseconds(). + (get_time_nanoseconds): Likewise. + * genutil.h (get_time_64): Likewise. + (get_time_nanoseconds): Likewise. + * util.cc (class cbl_timespec): Timing routine uses + get_time_nanoseconds(). + (operator-): Likewise. + (parse_file): Likewise. + +2025-06-02 Robert Dubner <rdubner@symas.com> + + PR cobol/119975 + * genapi.cc (parser_intrinsic_call_0): Use get_time_64() function. + * genutil.cc (get_time_64): Definition created. + * genutil.h (get_time_64): Declaration created. + +2025-06-01 Robert Dubner <rdubner@symas.com> + + PR cobol/119524 + * gengen.cc (gg_printf): Use the new __gg__fprintf_stderr() function + instead of generating a call to fprintf(). + +2025-05-20 Robert Dubner <rdubner@symas.com> + James K. Lowden <jklowden@cobolworx.com> + + PR cobol/119770 + PR cobol/119772 + PR cobol/119790 + PR cobol/119771 + PR cobol/119810 + PR cobol/119335 + PR cobol/119632 + * cdf-copy.cc (GLOB_BRACE): Eliminate <glob.h>. + * cdfval.h (_CDF_VAL_H_): Switch to C++ headers. + * copybook.h (class copybook_elem_t): Eliminate <glob.h>. + (class copybook_t): Likewise. + * gcobc: Numerous changes to improve utility. + * gcobol.1: Correct names in the list of functions. + * genapi.cc (compare_binary_binary): Use has_attr() function. + * lexio.cc (cdftext::lex_open): Typo; filename logic. + (cdftext::process_file): Filename logic. + * parse.y: Numerous parsing changes. + * parse_ante.h (new_alphanumeric): C++ includes; changes to temporaries. + (new_tempnumeric): Likewise. + (new_tempnumeric_float): Likewise. + (set_real_from_capacity): Created. + * scan.l: Use yy_pop_state(). + * scan_ante.h (typed_name): Find figconst from data.initial. + * symbols.cc (symbol_valid_udf_args): Eliminate. + (symbols_update): figconst processing. + (new_temporary_impl): For functions, set .initial to function name. + (temporaries_t::acquire): Likewise. + (new_alphanumeric): Likewise. + (new_temporary): Likewise. + * symbols.h (_SYMBOLS_H_): Use C++ includes. + (cbl_figconst_tok): Change handling of figconst. + (cbl_figconst_field_of): Change handling of figconst. + (symbol_valid_udf_args): Eliminate. + * symfind.cc (symbol_match2): Change declaration. + (symbol_match): Change declaration. + +2025-05-18 Mark Wielaard <mark@klomp.org> + + * lang.opt.urls: Regenerated. + +2025-05-16 Robert Dubner <rdubner@symas.com> + + * cobol1.cc (cobol_langhook_handle_option): Eliminate OPT_M. + * except.cc (cbl_enabled_exception_t::dump): Formatting. + (symbol_declaratives_add): Remove. + (declarative_runtime_match): Change to no-blob processing. + * exceptg.h (declarative_runtime_match): Change declaration. + (symbol_declaratives_add): Remove declaration. + * gcobc: Dialect handling. + * genapi.cc (parser_compile_ecs): Formatting; add SHOW_IF_PARSE. + (parser_compile_dcls): Likewise. + (parser_statement_begin): Avoid unnecessary store_location_stuff() call. + (gg_get_depending_on_value): Streamline get_depending_on_value_from_odo(). + (depending_on_value): Likewise. + (parser_display_field): Formatting. + (parser_display): Handle case ENV_NAME_e. + (parser_file_open): Avoid unnecessary store_location_stuff. + (parser_file_close): Likewise. + (parser_file_read): Likewise. + (parser_file_write): Likewise. + (parser_file_delete): Likewise. + (parser_file_rewrite): Likewise. + (parser_file_start): Likewise. + (parser_intrinsic_subst): Streamline get_depending_on_value_from_odo(). + (parser_intrinsic_call_1): Likewise. + (parser_lsearch_start): Likewise. + (parser_bsearch_start): Likewise. + (parser_sort): Likewise. + (store_location_stuff): Avoid unnecessary assignments. + (parser_pop_exception): Formatting. + * genmath.cc (parser_add): Avoid var_decl_default_compute_error assignment + when doing fast_add(). + (parser_subtract): Likewise. + * genutil.cc (REFER): Macro for analyzing code generation. + (get_integer_value): Use data_decl_node for integer value from FldLiteralN. + (get_data_offset): Streamline exception code processing. + (get_and_check_refstart_and_reflen): Likewise. + (get_depending_on_value_from_odo): Likewise. + (get_depending_on_value): Likewise. + (refer_is_clean): Formatting. + (refer_refmod_length): Streamline exception code processing. + (refer_fill_depends): Likewise. + (refer_offset): Likewise. + (refer_size_dest): Likewise. + (refer_size_source): Likewise. + * genutil.h (get_depending_on_value_from_odo): Likewise. + * lang-specs.h: Options definition. + * lang.opt: -M as in c.opt. + * lexio.h: Formatting. + * parse.y: Expand -dialect suggestions; SECTION SEGMENT messages. + * parse_ante.h (declarative_runtime_match): Dialect handling. + (labels_dump): Likewise. + (class current_tokens_t): Likewise. + (class prog_descr_t): Make program_index size_t to prevent padding bytes. + * scan.l: POP_FILE directive. + * scan_ante.h (class enter_leave_t): Better handle line number when + processing COPY statements. + * symbols.cc (symbol_elem_cmp): Eliminate SymFunction. + (symbols_dump): Likewise. + (symbol_label_section_exists): Likewise. + * symbols.h (NAME_MAX): Eliminate. (Was part of SymFunction). + (dialect_is): Improve dialect handling. + (dialect_gcc): Likewise. + (dialect_ibm): Likewise. + (dialect_gnu): Likewise. + (enum symbol_type_t): Eliminate SymFunction. + * util.cc (symbol_type_str): Likewise. + (class unique_stack): Option -M handling. + (cobol_set_pp_option): Likewise. + (parse_file): Likewise. + * util.h (cobol_set_pp_option): Likewise. + +2025-05-10 Robert Dubner <rdubner@symas.com> + + PR cobol/119337 + * Make-lang.in: Change how $(FLEX) is invoked. + * cdf.y: Change parser tokens. + * gcobc: Changed how name is inferred for PR119337 + * gcobol.1: Documentation for SOURCE format heuristic + * genapi.cc: Eliminate __gg__odo_violation. + (parser_display_field): Change comment. + * genutil.cc:Eliminate __gg__odo_violation. + (REFER): New macro for analyzing subscript/refmod calculations. + (get_integer_value): Likewise. + (get_data_offset): Eliminate __gg__odo_violation. + (scale_by_power_of_ten_N): Eliminate unnecessary var_decl_rdigits operation. + (refer_is_clean): Check for FldLiteralN. + (REFER_CHECK): Eliminate. + (refer_refmod_length): Streamline var_decl_rdigits processing. + (refer_fill_depends): Likewise. + (refer_offset): Streamline processing when FldLiteralN. + (refer_size): Tag with REFER macro. + (refer_size_dest): Likewise. + (refer_size_source): Likewise. + * genutil.h (get_integer_value): Delete declaration for odo_violation; + change comment for get_integer_value + (REFER_CHECK): Delete declaration. + (refer_check): Delete #define. + * lexio.cc (is_fixed_format): Changes for source format auto-detect. + (is_reference_format): Likewise. + (check_source_format_directive): Likewise. + (valid_sequence_area): Likewise. + (is_p): Likewise. + (is_program_id): Likewise. + (likely_nist_file): Likewise. + (infer_reference_format): Likewise. + (cdftext::free_form_reference_format): Likewise. + * parse.y: Token changes. + * parse_ante.h (class tokenset_t): Likewise. + (class current_tokens_t): Likewise. + (cmd_or_env_special_of): Likewise. + * scan.l: Likewise. + * scan_ante.h (bcomputable): Likewise. + (keyword_alias_add): Likewise. + (struct bint_t): Likewise. + (binary_integer_usage): Likewise. + (binary_integer_usage_of): Likewise. + * scan_post.h (start_condition_str): Likewise. + * symbols.cc (symbol_table_init): Formatting. + * symbols.h (struct cbl_field_data_t): Add "input" method to field_data_t. + (keyword_alias_add): Add forward declaration. + (binary_integer_usage_of): Likewise. + * token_names.h: Change list of tokens. + * util.cc (iso_cobol_word): Change list of COBOL reserved words. + +2025-05-08 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR cobol/119217 + * symbols.h (NAME_MAX): Define fallback. + +2025-05-08 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR cobol/119217 + * dts.h (csub_match): Initialize rm_so, rm_eo fields explicitly. + +2025-05-08 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR cobol/119217 + * cdf-copy.cc (GLOB_BRACE): Define fallback. + (GLOB_TILDE): Likewise. + +2025-05-05 Robert Dubner <rdubner@symas.com> + + * cdf.y: Exceptions. + * except.cc (cbl_enabled_exception_t::dump): Likewise. + (cbl_enabled_exceptions_t::dump): Likewise. + (cbl_enabled_exceptions_t::status): Likewise. + (cbl_enabled_exceptions_t::encode): Likewise. + (cbl_enabled_exceptions_t::turn_on_off): Likewise. + (cbl_enabled_exceptions_t::match): Likewise. + (declarative_runtime_match): Likewise. Likewise. + * exceptg.h (struct cbl_exception_files_t): Likewise. + (class exception_turn_t): Likewise. + (apply_cdf_turn): Likewise. + * genapi.cc (treeplet_fill_source): Use refer_offset(). + (function_handle_from_name): Likewise. + (parser_initialize_programs): Likewise. + (parser_statement_begin): Likewise. + (array_of_long_long): Exceptions. + (parser_compile_ecs): Exceptions. + (parser_compile_dcls): Exceptions. + (store_location_stuff): Exceptions. + (initialize_variable_internal): Use refer_offset(). + (compare_binary_binary): Use refer_offset(). + (cobol_compare): Use refer_offset(). + (paragraph_label): Formatting. + (parser_goto): Use refer_offset(). + (parser_perform_times): Likewise. + (internal_perform_through_times): Likewise. + (parser_enter_file): Exceptions. + (psa_FldLiteralN): Add comment. + (parser_accept): Use refer_offset(). + (parser_accept_command_line): Likewise. + (parser_accept_command_line_count): Likewise. + (parser_accept_envar): Likewise. + (parser_set_envar): Likewise. + (parser_display_internal): Likewise. + (parser_initialize_table): Likewise. + (parser_sleep): Likewise. + (parser_allocate): Likewise. + (parser_free): Likewise. + (parser_division): Likewise. + (parser_relop_long): Likewise. + (parser_see_stop_run): Likewise. + (parser_classify): Likewise. + (parser_file_add): Include symbol_table_index in __gg__file_init(). + (parser_file_open): Use refer_offset(). + (parser_file_write): Move forward declaration of store_location_stuff(). + (parser_file_start): Use refer_offset(). + (parser_inspect_conv): Likewise: + (parser_intrinsic_numval_c): Likewise: + (parser_intrinsic_subst): Likewise: + (parser_intrinsic_call_1): Likewise: + (parser_intrinsic_call_2): Likewise: + (parser_intrinsic_call_3): Likewise: + (parser_intrinsic_call_4): Likewise: + (parser_sort): Likewise: + (parser_return_start): Exceptions. + (parser_unstring): Use refer_offset(). + (create_and_call): Likewise. + (parser_set_pointers): Use refer_offset(). + (parser_program_hierarchy): Comment. + (parser_set_handled): Exceptions; removed. + (parser_set_file_number): Exceptions; removed. + (stash_exceptions): Exceptions; removed. + (parser_exception_prepare): Exceptions; removed. + (parser_match_exception): Exceptions; eliminate blob. + (parser_check_fatal_exception): Exceptions. + (parser_push_exception): Create. + (parser_pop_exception): Create. + (mh_identical): Use refer_offset(). + (mh_source_is_literalN): Likewise. + (mh_dest_is_float): Likewise. + (mh_numeric_display): Likewise. + (mh_little_endian): Likewise. + (mh_source_is_group): Likewise. + (move_helper): Likewise. + (binary_initial_from_float128): Formatting; change error message. + (initial_from_float128): Change name to "initial_from_initial" + (initial_from_initial): Add one byte to allocation for figconsts. + (parser_symbol_add): Use initial_from_initial(). + (parser_symbol_add): Eliminate unneeded logic around actually_create... + * genapi.h: Exceptions. + * genmath.cc (fast_add): Use refer_offset(). + (fast_subtract): Likewise. + (fast_multiply): Likewise. + (fast_divide): Likewise. + * genutil.cc: Exceptions; various global definitions. + (get_integer_value): Comment. + (get_data_offset_dest): Eliminate. + (get_data_offset_source): Rename to get_data_offset(). + (get_data_offset): Use refer_offset(). + (get_binary_value): Likewise; eliminate use of literal_decl_node. + (build_array_of_treeplets): Likewise. + (build_array_of_fourplets): Likewise. + (REFER_CHECK): Comment: + (refer_refmod_length): Use get_any_capacity(); use refer_offset; + set reflen to integer_one_node. + (refer_offset_dest): Change name to refer_offset. + (refer_offset): Use get_data_offset(). + (refer_size_dest): Change name to refer_size(). + (refer_size): Use get_any_capacity(). + (refer_offset_source): Use refer_offset(). + (refer_size_source): Likewise. + (qualified_data_source): Likewise. + (qualified_data_dest): Likewise. + (qualified_data_location): Likewise. + * genutil.h: Exceptions; changes to global declarations. + * lexio.cc (likely_nist_file): Added to detect NIST file format. + (cdftext::free_form_reference_format): Handle NIST file format. + * parse.y: (strip_trailing_zeroes): Added. + Changes for exceptions. + * parse_ante.h (parse_error_inc): Likewise. + (YYLLOC_DEFAULT): Likewise. + (static_cast): Likewise. + (is_cobol_word): Change to is_cobol_charset. + (is_cobol_charset): Refine allowed characters. + (require_numeric): Change to require integer. + (require_integer): Likewise. + (current_enabled_ecs): Exceptions. + (is_integer_literal): Change interpretation. + (procedure_division_ready): Exceptions. + (statement_epilog): Likewise. + (statement_begin): Likewise. + * show_parse.h: Changes to GCOBOL_SHOW handling. + * structs.cc: Add symbol_index to cblc_file_t structure. + * symbols.cc (field_str): Repair .initial handling in FldLiteralN. + * symbols.h (struct cbl_field_t): Eliminate literal_decl_node. + (current_enabled_ecs): Exceptions. + * util.cc (cbl_message): Add final newline to error message. + (ftoupper): Added. + (iso_cobol_word): Add list of ISO reserved words. + * util.h (ftoupper): Added. + +2025-05-02 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119364 + * structs.h (cbl_enabled_exception_type_node): New variable + declaration. + * structs.cc (cbl_enabled_exception_type_node): New variable. + (create_cbl_enabled_exception_t): New function. + (create_our_type_nodes): Initialize cbl_enabled_exception_type_node + using it. + * genapi.cc (stash_exceptions): Don't compare padding bits to + determine if the exceptions are the same as last time. Use + cbl_enabled_exception_type_node for target size and field offsets + and native_encode_expr to write each field into byte sequence. + +2025-05-02 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119364 + * util.cc (is_numeric_edited): Use HOST_SIZE_T_PRINT_UNSIGNED + instead of "%zu" and cast corresponding argument to fmt_size_t. + (normalize_picture): Use GCC_PRISZ instead of "z" and pass address + of fmt_size_t var to sscanf and copy afterwards. + (cbl_refer_t::str): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" and cast corresponding argument + to fmt_size_t. + (struct move_corresponding_field): Likewise. + (valid_move): Likewise. + (ambiguous_reference): Likewise. + (parent_names): Likewise. + (find_corresponding::find_corresponding): Likewise. + (corresponding_fields): Likewise. + (unique_stack::push): Likewise. + (cobol_filename): Likewise. + * lexio.cc: Include config.h first. + (recognize_replacements): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" and cast corresponding argument + to fmt_size_t. + (check_source_format_directive): Likewise. + (parse_replacing_pair): Use size_t(0) instead of 0UL in span_t + construction. + (parse_replace_pairs): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (parse_copy_directive): Likewise. + (parse_replace_last_off): Likewise. + (parse_replace_text): Likewise. + (bytespan_t::append): Likewise. + (cdftext::map_file): Likewise. + (cdftext::process_file): Likewise. + * symfind.cc (dump_symbol_map2): Likewise. + (dump_symbol_map_value): Likewise. + (build_symbol_map): Likewise. + (is_name::dump_key): Likewise. + (symbol_match2): Likewise. + (symbol_find): Likewise. + (symbol_find_of): Likewise. + * cdf.y: Likewise. + * symbols.cc: Include config.h first. + (cbl_field_t::set_attr): Return uint64_t rather than size_t + and replace size_t(attr) with uint64_t(attr). + (cbl_field_t::clear_attr): Likewise. + (symbol_field_capacity): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (symbol_find_odo_debug): Likewise. + (symbols_dump): Likewise. + (calculate_capacity): Likewise. + (field_str): Likewise. + (symbols_update): Likewise. + (symbol_field_forward): Likewise. + (numeric_group_attrs): Return uint64_t rather than size_t and + change inherit variable to from size_t to uint64_t. + (new_literal_add): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (temporaries_t::dump): Likewise. + (cbl_label_t::str): Likewise. + (symbol_label_add): Likewise. + (symbol_program_add): Likewise. + (symbol_forward_names): Likewise. + (symbol_forward_to): Likewise. + (cbl_file_key_t::deforward): Likewise. + (cbl_file_key_t::str): Likewise. + * gengen.cc (show_type): Use PRId64 instead of "ld". + (gg_unique_in_function): Use HOST_SIZE_T_PRINT_DEC instead of + %ld and cast corresponding argument to fmt_size_t. + * scan.l: Add %top section with #include "config.h". + * genmath.cc (parser_add): Use HOST_SIZE_T_PRINT_DEC instead of + %ld and cast corresponding argument to fmt_size_t. + (parser_subtract): Likewise. + * parse.y: Include "config.h" before <fstream>. Use + HOST_SIZE_T_PRINT_UNSIGNED instead of "%zu" and cast corresponding + argument to fmt_size_t. Change type of sign_attrs, group_sign and + type_implies from size_t to uint64_t. + (perform_t::ec_labels_t::new_label): Use HOST_SIZE_T_PRINT_UNSIGNED + instead of "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC + instead of "%zd" and cast corresponding argument to fmt_size_t. + (stringify_src_t::dump): Likewise. + (lang_check_failed): Likewise. + (numstr2i): Use GCC_PRISZ instead of "z" and pass address of temporary + with fmt_size_t type to sscanf and then copy it over. + (initialize_statement): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (dump_inspect_oper): Likewise. + (new_literal): Likewise. + (literal_subscripts_valid): Likewise. + (eval_subject_t::label): Likewise. + * genapi.cc (level_88_helper): Likewise. + (parser_call_targets_dump): Likewise. + (combined_name): Use HOST_SIZE_T_PRINT_DEC instead of "%ld" + and cast corresponding argument to fmt_size_t. + (section_label): Likewise. + (paragraph_label): Likewise. + (leave_procedure): Likewise. + (parser_perform): Likewise. + (parser_perform_times): Likewise. + (internal_perform_through): Likewise. + (internal_perform_through_times): Likewise. + (parser_enter_program): Likewise. + (parser_init_list_size): Likewise. + (parser_init_list): Likewise. + (psa_FldLiteralN): Likewise. + (psa_FldBlob): Likewise. + (parser_assign): Likewise. + (parser_free): Pass p->field->name to dbgmsg. + (parser_division): Use HOST_SIZE_T_PRINT_DEC instead of "%ld" + and cast corresponding argument to fmt_size_t. + (perform_outofline_before_until): Likewise. + (perform_outofline_after_until): Likewise. + (perform_outofline_testafter_varying): Likewise. + (perform_outofline_before_varying): Likewise. + (perform_inline_testbefore_varying): Likewise. + (parser_inspect): Change n_operations parameter type from + unsigned long to size_t. + (parser_intrinsic_callv): Use HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (parser_bitop): Use HOST_SIZE_T_PRINT_HEX_PURE instead of + "%lx" and cast corresponding argument to fmt_size_t. + (parser_bitwise_op): Likewise. + (parser_program_hierarchy): Use HOST_SIZE_T_PRINT_DEC instead of "%ld" + and cast corresponding argument to fmt_size_t. + (parser_set_handled): Use HOST_SIZE_T_PRINT_HEX_PURE instead of + "%lx" and cast corresponding argument to fmt_size_t. + (parser_set_numeric): Use HOST_SIZE_T_PRINT_DEC instead of "%ld" + and cast corresponding argument to fmt_size_t. + (psa_new_var_decl): Use HOST_SIZE_T_PRINT_DEC instead of "%ld" + and cast corresponding argument to fmt_size_t. + (parser_symbol_add): Use HOST_SIZE_T_PRINT_DEC instead of "%zd" + or HOST_SIZE_T_PRINT_HEX_PURE instead of "%lx" and cast corresponding + argument to fmt_size_t. + * cdf-copy.cc: Include "config.h" first. + * scan_ante.h (trim_location): Use HOST_SIZE_T_PRINT_UNSIGNED instead + of "%zu" or "%d" and cast corresponding argument to fmt_size_t. + * structs.cc (create_cblc_field_t): Use ULONGLONG instead of SIZE + for "attr". + * cbldiag.h (dbgmsg): Add ATTRIBUTE_PRINTF_1. + * gcobolspec.cc (lang_specific_driver): Use HOST_SIZE_T_PRINT_DEC + instead of "%ld" and cast corresponding argument to fmt_size_t. + * parse_ante.h (literal_of): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (evaluate_elem_t::dump): Likewise. + (arith_t::another_pair): Likewise. + (current_t::end_program): Likewise. + (file_add): Likewise. + (implicit_paragraph): Likewise. + (implicit_section): Likewise. + (data_division_ready): Use HOST_SIZE_T_PRINT_DEC instead of "%d" + and cast corresponding argument to fmt_size_t. + * symbols.h (struct cbl_field_t): Change attr member type from size_t + to uint64_t. + (cbl_field_t::set_attr): Change return type from size_t to uint64_t. + (cbl_field_t::clear_attr): Likewise. + (function_descr_t::init): Use HOST_SIZE_T_PRINT_UNSIGNED instead of + "%zu" or GCC_PRISZ instead of "z" or HOST_SIZE_T_PRINT_DEC instead + of "%zd" and cast corresponding argument to fmt_size_t. + (cbl_perform_tgt_t::dump): Likewise. + (numeric_group_attrs): Change return type from size_t to uint64_t. + +2025-04-24 Robert Dubner <rdubner@symas.com> + + * genapi.cc: (initialize_variable_internal): Change TRACE1 formatting. + (create_and_call): Repair RETURN-CODE processing. + (mh_source_is_group): Repair run-time IF type comparison. + (psa_FldLiteralA): Change TRACE1 formatting. + (parser_symbol_add): Eliminate unnecessary code. + * genutil.cc: Eliminate SET_EXCEPTION_CODE macro. + (get_data_offset_dest): Repair set_exception_code logic. + (get_data_offset_source): Likewise. + (get_binary_value): Likewise. + (refer_refmod_length): Likewise. + (refer_fill_depends): Likewise. + (refer_offset_dest): Likewise. + (refer_size_dest): Likewise. + (refer_offset_source): Likewise. + +2025-04-16 Bob Dubner <rdubner@symas.com> + + PR cobol/119759 + * LICENSE: Deleted. + +2025-04-15 Richard Biener <rguenther@suse.de> + + PR cobol/119302 + * Make-lang.in (GCOBOLIO_INSTALL_NAME): Define. + Use $(GCOBOLIO_INSTALL_NAME) for gcobol.3 manpage source + upon install. + +2025-04-14 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119776 + * lang.opt (fmax-errors): Remove. + * lang.opt.urls: Regenerate. + * cobol1.cc (cobol_langhook_handle_option) <case OPT_fmax_errors>: + Remove. + * gcobol.1: Document -fmax-errors=nerror rather than + -fmax-errors nerror. + +2025-04-14 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119777 + * lang.opt (include): Remove Var(cobol_include). + * cobol1.cc (cobol_langhook_handle_option) <case OPT_include>: Use + arg instead of cobol_include. + +2025-04-14 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119777 + * lang.opt (fsyntax-only): Remove. + * lang.opt.urls: Regenerate. + +2025-04-13 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + Simon Sobisch <simonsobisch@gnu.org> + + PR cobol/119217 + * parse.y: Rename OVERFLOW to OVERFLOW_kw. + Specify type name in %token directive. + * scan.l: Likewise. + * token_names.h: Regenerate. + +2025-04-13 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR cobol/119217 + * util.cc (class timespec_t): Rename to cbl_timespec. + +2025-04-13 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + * genapi.cc: Include target.h. + (section_label): Use ASM_COMMENT_START. + (paragraph_label): Likewise. + (parser_perform): Likewise. + (internal_perform_through): Likewise. + (hijack_for_development): Likewise. + +2025-04-12 Bob Dubner <rdubner@symas.com> + + PR cobol/119694 + * cbldiag.h: Eliminate getenv() calls. + * cdf.y: Likewise. + * cobol1.cc: Likewise. + * except.cc: Likewise. + * genapi.cc: Likewise. + * lexio.cc: Likewise. + * parse.y: Likewise. + * scan_ante.h: Likewise. + * show_parse.h: Likewise. + * symbols.cc: Likewise. + * symfind.cc: Likewise. + * util.cc: Likewise. + +2025-04-09 Bob Dubner <rdubner@symas.com> + + PR cobol/119682 + * genapi.cc: (cobol_compare): Change the call to __gg__compare(). + +2025-04-08 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119364 + * genapi.cc (function_handle_from_name): Use sizeof_pointer. + (parser_file_add): Use int_size_in_bytes(VOID_P) and + int_size_in_bytes(int). + (inspect_tally): Use int_size_in_bytes(VOID_P). + (inspect_replacing): Likewise. + (gg_array_of_field_pointers): Likewise. + (gg_array_of_file_pointers): Likewise. + (parser_set_pointers): Use sizeof_pointer. + * cobol1.cc (create_our_type_nodes_init): Use + int_size_in_bytes(SIZE_T) and int_size_in_bytes(VOID_P). + * gengen.cc (gg_array_of_size_t): Use int_size_in_bytes(SIZE_T). + (gg_array_of_bytes): Just use N, don't multiply it by + sizeof(unsigned char). + * parse.y: Include tree.h. Use int_size_in_bytes(ptr_type_node). + +2025-04-07 Iain Sandoe <iain@sandoe.co.uk> + + * symbols.cc : Remove trailing // on standard_internal. + (cbl_field_t::internalize): Print a warning if we fail to + initialise iconv. + +2025-04-07 Jakub Jelinek <jakub@redhat.com> + + * Make-lang.in (cobol/charmaps.cc, cobol/valconv.cc): Use a BRE + only sed regex. + +2025-04-07 Jakub Jelinek <jakub@redhat.com> + + PR web/119227 + * Make-lang.in (GCOBOL_HTML_FILES): New variable. + (cobol.install-html, cobol.html, cobol.srchtml): Use + $(GCOBOL_HTML_FILES) instead of gcobol.html gcobol-io.html. + (gcobol.html): Rename goal to ... + ($(build_htmldir)/gcobol/gcobol.html): ... this. Run mkinstalldirs. + (gcobol-io.html): Rename goal to ... + ($(build_htmldir)/gcobol/gcobol-io.html): ... this. Run mkinstalldirs. + +2025-04-06 Iain Sandoe <iain@sandoe.co.uk> + + PR cobol/119414 + * gcobolspec.cc (append_rdynamic, + append_allow_multiple_definition, append_fpic): Remove. + (lang_specific_driver): Remove platform-specific command + line option handling. + +2025-04-05 Iain Sandoe <iain@sandoe.co.uk> + + * gcobolspec.cc (SPEC_FILE): New. + (lang_specific_driver): Make the 'need libgcobol' flag global + so that the prelink callback can use it. Libm use is now handled + via the library spec. + (lang_specific_pre_link): Include libgcobol.spec where needed. + +2025-04-04 Bob Dubner <rdubner@symas.com> + + * cobol1.cc: Eliminate cobol_langhook_post_options. + * symbols.cc: Definition of RETURN-CODE special register sets + ::attr member to signable_e. + +2025-04-04 Bob Dubner <rdubner@symas.com> + + * cobol1.cc: (cobol_langhook_post_options): Implemented in order to set + flag_strict_aliasing to zero. + * genapi.cc: (set_user_status): Add comment. + (parser_intrinsic_subst): Expand SHOW_PARSE information. + (psa_global): Change names of return-code and upsi globals, + (psa_FldLiteralA): Set DECL_PRESERVE_P for FldLiteralA. + * gengen.cc: (show_type): Add POINTER type. + (gg_define_function_with_no_parameters): Set DECL_PRESERVE_P for COBOL- + style nested programs. (gg_array_of_bytes): Fix bad cast. + +2025-04-03 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119242 + * genapi.cc (binary_initial_from_float128): Use + native_encode_wide_int. + +2025-04-02 Bob Dubner <rdubner@symas.com> + + PR cobol/119521 + * genapi.cc: (parser_division): Change comment. + (parser_symbol_add): Change intermediate_t handling. + * parse.y: Multiple changes to new_alphanumeric() calls. + * parse_ante.h: Establish named constant for date function + calls. Change declaration of new_alphanumeric() function. + * symbols.cc: (new_temporary_impl): Use named constant + for default size of temporary alphanumerics. + * symbols.h: Establish MAXIMUM_ALPHA_LENGTH constant. + +2025-04-02 Jonathan Wakely <jwakely@redhat.com> + + * symfind.cc (finalize_symbol_map2): Use std::list::remove_if + instead of std::remove_if. + +2025-04-01 Bob Dubner <rdubner@symas.com> + + * genapi.cc: (section_label): Use xasprintf() instead of sprintf(). + (paragraph_label): Likewise. (leave_procedure): Likewise. + (find_procedure): Likewise. (parser_goto): Likewise. + (parser_enter_file): Likewise. + +2025-03-28 Jakub Jelinek <jakub@redhat.com> + + * Make-lang.in (cobol/charmaps.cc, cobol/valconv.cc): Used sed -e + instead of cp and multiple sed -i commands. Always prefix libgcobol + header names in #include directives with ../../libgcobol/ rather than + something depending on $(LIB_SOURCE). + +2025-03-28 Bob Dubner <rdubner@symas.com> + + * Make-lang.in: Eliminate libgcobol.h from gcc/cobol files. + * genapi.cc: Eliminate "#include libgcobol.h". + (parser_display_internal): Change comment. + * genmath.cc: Eliminate "#include libgcobol.h". + * genutil.cc: Likewise. + (get_power_of_ten): Change comment. + * structs.cc: Eliminate cblc_int128_type_node. + * structs.h: Likewise. + * symbols.h: Receive comment from libgcobol.h + +2025-03-28 Jakub Jelinek <jakub@redhat.com> + + * Make-lang.in (cobol.srcextra): Use sed to turn + .../gcc/cobol/*.{y,l,h,cc} and cobol/*.{y,l,h,cc} in #line directives + into just *.{y,l,h,cc}. + +2025-03-28 Richard Biener <rguenther@suse.de> + + PR bootstrap/119513 + * Make-lang.in (cobol.srcextra): Use cp instead of ln, ignore + errors. + +2025-03-28 Bob Dubner <rdubner@symas.com> + + * genapi.cc: (create_and_call): cast unsigned char to int + +2025-03-28 Richard Biener <rguenther@suse.de> + + * genapi.cc (initial_from_float128): Use native_encode_real. + +2025-03-28 Iain Sandoe <iain@sandoe.co.uk> + + * cobol-system.h: Remove <cmath>. + +2025-03-26 Jonathan Wakely <jwakely@redhat.com> + + * except.cc (cbl_enabled_exceptions_t::turn_on_off): Replace + quadratic loop with a single pass. + +2025-03-26 Bob Dubner <rdubner@symas.com> + + * genapi.cc: (parser_display_internal): Adjust for E vs e exponent notation. + * parse.y: (literal_refmod_valid): Display correct value in error message. + +2025-03-26 Jakub Jelinek <jakub@redhat.com> + + PR cobol/119242 + * genutil.h (get_power_of_ten): Remove #pragma GCC diagnostic + around declaration. + * genapi.cc (psa_FldLiteralN): Change type of value from + __int128 to FIXED_WIDE_INT(128). Remove #pragma GCC diagnostic + around the declaration. Use wi::min_precision to determine + minimum unsigned precision of the value. Use wi::neg_p instead + of value < 0 tests and wi::set_bit_in_zero<FIXED_WIDE_INT(128)> + to build sign bit. Handle field->data.capacity == 16 like + 1, 2, 4 and 8, use wide_int_to_tree instead of build_int_cst. + (mh_source_is_literalN): Remove #pragma GCC diagnostic around + the definition. + (binary_initial_from_float128): Likewise. + * genutil.cc (get_power_of_ten): Remove #pragma GCC diagnostic + before the definition. + +2025-03-25 Bob Dubner <rdubner@symas.com> + Richard Biener <rguenth@suse.de> + Jakub Jelinek <jakub@redhat.com> + James K. Lowden <jklowden@cobolworx.com> + Robert Dubner <rdubher@symas.com> + + PR cobol/119241 + * cdf.y: (cdfval_base_t::operator()): Return const. + * cdfval.h: (struct cdfval_base_t): Add const cdfval_base_t& + operator(). + (struct cdfval_t): Add cdfval_t constructor. Change cdf_value + definitions. + * gcobolspec.cc (lang_specific_driver): Formatting fix. + * genapi.cc: Include fold-const.h and realmpfr.h. + (initialize_variable_internal): Use real_to_decimal instead of + strfromf128. + (get_binary_value_from_float): Use wide_int_to_tree instead of + build_int_cst_type. + (psa_FldLiteralN): Use fold_convert instead of strfromf128, + real_from_string and build_real. + (parser_display_internal): Rewritten to work on REAL_VALUE_TYPE + rather than _Float128. + (mh_source_is_literalN): Use FIXED_WIDE_INT(128) rather than + __int128, wide_int_to_tree rather than build_int_cst_type, + fold_convert rather than build_string_literal. + (real_powi10): New function. + (binary_initial_from_float128): Change type of last argument from + _Float128 to REAL_VALUE_TYPE, process it using real.cc and mpfr + APIs. + (digits_from_float128): Likewise. + (initial_from_float128): Make static. Remove value argument, add + local REAL_VALUE_TYPE value variable instead, process it using + real.cc and native_encode_expr APIs. + (parser_symbol_add): Adjust initial_from_float128 caller. + * genapi.h (initial_from_float128): Remove declaration. + * genutil.cc (get_power_of_ten): Change return type from __int128 + to FIXED_WIDE_INT(128), ditto for retval type, change type of pos + from __int128 to unsigned long long. + (scale_by_power_of_ten_N): Use wide_int_to_tree instead of + build_int_cst_type. Use FIXED_WIDE_INT(128) instead of __int128 + as power_of_ten variable type. + (copy_little_endian_into_place): Likewise. + * genutil.h (get_power_of_ten): Change return type from __int128 + to FIXED_WIDE_INT(128). + * parse.y (%union): Change type of float128 from _Float128 to + REAL_VALUE_TYPE. + (string_of): Change argument type from _Float128 to + const REAL_VALUE_TYPE &, use real_to_decimal rather than + strfromf128. Add another overload with tree argument type. + (field: cdf): Use real_zerop rather than comparison against 0.0. + (occurs_clause, const_value): Use real_to_integer. + (value78): Use build_real and real_to_integer. + (data_descr1): Use real_to_integer. + (count): Use real_to_integer, real_from_integer and real_identical + instead of direct comparison. + (value_clause): Use real_from_string3 instead of num_str2i. Use + real_identical instead of direct comparison. Use build_real. + (allocate): Use real_isneg and real_iszero instead of <= 0 comparison. + (move_tgt): Use real_to_integer, real_value_truncate, + real_from_integer and real_identical instead of comparison of casts. + (cce_expr): Use real_arithmetic and real_convert or real_value_negate + instead of direct arithmetics on _Float128. + (cce_factor): Use real_from_string3 instead of numstr2i. + (literal_refmod_valid): Use real_to_integer. + * symbols.cc (symbol_table_t::registers_t::registers_t): Formatting + fix. + (ERROR_FIELD): Likewise. + (extend_66_capacity): Likewise. + (cbl_occurs_t::subscript_ok): Use real_to_integer, real_from_integer + and real_identical. + * symbols.h (cbl_field_data_t::etc_t::value): Change type from + _Float128 to tree. + (cbl_field_data_t::etc_t::etc_t): Adjust defaulted argument value. + (cbl_field_data_t::cbl_field_data_t): Formatting fix. Use etc() + rather than etc(0). + (cbl_field_data_t::value_of): Change return type from _Float128 to + tree. + (cbl_field_data_t::operator=): Change return and argument type from + _Float128 to tree. + (cbl_field_data_t::valify): Use real_from_string, real_value_truncate + and build_real. + (cbl_field_t::same_as): Use build_zero_cst instead of _Float128(0.0). + +2025-03-24 Iain Sandoe <iain@sandoe.co.uk> + + * cdf-copy.cc: Move host include before system.h + +2025-03-24 Andreas Schwab <schwab@suse.de> + + PR cobol/119390 + * gcobolspec.cc (lang_specific_driver): Use pointer instead of + copying into fixed array. + +2025-03-21 Iain Sandoe <iain@sandoe.co.uk> + + * gcobolspec.cc (lang_specific_driver): Add libstdc++ + for any link line. + +2025-03-21 Iain Sandoe <iain@sandoe.co.uk> + + * gcobolspec.cc (add_arg_lib): Fix typo. + (lang_specific_driver): Arrange to append both -lgcobol + and -static-libgcobol for targets without + HAVE_LD_STATIC_DYNAMIC. + +2025-03-21 Jakub Jelinek <jakub@redhat.com> + + * parse.y: Rename COB_BLOCK to BLOCK_kw, COB_SIGNED to SIGNED_kw and + COB_UNSIGNED to UNSIGNED_kw. + * scan.l: Likewise. + * token_names.h: Regenerate. + +2025-03-21 Richard Biener <rguenther@suse.de> + + * symbols.cc (empty_float, empty_comp5, empty_literal, + empty_conditional, debug_registers, special_registers): Move + global cbl_field_t typed data to ... + (symbol_table_init): ... local scope here. + +2025-03-21 Richard Biener <rguenther@suse.de> + + PR cobol/119241 + * symbols.h: Do not typedef tree. + * cdf.y: Include coretypes.h and tree.h. + * symbols.cc: Likewise. + * symfind.cc: Likewise. + * util.cc: Likewise. + * parse.y: Include coretypes.h and tree.h where appropriate. + Rename BLOCK to COB_BLOCK, SIGNED to COB_SIGNED, UNSIGNED + to COB_UNSIGNED. + * scan.l: Likewise. + * token_names.h: Likewise. + * cobol1.cc: Do not define HOWEVER_GCC_DEFINES_TREE. + * except.cc: Likewise. + * genapi.cc: Likewise. + * gengen.cc: Likewise. + * genmath.cc: Likewise. + * genutil.cc: Likewise. + * structs.cc: Likewise. + +2025-03-20 Iain Sandoe <iain@sandoe.co.uk> + + * cdfval.h (struct cdfval_t): Overload long instead of int64_t. + 2025-03-18 Iain Sandoe <iain@sandoe.co.uk> * gcobolspec.cc (append_rpath): Remove. diff --git a/gcc/cobol/LICENSE b/gcc/cobol/LICENSE deleted file mode 100644 index aa5ba60..0000000 --- a/gcc/cobol/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -######################################################################### -# -# 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. diff --git a/gcc/cobol/Make-lang.in b/gcc/cobol/Make-lang.in index 5b61ae9..a52e6d8 100644 --- a/gcc/cobol/Make-lang.in +++ b/gcc/cobol/Make-lang.in @@ -34,12 +34,18 @@ # - the compiler proper (eg: cc1plus) # - define the names for selecting the language in LANGUAGES. +# Use strict warnings for this front end. +cobol-warn = $(STRICT_WARN) + GCOBOL_INSTALL_NAME := $(shell echo gcobol|sed '$(program_transform_name)') +GCOBOLIO_INSTALL_NAME := $(shell echo gcobol-io|sed '$(program_transform_name)') GCOBOL_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcobol|sed '$(program_transform_name)') GCOBC_INSTALL_NAME := $(shell echo gcobc|sed '$(program_transform_name)') GCOBC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcobc|sed '$(program_transform_name)') +GCOBOL_HTML_FILES = $(addprefix $(build_htmldir)/gcobol/,gcobol.html gcobol-io.html) + cobol: cobol1$(exeext) cobol.serial = cobol1$(exeext) .PHONY: cobol @@ -65,6 +71,7 @@ cobol1_OBJS = \ cobol/genmath.o \ cobol/gengen.o \ cobol/lexio.o \ + cobol/messages.o \ cobol/parse.o \ cobol/scan.o \ cobol/structs.o \ @@ -87,29 +94,8 @@ cobol1_OBJS = \ # Various #includes in the files copied from gcc/libgcobol need to be modified # so that the .h files can be found. -cobol/charmaps.cc: $(LIB_SOURCE)/charmaps.cc - cp $^ $@ - sed -i "s|\"ec[.]h\"|\"$(LIB_SOURCE)/ec.h\"|g" $@ - sed -i "s|\"common-defs[.]h\"|\"$(LIB_SOURCE)/common-defs.h\"|g" $@ - sed -i "s|\"io[.]h\"|\"$(LIB_SOURCE)/io.h\"|g" $@ - sed -i "s|\"gcobolio[.]h\"|\"$(LIB_SOURCE)/gcobolio.h\"|g" $@ - sed -i "s|\"libgcobol[.]h\"|\"$(LIB_SOURCE)/libgcobol.h\"|g" $@ - sed -i "s|\"gfileio[.]h\"|\"$(LIB_SOURCE)/gfileio.h\"|g" $@ - sed -i "s|\"charmaps[.]h\"|\"$(LIB_SOURCE)/charmaps.h\"|g" $@ - sed -i "s|\"valconv[.]h\"|\"$(LIB_SOURCE)/valconv.h\"|g" $@ - sed -i "s|\"exceptl[.]h\"|\"$(LIB_SOURCE)/exceptl.h\"|g" $@ - -cobol/valconv.cc: $(LIB_SOURCE)/valconv.cc - cp $^ $@ - sed -i "s|\"ec[.]h\"|\"$(LIB_SOURCE)/ec.h\"|g" $@ - sed -i "s|\"common-defs[.]h\"|\"$(LIB_SOURCE)/common-defs.h\"|g" $@ - sed -i "s|\"io[.]h\"|\"$(LIB_SOURCE)/io.h\"|g" $@ - sed -i "s|\"gcobolio[.]h\"|\"$(LIB_SOURCE)/gcobolio.h\"|g" $@ - sed -i "s|\"libgcobol[.]h\"|\"$(LIB_SOURCE)/libgcobol.h\"|g" $@ - sed -i "s|\"gfileio[.]h\"|\"$(LIB_SOURCE)/gfileio.h\"|g" $@ - sed -i "s|\"charmaps[.]h\"|\"$(LIB_SOURCE)/charmaps.h\"|g" $@ - sed -i "s|\"valconv[.]h\"|\"$(LIB_SOURCE)/valconv.h\"|g" $@ - sed -i "s|\"exceptl[.]h\"|\"$(LIB_SOURCE)/exceptl.h\"|g" $@ +cobol/charmaps.cc cobol/valconv.cc: cobol/%.cc: $(LIB_SOURCE)/%.cc + sed -e '/^#include/s,"\([^"]*[^g"].h\)","../../libgcobol/\1",' $^ > $@ LIB_SOURCE_H=$(wildcard $(LIB_SOURCE)/*.h) @@ -175,10 +161,9 @@ cobol/cdf.cc: cobol/cdf.y FLEX_WARNING = warning, dangerous trailing context cobol/scan.cc: cobol/scan.l - $(FLEX) -o$@ $(LFLAGS) $< >$@~ 2>&1 + $(FLEX) -o$@ $(LFLAGS) $< 2>$@~ || { cat $@~ >&1; exit 1; } awk '! /$(FLEX_WARNING)/ {print > "/dev/stderr"; nerr++} \ - END {print "$(FLEX):", NR, "messages" > "/dev/stderr"; \ - exit nerr}' $@~ + END {print "$(FLEX):", NR, "messages" > "/dev/stderr"}' $@~ @rm $@~ @@ -241,6 +226,7 @@ cobol/scan.o: cobol/scan.cc \ $(srcdir)/cobol/scan_post.h \ $(srcdir)/cobol/symbols.h \ $(srcdir)/cobol/util.h \ + $(srcdir)/cobol/token_names.h \ $(srcdir)/hwint.h \ $(srcdir)/system.h \ $(srcdir)/../include/ansidecl.h \ @@ -257,6 +243,15 @@ cobol/scan.o: cobol/scan.cc \ cobol/cdf.cc \ cobol/parse.cc +# Update token names if the generator script is installed +# (by a developer) and there's been a change. +$(srcdir)/cobol/token_names.h: cobol/parse.cc + @if [ -f $@.gen ]; then \ + $@.gen $(subst .cc,.h,$^) \ + | diff -u $@ - \ + | patch -t --set-time $@ ; \ + fi + # # The src<foo> targets are executed if # ‘--enable-generated-files-in-srcdir’ was specified as a configure @@ -272,8 +267,9 @@ cobol/scan.o: cobol/scan.cc \ # output, and do not require those tools to be installed. # cobol.srcextra: cobol/parse.cc cobol/cdf.cc cobol/scan.cc - ln -f $^ cobol/parse.h cobol/cdf.h $(srcdir)/cobol/ - + -for i in $^ cobol/parse.h cobol/cdf.h; do \ + sed -e '/^#line/s,"\(.*gcc/\)\?cobol/\([^/]*\.\([ylh]\|cc\)\)","\2",' $$i \ + > $(srcdir)/$$i; done # And the cobol1 front end @@ -302,16 +298,14 @@ cobol.install-common: installdirs rm -f $(DESTDIR)$(bindir)/$(GCOBOL_TARGET_INSTALL_NAME)$(exeext); \ rm -f $(DESTDIR)$(bindir)/$(GCOBC_TARGET_INSTALL_NAME)$(exeext); \ ( cd $(DESTDIR)$(bindir) && \ - $(LN) $(GCOBOL_INSTALL_NAME)$(exeext) $(GCOBOL_TARGET_INSTALL_NAME)$(exeext) ); \ + $(LN) $(GCOBOL_INSTALL_NAME)$(exeext) $(GCOBOL_TARGET_INSTALL_NAME)$(exeext) && \ $(LN) $(GCOBC_INSTALL_NAME)$(exeext) $(GCOBC_TARGET_INSTALL_NAME)$(exeext) ); \ fi; \ fi - mkdir -p $(DESTDIR)$(datadir)/gcobol/udf - $(INSTALL_DATA) $(srcdir)/cobol/udf/* $(DESTDIR)$(datadir)/gcobol/udf/ cobol.install-man: installdirs $(INSTALL_DATA) $(srcdir)/cobol/gcobol.1 $(DESTDIR)$(man1dir)/$(GCOBOL_INSTALL_NAME)$(man1ext) - $(INSTALL_DATA) $(srcdir)/cobol/gcobol.3 $(DESTDIR)$(man3dir)/ + $(INSTALL_DATA) $(srcdir)/cobol/gcobol.3 $(DESTDIR)$(man3dir)/$(GCOBOLIO_INSTALL_NAME)$(man3ext) cobol.install-info: @@ -321,8 +315,8 @@ cobol.install-pdf: installdirs gcobol.pdf gcobol-io.pdf cobol.install-plugin: -cobol.install-html: installdirs gcobol.html gcobol-io.html - $(INSTALL_DATA) gcobol.html gcobol-io.html $(DESTDIR)$(htmldir)/ +cobol.install-html: installdirs $(GCOBOL_HTML_FILES) + $(INSTALL_DATA) $(GCOBOL_HTML_FILES) $(DESTDIR)$(htmldir)/ cobol.info: cobol.srcinfo: @@ -335,20 +329,22 @@ cobol.srcpdf: gcobol.pdf gcobol-io.pdf ln $^ $(srcdir)/cobol/ gcobol.pdf: $(srcdir)/cobol/gcobol.1 - groff -mdoc -T pdf $^ > $@~ + groff -mdoc -t -T pdf $^ > $@~ @mv $@~ $@ gcobol-io.pdf: $(srcdir)/cobol/gcobol.3 groff -mdoc -T pdf $^ > $@~ @mv $@~ $@ -cobol.html: gcobol.html gcobol-io.html -cobol.srchtml: gcobol.html gcobol-io.html +cobol.html: $(GCOBOL_HTML_FILES) +cobol.srchtml: $(GCOBOL_HTML_FILES) ln $^ $(srcdir)/cobol/ -gcobol.html: $(srcdir)/cobol/gcobol.1 +$(build_htmldir)/gcobol/gcobol.html: $(srcdir)/cobol/gcobol.1 + $(mkinstalldirs) $(build_htmldir)/gcobol mandoc -T html $^ > $@~ @mv $@~ $@ -gcobol-io.html: $(srcdir)/cobol/gcobol.3 +$(build_htmldir)/gcobol/gcobol-io.html: $(srcdir)/cobol/gcobol.3 + $(mkinstalldirs) $(build_htmldir)/gcobol mandoc -T html $^ > $@~ @mv $@~ $@ @@ -358,18 +354,24 @@ cobol.uninstall: rm -rf $(DESTDIR)$(bindir)/$(GCOBOL_INSTALL_NAME)$(exeext) \ $(DESTDIR)$(bindir)/$(GCOBC_INSTALL_NAME) \ $(DESTDIR)$(datadir)/gcobol/ \ - $(DESTDIR)$(man1dir)/$(GCOBOL_INSTALL_NAME).1 \ - $(DESTDIR)$(man3dir)/gcobol.3 + $(DESTDIR)$(man1dir)/$(GCOBOL_INSTALL_NAME)$(man1ext) \ + $(DESTDIR)$(man3dir)/$(GCOBOLIO_INSTALL_NAME)$(man3ext) cobol.man: cobol.srcman: cobol.mostlyclean: -cobol.clean: +gcobol.clean: +# This is intended for non-general use. It is a last-ditch effort to flush +# out all oject files and executable code for gcobol and libgcobol, causing +# a complete rebuild of all executable code. rm -fr gcobol cobol1 cobol/* \ ../*/libgcobol/* +cobol.clean: + rm -fr gcobol cobol1 cobol/* + cobol.distclean: cobol.maintainer-clean: @@ -391,3 +393,4 @@ cobol.stagefeedback: stagefeedback-start selftest-cobol: lang_checks += check-cobol + diff --git a/gcc/cobol/TODO b/gcc/cobol/TODO new file mode 100644 index 0000000..02ee0e2 --- /dev/null +++ b/gcc/cobol/TODO @@ -0,0 +1,33 @@ +Below is listed work to be done, hopefully all of it in 2025 for +GCC 16. They are vaguely in priority order, in that addressing more +technical issues may illuminate ways to attack more amorphous ones. + +Portability: + - host/target, for cross-compilation + - OS portability, BSD, macOS, Solaris + - 64-bit portability, LE + - 64-bit portability, BE + - 2025 goal: Compile & run on primary & secondary GCC 15 platforms + https://www.gnu.org/software/gcc/gcc-15/criteria.html + +Correctness: + - LTO ODR, PR 119215 + - cppcheck + - valgrind + - -static produces dynamic + +Efficiency: + - Code size for MOVE 'a' TO FOO(1,1) + - EC checking + +COBOL Features: + - XML, JSON + - MF system functions + - National characters (and Unicode, for IBM) + - GLOBAL and PERFORM declaratives + - dialect feature names (to enable and enumerate) + +GCC features: + - make check-nist + - -Werror, -Wno-<foo> + - -fEC-ALL, -fno-EC-I-O diff --git a/gcc/cobol/cbldiag.h b/gcc/cobol/cbldiag.h index ed754f1..388bc78 100644 --- a/gcc/cobol/cbldiag.h +++ b/gcc/cobol/cbldiag.h @@ -33,14 +33,19 @@ #else #define _CBLDIAG_H +#if GCOBOL_GETENV +#define gcobol_getenv(x) getenv(x) +#else +#define gcobol_getenv(x) ((char *)nullptr) +#endif + const char * cobol_filename(); /* * These are user-facing messages. They go through the gcc * diagnostic framework and use text that can be localized. */ -void yyerror( const char fmt[], ... ); -bool yywarn( const char fmt[], ... ); +void yyerror( const char fmt[], ... ) ATTRIBUTE_GCOBOL_DIAG(1, 2); /* Location type. Borrowed from parse.h as generated by Bison. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED @@ -72,39 +77,161 @@ struct YDFLTYPE #endif +struct cbl_loc_t { + int first_line; + int first_column; + int last_line; + int last_column; + + cbl_loc_t( const YYLTYPE& loc ) + : first_line(loc.first_line) + , first_column(loc.first_column) + , last_line(loc.last_line) + , last_column(loc.last_column) + {} + + operator YYLTYPE() const { + return { first_line, first_column, last_line, last_column }; + } +}; + +/* + * Naming Convention: Names end with a letter that indicates + * their kind: + * F fatal, "fatal error: " + * I ice, "internal compiler error: " + * E error, "error: " + * S sorry, "sorry, unimplemented: " + * W warning, "warning: " + * A anachronism, "anachronism: " + * N note, "note: " + * D debug, "debug: " + */ +enum cbl_diag_id_t : uint64_t { + CdfNotFoundW, + CdfParameterW, + + EcUnknownW, + + LexIncludeE, + LexIncludeOkN, + LexIndicatorE, + LexInputN, + LexLineE, + LexPreprocessE, + LexReplaceE, + LexSeparatorE, + + IbmEjectE, + IbmEqualAssignE, + IbmLengthOf, + IbmProcedurePointer, + IbmSectionNegE, + IbmSectionRangeE, + IbmSectionSegmentW, + IbmStopNumber, + IbmVolatileE, + IbmVolatileW, // dialect warning for ignored syntax + + IsoResume, + + MfBinaryLongLong, + MfCallGiving, + MfCdfDollar, + MfComp6, + MfCompX, + MfLevel_1_Occurs, + MfLevel78, + MfMovePointer, + MfReturningNum, + MfUsageTypename, + MfTrailing, + + Par78CdfDefinedW, + ParIconvE, + ParInfoI, + ParLangInfoW, + ParLiteral2W, + ParLocaleW, + ParNoCorrespondingW, + ParNumstrW, + ParUnresolvedProcE, + + SynApplyCommit, + SynFileCodeSet, + SynHighOrderBit, + SynRecordingMode, + SynSetLocaleTo, + SynSetToLocale, + + DiagDiagDiag // always last +}; + +bool cbl_message( cbl_diag_id_t id, const char msg[], ... ) + ATTRIBUTE_GCOBOL_DIAG(2, 3); + +bool cbl_message( cbl_loc_t loc, cbl_diag_id_t id, const char msg[], ... ) + ATTRIBUTE_GCOBOL_DIAG(3, 4); + +bool +dialect_ok( const cbl_loc_t& loc, cbl_diag_id_t id, const char term[], bool ok = true ); + +static inline bool +dialect_not_ok( const cbl_loc_t& loc, cbl_diag_id_t id, const char term[] ) { + return dialect_ok(loc, id, term, false); +} + +// Diagnostic format specifiers are documented in gcc/pretty-print.cc // an error at a location, called from the parser for semantic errors -void error_msg( const YYLTYPE& loc, const char gmsgid[], ... ); +void error_msg( const YYLTYPE& loc, const char gmsgid[], ... ) + ATTRIBUTE_GCOBOL_DIAG(2, 3); -void dialect_error( const YYLTYPE& loc, const char term[], const char dialect[] ); +bool +warn_msg( const YYLTYPE& loc, const char gmsgid[], ... ) + ATTRIBUTE_GCOBOL_DIAG(2, 3); +// an error that uses token_location, not yylloc +void error_msg_direct( const char gmsgid[], ... ) + ATTRIBUTE_GCOBOL_DIAG(1, 2); // for CDF and other warnings that refer back to an earlier line // (not in diagnostic framework yet) -void yyerrorvl( int line, const char *filename, const char fmt[], ... ); +void yyerrorvl( int line, const char *filename, const char fmt[], ... ) + ATTRIBUTE_PRINTF_3; -void cbl_unimplementedw(const char *gmsgid, ...); // warning -void cbl_unimplemented(const char *gmsgid, ...); // error -void cbl_unimplemented_at( const YYLTYPE& loc, const char *gmsgid, ... ); +void cbl_unimplementedw(cbl_diag_id_t id, const char *gmsgid, ...) + ATTRIBUTE_GCOBOL_DIAG(2, 3); // warning +void cbl_unimplemented(const char *gmsgid, ...) + ATTRIBUTE_GCOBOL_DIAG(1, 2); // error +void cbl_unimplemented_at( const YYLTYPE& loc, const char *gmsgid, ... ) + ATTRIBUTE_GCOBOL_DIAG(2, 3); /* - * dbgmsg produce messages not intended for the user. They cannot - * be localized and fwrite directly to standard out. dbgmsg is activated by - * -fflex-debug or -fyacc-debug. + * dbgmsg produce messages not intended for the user. They cannot be localized + * and fwrite directly to standard error. dbgmsg is activated by -fflex-debug + * or -fyacc-debug. */ -void dbgmsg( const char fmt[], ... ); +void dbgmsg( const char fmt[], ... ) ATTRIBUTE_PRINTF_1; void gcc_location_set( const YYLTYPE& loc ); +void gcc_location_dump(); + // tree.h defines yy_flex_debug as a macro because options.h #if ! defined(yy_flex_debug) template <typename LOC> static void location_dump( const char func[], int line, const char tag[], const LOC& loc) { - extern int yy_flex_debug; - if( yy_flex_debug && getenv("update_location") ) - fprintf(stderr, "%s:%d: %s location (%d,%d) to (%d,%d)\n", - func, line, tag, - loc.first_line, loc.first_column, loc.last_line, loc.last_column); + extern int yy_flex_debug; // cppcheck-suppress shadowVariable + if( yy_flex_debug ) { + const char *detail = gcobol_getenv("update_location"); + if( detail ) { // cppcheck-suppress knownConditionTrueFalse + fprintf(stderr, "%s:%d: %s location (%d,%d) to (%d,%d)\n", + func, line, tag, + loc.first_line, loc.first_column, loc.last_line, loc.last_column); + if( *detail == '2' ) gcc_location_dump(); + } + } } #endif // defined(yy_flex_debug) diff --git a/gcc/cobol/cdf-copy.cc b/gcc/cobol/cdf-copy.cc index 179dbac..11be9b8 100644 --- a/gcc/cobol/cdf-copy.cc +++ b/gcc/cobol/cdf-copy.cc @@ -34,13 +34,13 @@ // // We regret any confusion engendered. +#include "config.h" + #include "cobol-system.h" #include "cbldiag.h" #include "util.h" #include "copybook.h" -#include <glob.h> - #define COUNT_OF(X) (sizeof(X) / sizeof(X[0])) /* @@ -75,7 +75,6 @@ * space. This function only applies them. */ -extern int yydebug; const char * cobol_filename(); bool is_fixed_format(); bool is_reference_format(); @@ -125,13 +124,13 @@ verify_bounds( size_t pos, size_t size, const char input[] ) { */ const char * esc( size_t len, const char input[] ) { - static char spaces[] = "([,;]?[[:space:]])+"; - static char spaceD[] = "(\n {6}D" "|" "[,;]?[[:space:]])+"; + static const char space[] = "([,;]?[[:space:]])+"; + static const char spaceD[] = "(\n {6}D" "|" "[,;]?[[:space:]])+"; static char buffer[64 * 1024]; char *p = buffer; const char *eoinput = input + len; - const char *spacex = is_reference_format()? spaceD : spaces; + const char *spacex = is_reference_format()? spaceD : space; for( const char *s=input; *s && s < eoinput; s++ ) { *p = '\0'; @@ -179,12 +178,6 @@ esc( size_t len, const char input[] ) { return buffer; // caller must strdup static buffer } -static int -glob_error(const char *epath, int eerrno) { - dbgmsg("%s: COPY file search: '%s': %s", __func__, epath, xstrerror(eerrno)); - return 0; -} - void copybook_directory_add( const char gcob_copybook[] ) { if( !gcob_copybook ) return; @@ -231,27 +224,15 @@ copybook_extension_add( const char ext[] ) { copybook.extensions_add( ext, alt ); } -extern int yydebug; -const char * copybook_elem_t::extensions; +std::list<const char *> copybook_elem_t::suffixes { + "", ".cpy", ".CPY", ".cbl", ".CBL", ".cob", ".COB" +}; void copybook_t::extensions_add( const char ext[], const char alt[] ) { - char *output; - if( alt ) { - output = xasprintf("%s,%s", ext, alt); - } else { - output = xstrdup(ext); - } - gcc_assert(output); - if( book.extensions ) { - char *s = xasprintf("%s,%s", output, book.extensions); - free(const_cast<char*>(book.extensions)); - free(output); - book.extensions = s; - } else { - book.extensions = output; - } + book.suffixes.push_back(ext); + if( alt ) book.suffixes.push_back(alt); } static inline ino_t @@ -265,9 +246,7 @@ inode_of( int fd ) { int copybook_elem_t::open_file( const char directory[], bool literally ) { - int erc; - char *pattern, *copier = xstrdup(cobol_filename()); - char *dname = NULL; + char *dname = NULL, *copier = xstrdup(cobol_filename()); if ( directory ) { dname = xstrdup(directory); @@ -300,7 +279,7 @@ copybook_elem_t::open_file( const char directory[], bool literally ) { dbgmsg("copybook_elem_t::open_file: trying %s", path); if( (this->fd = open(path, O_RDONLY)) == -1 ) { - dbgmsg("could not open %s: %m", path); + dbgmsg("could not open %s: %s", path, xstrerror(errno)); return fd; } this->source.name = path; @@ -313,52 +292,27 @@ copybook_elem_t::open_file( const char directory[], bool literally ) { } gcc_assert( ! literally ); - if( extensions ) { - pattern = xasprintf("%s{,.cpy,.CPY,.cbl,.CBL,.cob,.COB,%s}", - path, this->extensions); - } else { - pattern = xasprintf("%s{,.cpy,.CPY,.cbl,.CBL,.cob,.COB}", path); - } - free(copier); - static int flags = GLOB_MARK | GLOB_BRACE | GLOB_TILDE; - glob_t globber; - - if( (erc = glob(pattern, flags, glob_error, &globber)) != 0 ) { - switch(erc) { - case GLOB_NOSPACE: - yywarn("COPY file search: out of memory"); - break; - case GLOB_ABORTED: - yywarn("COPY file search: read error"); - break; - case GLOB_NOMATCH: - dbgmsg("COPY '%s': no files match %s", this->source.name, pattern); - default: - break; // caller says no file found - } - return -1; - } - - free(pattern); + for( auto suffix : suffixes ) { + std::string pattern(path); + pattern += suffix; + dbgmsg("%s: trying %s", __func__, pattern.c_str()); - for( size_t i=0; i < globber.gl_pathc; i++ ) { - auto filename = globber.gl_pathv[i]; + auto filename = pattern.c_str(); if( (this->fd = open(filename, O_RDONLY)) != -1 ) { dbgmsg("found copybook file %s", filename); this->source.name = xstrdup(filename); if( ! cobol_filename(this->source.name, inode_of(fd)) ) { - error_msg(source.loc, "recursive copybook: '%s' includes itself", this->source); + error_msg(source.loc, "recursive copybook: '%s' includes itself", + this->source.name); (void)! close(fd); fd = -1; } - globfree(&globber); + dbgmsg("%s: opened %s as fd %d", __func__, source.name, fd); return fd; } } - yywarn("could not open copy source for '%s'", source); - globfree(&globber); return -1; } diff --git a/gcc/cobol/cdf.y b/gcc/cobol/cdf.y index 12d11e7..72e46b3f 100644 --- a/gcc/cobol/cdf.y +++ b/gcc/cobol/cdf.y @@ -30,6 +30,9 @@ %{ #include "cobol-system.h" +#include "coretypes.h" +#include "tree.h" +#undef yy_flex_debug #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -92,7 +95,7 @@ void input_file_status_notify(); } \ location_dump("cdf.c", __LINE__, "current", (Current)); \ input_file_status_notify(); \ - gcc_location_set( location_set(Current) ); \ + location_set(Current); \ } while (0) %} @@ -102,14 +105,14 @@ void input_file_status_notify(); using std::map; - static map<std::string, cdfval_t> dictionary; - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" static bool cdfval_add( const char name[], const cdfval_t& value, bool override = false ) { + cdf_values_t& dictionary( cdf_dictionary() ); + if( scanner_parsing() ) { if( ! override ) { if( dictionary.find(name) != dictionary.end() ) return false; @@ -120,6 +123,8 @@ void input_file_status_notify(); } static void cdfval_off( const char name[] ) { + cdf_values_t& dictionary( cdf_dictionary() ); + if( scanner_parsing() ) { auto p = dictionary.find(name); if( p == dictionary.end() ) { @@ -146,84 +151,28 @@ void input_file_status_notify(); cdfval_t operator/( const cdfval_base_t& lhs, const cdfval_base_t& rhs ); cdfval_t negate( cdfval_base_t lhs ); + cbl_field_t + cdf_literalize( const std::string& name, const cdfval_t& value ); + } %{ static char *display_msg; const char * keyword_str( int token ); -static class exception_turns_t { - typedef std::list<size_t> filelist_t; - typedef std::map<ec_type_t, filelist_t> ec_filemap_t; - ec_filemap_t exceptions; - public: - bool enabled, location; - - exception_turns_t() : enabled(false), location(false) {}; - - const ec_filemap_t& exception_files() const { return exceptions; } - - struct args_t { - size_t nexception; - cbl_exception_files_t *exceptions; - }; - - bool add_exception( ec_type_t type, const filelist_t files = filelist_t() ) { - ec_disposition_t disposition = ec_type_disposition(type); - if( disposition != ec_implemented(disposition) ) { - cbl_unimplementedw("CDF: exception '%s'", ec_type_str(type)); - } - auto elem = exceptions.find(type); - if( elem != exceptions.end() ) return false; // cannot add twice - - exceptions[type] = files; - return true; - } - - args_t args() const { - args_t args; - args.nexception = exceptions.size(); - args.exceptions = NULL; - if( args.nexception ) { - args.exceptions = new cbl_exception_files_t[args.nexception]; - } - std::transform( exceptions.begin(), exceptions.end(), args.exceptions, - []( auto& input ) { - cbl_exception_files_t output; - output.type = input.first; - output.nfile = input.second.size(); - output.files = NULL; - if( output.nfile ) { - output.files = new size_t[output.nfile]; - std::copy(input.second.begin(), - input.second.end(), - output.files ); - } - return output; - } ); - return args; - } - - void clear() { - for( auto& ex : exceptions ) { - ex.second.clear(); - } - exceptions.clear(); - enabled = location = false; - } - -} exception_turns; - - -static bool -apply_cdf_turn( exception_turns_t& turns ) { - for( auto elem : turns.exception_files() ) { +exception_turn_t exception_turn; + +bool +apply_cdf_turn( const exception_turn_t& turn ) { + cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); + + for( auto elem : turn.exception_files() ) { std::set<size_t> files(elem.second.begin(), elem.second.end()); - enabled_exceptions.turn_on_off(turns.enabled, - turns.location, + enabled_exceptions.turn_on_off(turn.enabled, + turn.location, elem.first, files); } - if( getenv("SHOW_PARSE") ) enabled_exceptions.dump(); + if( getenv("GCOBOL_SHOW") ) enabled_exceptions.dump(); return true; } %} @@ -238,70 +187,81 @@ apply_cdf_turn( exception_turns_t& turns ) { std::set<size_t> *files; } +%printer { fprintf(yyo, "'%s'", $$? "true" : "false" ); } <boolean> %printer { fprintf(yyo, "'%s'", $$ ); } <string> %printer { fprintf(yyo, "%s '%s'", keyword_str($$.token), $$.string? $$.string : "<nil>" ); } <cdfarg> -%printer { fprintf(yyo, "%ld '%s'", - $$.number, $$.string? $$.string : "" ); } <cdfval> +/* cppcheck-suppress invalidPrintfArgType_sint */ +%printer { fprintf(yyo, HOST_SIZE_T_PRINT_DEC " '%s'", + (fmt_size_t)$$.number, $$.string? $$.string : "" ); } <cdfval> %type <string> NAME NUMSTR LITERAL PSEUDOTEXT %type <string> LSUB RSUB SUBSCRIPT %type <cdfarg> namelit name_any name_one %type <string> name subscript subscripts inof %token <boolean> BOOL -%token <number> FEATURE 363 NUMBER 302 EXCEPTION_NAME 280 "EXCEPTION NAME" +%token <number> FEATURE 367 NUMBER 304 EXCEPTION_NAME 280 "EXCEPTION NAME" %type <cdfval> cdf_expr %type <cdfval> cdf_relexpr cdf_reloper cdf_and cdf_bool_expr %type <cdfval> cdf_factor -%type <boolean> cdf_cond_expr override +%type <boolean> cdf_cond_expr override except_check %type <file> filename %type <files> filenames -%token BY 476 -%token COPY 360 -%token CDF_DISPLAY 382 ">>DISPLAY" -%token IN 595 +%type <number> cdf_stackable + +%token BY 488 +%token COPY 364 +%token CDF_DISPLAY 386 ">>DISPLAY" +%token IN 606 %token NAME 286 -%token NUMSTR 304 "numeric literal" -%token OF 676 -%token PSEUDOTEXT 711 -%token REPLACING 733 -%token LITERAL 297 -%token SUPPRESS 374 - -%token LSUB 365 "(" -%token SUBSCRIPT 373 RSUB 370 ")" - -%token CDF_DEFINE 381 ">>DEFINE" -%token CDF_IF 383 ">>IF" -%token CDF_ELSE 384 ">>ELSE" -%token CDF_END_IF 385 ">>END-IF" -%token CDF_EVALUATE 386 ">>EVALUATE" -%token CDF_WHEN 387 ">>WHEN" -%token CDF_END_EVALUATE 388 ">>END-EVALUATE" - -%token AS 458 CONSTANT 359 DEFINED 361 +%token NUMSTR 306 "numeric literal" +%token OF 687 +%token PSEUDOTEXT 723 +%token REPLACING 745 +%token LITERAL 299 +%token SUPPRESS 378 + +%token LSUB 369 "(" +%token SUBSCRIPT 377 RSUB 374 ")" + +%token CDF_DEFINE 385 ">>DEFINE" +%token CDF_IF 387 ">>IF" +%token CDF_ELSE 388 ">>ELSE" +%token CDF_END_IF 389 ">>END-IF" +%token CDF_EVALUATE 390 ">>EVALUATE" +%token CDF_WHEN 391 ">>WHEN" +%token CDF_END_EVALUATE 392 ">>END-EVALUATE" + +%token ALL 452 +%token CALL_CONVENTION 393 ">>CALL-CONVENTION" +%token COBOL_WORDS 382 ">>COBOL-WORDS" +%token CDF_PUSH 396 ">>PUSH" +%token CDF_POP 397 ">>POP" +%token SOURCE_FORMAT 398 ">>SOURCE FORMAT" + +%token AS 470 CONSTANT 363 DEFINED 365 %type <boolean> DEFINED -%token OTHER 688 PARAMETER_kw 366 "PARAMETER" -%token OFF 677 OVERRIDE 367 -%token THRU 929 -%token TRUE_kw 803 "True" +%token OTHER 699 PARAMETER_kw 370 "PARAMETER" +%token OFF 688 OVERRIDE 371 +%token THRU 950 +%token TRUE_kw 815 "True" -%token CALL_COBOL 389 "CALL" -%token CALL_VERBATIM 390 "CALL (as C)" +%token CALL_COBOL 394 "CALL" +%token CALL_VERBATIM 395 "CALL (as C)" -%token TURN 805 CHECKING 486 LOCATION 639 ON 679 WITH 831 +%token TURN 817 CHECKING 498 LOCATION 650 ON 690 WITH 844 -%left OR 930 -%left AND 931 -%right NOT 932 -%left '<' '>' '=' NE 933 LE 934 GE 935 +%left OR 951 +%left AND 952 +%right NOT 953 +%left '<' '>' EQ 298 "EQUAL" NE 954 LE 955 GE 956 %left '-' '+' %left '*' '/' -%right NEG 937 +%right NEG 958 %define api.prefix {ydf} %define api.token.prefix{YDF_} @@ -319,8 +279,8 @@ top: partials { YYACCEPT; } YYACCEPT; } | copy error { - error_msg(@error, "COPY directive must end in a '.'"); - YYACCEPT; + error_msg(@error, "COPY directive must end in a %<.%>"); + YYABORT; } | completes { YYACCEPT; } ; @@ -333,6 +293,8 @@ complete: cdf_define | cdf_display | cdf_turn | cdf_call_convention + | cdf_push + | cdf_pop ; /* @@ -384,6 +346,7 @@ cdf_define: CDF_DEFINE cdf_constant NAME as cdf_expr[value] override } if( !cdfval_add( $NAME, cdfval_t($value), $override) ) { error_msg(@NAME, "name already in dictionary: %s", $NAME); + cdf_values_t& dictionary( cdf_dictionary() ); const cdfval_t& entry = dictionary[$NAME]; if( entry.filename ) { error_msg(@NAME, "%s previously defined in %s:%d", @@ -393,8 +356,13 @@ cdf_define: CDF_DEFINE cdf_constant NAME as cdf_expr[value] override } YYERROR; } + if( symbols_begin() < symbols_end() ) { + cbl_field_t field = cdf_literalize($NAME, $value); + symbol_field_add(current_program_index(), &field); + } + } - | CDF_DEFINE cdf_constant NAME '=' cdf_expr[value] override + | CDF_DEFINE cdf_constant NAME EQ cdf_expr[value] override { /* accept, but as error */ if( scanner_parsing() ) { error_msg(@NAME, "CDF error: %s = value invalid", $NAME); @@ -413,21 +381,24 @@ cdf_define: CDF_DEFINE cdf_constant NAME as cdf_expr[value] override * available regardless. */ { - if( 0 == dictionary.count($NAME) ) { - yywarn("CDF: '%s' is defined AS PARAMETER " - "but was not defined", $NAME); + if( 0 == cdf_dictionary().count($NAME) ) { + cbl_message(@NAME, CdfParameterW, + "CDF: '%s' is defined AS PARAMETER " + "but was not defined", $NAME); } } | CDF_DEFINE FEATURE as ON { auto feature = cbl_gcobol_feature_t($2); if( ! cobol_gcobol_feature_set(feature, true) ) { - error_msg(@FEATURE, ">>DEFINE %EBCDIC-MODE is invalid within program body"); + error_msg(@FEATURE, + "%<>>DEFINE %%EBCDIC-MODE%> is invalid within program body"); } } | CDF_DEFINE FEATURE as OFF { auto feature = cbl_gcobol_feature_t($2); if( ! cobol_gcobol_feature_set(feature, false) ) { - error_msg(@FEATURE, ">>DEFINE %EBCDIC-MODE is invalid within program body"); + error_msg(@FEATURE, + "%<>>DEFINE %%EBCDIC-MODE%> is invalid within program body"); } } ; @@ -440,8 +411,8 @@ override: %empty { $$ = false; } cdf_turn: TURN except_names except_check { - apply_cdf_turn(exception_turns); - exception_turns.clear(); + apply_cdf_turn(exception_turn); + exception_turn.clear(); } ; @@ -454,28 +425,55 @@ cdf_call_convention: } ; +cdf_push: CDF_PUSH cdf_stackable { + switch( $cdf_stackable ) { + case YDF_ALL: cdf_push(); break; + case YDF_CALL_CONVENTION: cdf_push_call_convention(); break; + case YDF_CDF_DEFINE: cdf_push_dictionary(); break; + case YDF_COBOL_WORDS: cdf_push_current_tokens(); break; + case YDF_SOURCE_FORMAT: cdf_push_source_format(); break; + default: gcc_unreachable(); + } + } + ; +cdf_pop: CDF_POP cdf_stackable { + switch( $cdf_stackable ) { + case YDF_ALL: cdf_pop(); break; + case YDF_CALL_CONVENTION: cdf_pop_call_convention(); break; + case YDF_CDF_DEFINE: cdf_pop_dictionary(); break; + case YDF_COBOL_WORDS: cdf_pop_current_tokens(); break; + case YDF_SOURCE_FORMAT: cdf_pop_source_format(); break; + default: gcc_unreachable(); + } + } + ; + +cdf_stackable: ALL { $$ = YDF_ALL; } + | CALL_CONVENTION { $$ = YDF_CALL_CONVENTION; } + | COBOL_WORDS { $$ = YDF_COBOL_WORDS; } + | CDF_DEFINE { $$ = YDF_CDF_DEFINE; } + | SOURCE_FORMAT { $$ = YDF_SOURCE_FORMAT; } + ; except_names: except_name | except_names except_name ; except_name: EXCEPTION_NAME[ec] { assert($ec != ec_none_e); - exception_turns.add_exception(ec_type_t($ec)); + exception_turn.add_exception(ec_type_t($ec)); } | EXCEPTION_NAME[ec] filenames { assert($ec != ec_none_e); - std::list<size_t> files; - std::copy( $filenames->begin(), $filenames->end(), - std::back_inserter(files) ); - exception_turns.add_exception(ec_type_t($ec), files); + std::list<size_t> files($filenames->begin(), $filenames->end()); + exception_turn.add_exception(ec_type_t($ec), files); } ; -except_check: CHECKING on { exception_turns.enabled = true; } - | CHECKING OFF { exception_turns.enabled = false; } +except_check: CHECKING on { $$ = exception_turn.enable(true); } + | CHECKING OFF { $$ = exception_turn.enable(false); } | CHECKING on with LOCATION { - exception_turns.enabled = exception_turns.location = true; + $$ = exception_turn.enable(true, true); } ; @@ -488,7 +486,7 @@ filenames: filename { auto inserted = $$->insert(symbol_index(symbol_elem_of($2))); if( ! inserted.second ) { error_msg(@2, "%s: No file-name shall be specified more than " - " once for one exception condition", $filename->name); + "once for one exception condition", $filename->name); } } ; @@ -507,7 +505,6 @@ 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; } @@ -526,18 +523,18 @@ cdf_eval_obj: cdf_cond_expr ; cdf_cond_expr: BOOL - | NAME DEFINED[maybe] + | NAME DEFINED { + cdf_values_t& dictionary( cdf_dictionary() ); auto p = dictionary.find($1); bool found = p != dictionary.end(); - if( !$maybe ) found = ! found; - if( ! found ) { - $$ = !$2; - dbgmsg("CDF: %s not found in dictionary (result %s)", + if( !$DEFINED ) found = ! found; + $$ = found; + if( found ) { + dbgmsg("CDF: %s found in dictionary (result %s)", $1, $$? "true" : "false"); } else { - $$ = $2; - dbgmsg("CDF: %s found in dictionary (result %s)", + dbgmsg("CDF: %s not found in dictionary (result %s)", $1, $$? "true" : "false"); } } @@ -567,7 +564,7 @@ cdf_reloper: cdf_relexpr cdf_relexpr: cdf_relexpr '<' cdf_expr { $$ = $1(@1) < $3(@3); } | cdf_relexpr LE cdf_expr { $$ = $1(@1) <= $3(@3); } - | cdf_relexpr '=' cdf_expr { + | cdf_relexpr EQ cdf_expr { $$ = cdfval_t(false); if( ( $1.string && $3.string) || (!$1.string && !$3.string) ) @@ -577,7 +574,7 @@ cdf_relexpr: cdf_relexpr '<' cdf_expr { $$ = $1(@1) < $3(@3); } const char *msg = $1.string? "incommensurate comparison is FALSE: '%s' = %ld" : "incommensurate comparison is FALSE: %ld = '%s'" ; - error_msg(@1, msg); + error_msg(@1, "%s", msg); } } | cdf_relexpr NE cdf_expr @@ -591,7 +588,7 @@ cdf_relexpr: cdf_relexpr '<' cdf_expr { $$ = $1(@1) < $3(@3); } const char *msg = $1.string? "incommensurate comparison is FALSE: '%s' = %ld" : "incommensurate comparison is FALSE: %ld = '%s'" ; - error_msg(@1, msg); + error_msg(@1, "%s", msg); } } | cdf_relexpr GE cdf_expr { $$ = $1(@1) >= $3(@3); } @@ -610,12 +607,14 @@ cdf_expr: cdf_expr '+' cdf_expr { $$ = $1(@1) + $3(@3); } ; cdf_factor: NAME { + cdf_values_t& dictionary( cdf_dictionary() ); auto that = dictionary.find($1); if( that != dictionary.end() ) { $$ = that->second; } else { if( ! scanner_parsing() ) { - yywarn("CDF skipping: no such variable '%s' (ignored)", $1); + cbl_message(CdfNotFoundW, + "CDF skipping: no such variable '%s'", $1); } else { error_msg(@NAME, "CDF error: no such variable '%s'", $1); } @@ -627,7 +626,7 @@ cdf_factor: NAME { | NUMSTR { auto value = integer_literal($NUMSTR); if( !value.second ) { - error_msg(@1, "CDF error: parsed %s as %ld", + error_msg(@1, "CDF error: parsed %qs as %lld", $NUMSTR, value.first); YYERROR; } @@ -645,7 +644,7 @@ copybook_name: COPY name_one[src] if( -1 == copybook.open(@src, $src.string) ) { error_msg(@src, "could not open copybook file " "for '%s'", $src.string); - YYERROR; + YYABORT; } } | COPY name_one[src] IN name_one[lib] @@ -653,8 +652,8 @@ copybook_name: COPY name_one[src] copybook.library(@lib, $lib.string); if( -1 == copybook.open(@src, $src.string) ) { error_msg(@src, "could not open copybook file " - "for '%s' in '%'s'", $src.string, $lib.string); - YYERROR; + "for %<%s%> in %<%s%>", $src.string, $lib.string); + YYABORT; } } ; @@ -709,6 +708,7 @@ name_any: namelit name_one: NAME { + cdf_values_t& dictionary( cdf_dictionary() ); cdf_arg_t arg = { YDF_NAME, $1 }; auto p = dictionary.find($1); @@ -723,6 +723,7 @@ name_one: NAME namelit: name { + cdf_values_t& dictionary( cdf_dictionary() ); cdf_arg_t arg = { YDF_NAME, $1 }; auto p = dictionary.find($1); @@ -803,6 +804,7 @@ location_set( const YYLTYPE& loc ) { bool // used by cobol1.cc defined_cmd( const char arg[] ) { + cdf_values_t& dictionary( cdf_dictionary() ); cdfval_t value(1); char *name = xstrdup(arg); @@ -825,7 +827,8 @@ defined_cmd( const char arg[] ) if( yydebug ) { if( cdf_name->second.is_numeric() ) { - dbgmsg("%s: added -D %s = %ld", __func__, name, cdf_name->second.as_number()); + dbgmsg("%s: added -D %s = " HOST_SIZE_T_PRINT_DEC, + __func__, name, (fmt_size_t)cdf_name->second.as_number()); } else { dbgmsg("%s: added -D %s = \"%s\"", __func__, name, cdf_name->second.string); } @@ -924,7 +927,8 @@ static int ydflex(void) { } bool -cdf_value( const char name[], cdfval_t value ) { +cdf_value( const char name[], const cdfval_t& value ) { + cdf_values_t& dictionary( cdf_dictionary() ); auto p = dictionary.find(name); if( p != dictionary.end() ) return false; @@ -935,6 +939,7 @@ cdf_value( const char name[], cdfval_t value ) { const cdfval_t * cdf_value( const char name[] ) { + cdf_values_t& dictionary( cdf_dictionary() ); auto p = dictionary.find(name); if( p == dictionary.end() ) return NULL; @@ -951,8 +956,11 @@ verify_integer( const YDFLTYPE& loc, const cdfval_base_t& val ) { return true; } -cdfval_base_t& +const cdfval_base_t& cdfval_base_t::operator()( const YDFLTYPE& loc ) { static cdfval_t zero(0); + // cppcheck-suppress returnTempReference return verify_integer(loc, *this) ? *this : zero; } + + diff --git a/gcc/cobol/cdfval.h b/gcc/cobol/cdfval.h index 1453f2a..cc474a2 100644 --- a/gcc/cobol/cdfval.h +++ b/gcc/cobol/cdfval.h @@ -32,18 +32,26 @@ #ifndef _CDF_VAL_H_ #define _CDF_VAL_H_ -#include <assert.h> -#include <stdint.h> -#include <stdlib.h> +#include <cassert> +#include <cstdint> +#include <cstdlib> bool scanner_parsing(); +/* cdfval_base_t has no constructor because otherwise: + * cobol/cdf.h:172:7: note: ‘YDFSTYPE::YDFSTYPE()’ is implicitly deleted + * because the default definition would be ill-formed: + * 172 | union YDFSTYPE + * + * We use the derived type cdfval_t, which can be properly constructed and + * operated on, but tell Bison only about its POD base class. + */ struct YDFLTYPE; struct cdfval_base_t { bool off; const char *string; int64_t number; - cdfval_base_t& operator()( const YDFLTYPE& loc ); + const cdfval_base_t& operator()( const YDFLTYPE& loc ); }; struct cdf_arg_t { @@ -65,35 +73,43 @@ struct cdfval_t : public cdfval_base_t { cdfval_base_t::string = NULL; cdfval_base_t::number = 0; } - cdfval_t( const char value[] ) + cdfval_t( const char value[] ) // cppcheck-suppress noExplicitConstructor : 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 ) // cppcheck-suppress noExplicitConstructor + : lineno(yylineno), filename(cobol_filename()) + { + cdfval_base_t::off = false; + cdfval_base_t::string = NULL; + cdfval_base_t::number = value; + } + cdfval_t( long value ) // cppcheck-suppress noExplicitConstructor : 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( int value ) // cppcheck-suppress noExplicitConstructor : lineno(yylineno), filename(cobol_filename()) { cdfval_base_t::off = false; cdfval_base_t::string = NULL; cdfval_base_t::number = value; } - cdfval_t( int value ) + explicit cdfval_t( const REAL_VALUE_TYPE& r ) : lineno(yylineno), filename(cobol_filename()) { cdfval_base_t::off = false; cdfval_base_t::string = NULL; + HOST_WIDE_INT value = real_to_integer(&r); cdfval_base_t::number = value; } - cdfval_t( const cdfval_base_t& value ) + cdfval_t( const cdfval_base_t& value ) // cppcheck-suppress noExplicitConstructor : lineno(yylineno), filename(cobol_filename()) { cdfval_base_t *self(this); @@ -104,10 +120,14 @@ struct cdfval_t : public cdfval_base_t { int64_t as_number() const { assert(is_numeric()); return number; } }; -bool -cdf_value( const char name[], cdfval_t value ); - const cdfval_t * cdf_value( const char name[] ); +bool +cdf_value( const char name[], const cdfval_t& value ); + +typedef std::map<std::string, cdfval_t> cdf_values_t; + +cdf_values_t& cdf_dictionary(); + #endif diff --git a/gcc/cobol/cobol-system.h b/gcc/cobol/cobol-system.h index 81529bd..828f4f5 100644 --- a/gcc/cobol/cobol-system.h +++ b/gcc/cobol/cobol-system.h @@ -53,7 +53,6 @@ #include <deque> #include <numeric> #include <limits> -#include <cmath> #include <unordered_map> #include <unordered_set> @@ -61,4 +60,10 @@ // The following "local" #include is part of the GCC core code #include "system.h" +#if (CHECKING_P && GCC_VERSION >= 4001) || GCC_VERSION == BUILDING_GCC_VERSION +#define ATTRIBUTE_GCOBOL_DIAG(m, n) __attribute__ ((__format__ (__gcc_tdiag__, m, n))) ATTRIBUTE_NONNULL(m) +#else +#define ATTRIBUTE_GCOBOL_DIAG(m, n) ATTRIBUTE_NONNULL(m) +#endif + #endif diff --git a/gcc/cobol/cobol1.cc b/gcc/cobol/cobol1.cc index 08af367..5f1260e 100644 --- a/gcc/cobol/cobol1.cc +++ b/gcc/cobol/cobol1.cc @@ -20,16 +20,15 @@ along with GCC; see the file COPYING3. If not see #include "cobol-system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic.h" -#include "opts.h" -#include "debug.h" -#include "langhooks.h" -#include "langhooks-def.h" -#include "target.h" -#include "stringpool.h" -#define HOWEVER_GCC_DEFINES_TREE 1 +#include <coretypes.h> +#include <tree.h> +#include <diagnostic.h> +#include <opts.h> +#include <debug.h> +#include <langhooks.h> +#include <langhooks-def.h> +#include <target.h> +#include <stringpool.h> #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -40,7 +39,6 @@ along with GCC; see the file COPYING3. If not see #include "genapi.h" #include "../../libgcobol/exceptl.h" #include "exceptg.h" -#include "util.h" #include "gengen.h" // This has some GTY(()) markers #include "structs.h" // This has some GTY(()) markers @@ -167,8 +165,8 @@ create_our_type_nodes_init() long_double_ten_node = build_real_from_int_cst( LONGDOUBLE, build_int_cst_type(INT,10)); - sizeof_size_t = build_int_cst_type(SIZE_T, sizeof(size_t)); - sizeof_pointer = build_int_cst_type(SIZE_T, sizeof(void *)); + sizeof_size_t = build_int_cst_type(SIZE_T, int_size_in_bytes(SIZE_T)); + sizeof_pointer = build_int_cst_type(SIZE_T, int_size_in_bytes(VOID_P)); bool_true_node = build2(EQ_EXPR, integer_type_node, @@ -295,7 +293,7 @@ cobol_langhook_init_options_struct (struct gcc_options *opts) { cobol_set_debugging( false, false, false ); - copybook_directory_add( getenv("GCOB_COPYBOOK") ); + copybook_directory_add( getenv("GCOBOL_COPYBOOK") ); } static unsigned int @@ -319,7 +317,7 @@ enable_exceptions( bool enable ) { NULL != (name = strtok(name, ",")); name = NULL ) { ec_type_t type = ec_type_of(name); if( type == ec_none_e ) { - yywarn("unrecognized exception '%s' was ignored", name); + cbl_message(EcUnknownW, "unrecognized exception '%s'", name); continue; } ec_disposition_t disposition = ec_type_disposition(type); @@ -330,17 +328,21 @@ enable_exceptions( bool enable ) { } } +void cobol_warning( cbl_diag_id_t id, int yn, bool ); + static bool cobol_langhook_handle_option (size_t scode, - const char *arg ATTRIBUTE_UNUSED, + const char *arg, HOST_WIDE_INT value, - int kind ATTRIBUTE_UNUSED, + int kind, location_t loc ATTRIBUTE_UNUSED, const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED) { // process_command (decoded_options_count, decoded_options); enum opt_code code = (enum opt_code) scode; + auto super_kind = diagnostics::kind(kind); + bool warning_as_error = super_kind == diagnostics::kind::error; switch(code) { @@ -358,24 +360,31 @@ cobol_langhook_handle_option (size_t scode, copybook_extension_add(cobol_copyext); return true; + case OPT_M: + cobol_set_pp_option('M'); + return true; + case OPT_fstatic_call: use_static_call( arg? true : false ); return true; case OPT_fdefaultbyte: + // cobol_default_byte is an unsigned ing wsclear(cobol_default_byte); return true; - case OPT_fflex_debug: + case OPT_fflex_debug: // cppcheck-suppress syntaxError // The need for this is a mystery yy_flex_debug = 1; cobol_set_debugging( true, yy_debug == 1, cobol_trace_debug == 1 ); return true; + case OPT_fyacc_debug: yy_debug = 1; cobol_set_debugging(yy_flex_debug == 1, true, cobol_trace_debug == 1 ); return true; + case OPT_ftrace_debug: cobol_set_debugging( yy_flex_debug == 1, yy_debug == 1, true ); return true; @@ -386,10 +395,6 @@ cobol_langhook_handle_option (size_t scode, return true; } - case OPT_fmax_errors: - flag_max_errors = atoi(arg); - return true; - case OPT_ffixed_form: cobol_set_indicator_column(-7); return true; @@ -402,20 +407,24 @@ cobol_langhook_handle_option (size_t scode, return true; case OPT_dialect: + // gcc disallows 0 as an enumerated value, so we used 0x10 for iso. + if( cobol_dialect == 0x100 ) cobol_dialect = 0; cobol_dialect_set(cbl_dialect_t(cobol_dialect)); return true; case OPT_fsyntax_only: mode_syntax_only(identification_div_e); break; + case OPT_preprocess: if( ! preprocess_filter_add(arg) ) { cbl_errx( "could not execute preprocessor %s", arg); } return true; + case OPT_include: - if( ! include_file_add(cobol_include) ) { - cbl_errx( "could not include %s", cobol_include); + if( ! include_file_add(arg) ) { + cbl_errx( "could not include %s", arg); } return true; @@ -436,6 +445,194 @@ cobol_langhook_handle_option (size_t scode, cobol_gcobol_feature_set(feature_internal_ebcdic_e); return true; + // Warnings and errors + + case OPT_Wbinary_long_long: + cobol_warning(MfBinaryLongLong, binary_long_long, warning_as_error); + return true; + + case OPT_Wcall_giving: + cobol_warning(MfCallGiving, call_giving, warning_as_error); + return true; + + case OPT_Wcdf_dollar: + cobol_warning(MfCdfDollar, cdf_dollar, warning_as_error); + return true; + + case OPT_Wcomp_6: + cobol_warning(MfComp6, comp_6, warning_as_error); + return true; + + case OPT_Wcomp_x: + cobol_warning(MfCompX, comp_x, warning_as_error); + return true; + + case OPT_Winspect_trailing: + cobol_warning(MfTrailing, inspect_trailing, warning_as_error); + return true; + + case OPT_Wlevel_1_occurs: + cobol_warning(MfLevel_1_Occurs, level_1_occurs, warning_as_error); + return true; + + case OPT_Wlevel_78_defined: + cobol_warning(Par78CdfDefinedW, level_78_defined, warning_as_error); + return true; + + case OPT_Wmove_pointer: + cobol_warning(MfMovePointer, move_pointer, warning_as_error); + return true; + + case OPT_Wlevel_78: + cobol_warning(MfLevel78, level_78, warning_as_error); + return true; + + case OPT_Wreturning_number: + cobol_warning(MfReturningNum, returning_number, warning_as_error); + return true; + + case OPT_Wusage_typename: + cobol_warning(MfUsageTypename, usage_typename, warning_as_error); + return true; + + case OPT_Wbad_line_directive: + cobol_warning(LexLineE, bad_line_directive, warning_as_error); + return true; + + case OPT_Wequal_assign: + cobol_warning(IbmEqualAssignE, equal_assign, warning_as_error); + return true; + + case OPT_Wbad_numeric: + cobol_warning(ParNumstrW, bad_numeric, warning_as_error); + return true; + + case OPT_Wcdf_invalid_parameter: + cobol_warning(CdfParameterW, cdf_invalid_parameter, warning_as_error); + return true; + + case OPT_Wcdf_name_not_found: + cobol_warning(CdfNotFoundW, cdf_name_not_found, warning_as_error); + return true; + + case OPT_Wcopybook_found: + cobol_warning(LexInputN, copybook_found, warning_as_error); + return true; + + case OPT_Wec_unknown: + cobol_warning(EcUnknownW, ec_unknown, warning_as_error); + return true; + + case OPT_Wentry_convention: + cobol_warning(ParInfoI, entry_convention, warning_as_error); + return true; + + case OPT_Wiconv_error: + cobol_warning(ParIconvE, iconv_error, warning_as_error); + return true; + + case OPT_Winclude_file_found: + cobol_warning(LexIncludeOkN, include_file_found, warning_as_error); + return true; + + case OPT_Winclude_file_not_found: + cobol_warning(LexIncludeE, include_file_not_found, warning_as_error); + return true; + + case OPT_Wliteral_concat: + cobol_warning(ParLiteral2W, literal_concat, warning_as_error); + return true; + + case OPT_Wlocale_error: + cobol_warning(ParLocaleW, locale_error, warning_as_error); + return true; + + case OPT_Wmove_corresponding: + cobol_warning(ParNoCorrespondingW, warn_corresponding, warning_as_error); + return true; + + case OPT_Wnllanginfo_error: + cobol_warning(ParLangInfoW, nllanginfo_error, warning_as_error); + return true; + + case OPT_Wlength_of: + cobol_warning(IbmLengthOf, cobol_length_of, warning_as_error); + return true; + + case OPT_Wpreprocessor_error: + cobol_warning(ParLangInfoW, preprocessor_error, warning_as_error); + return true; + + case OPT_Wprocedure_pointer: + cobol_warning(IbmProcedurePointer, procedure_pointer, warning_as_error); + return true; + + case OPT_Wprocedure_not_found: + cobol_warning(ParUnresolvedProcE, procedure_not_found, warning_as_error); + return true; + + case OPT_Wreplace_error: + cobol_warning(LexReplaceE, replace_error, warning_as_error); + return true; + + case OPT_Wsegment_error: + cobol_warning(IbmSectionRangeE, segment_error, warning_as_error); + return true; + + case OPT_Wsegment_negative: + cobol_warning(IbmSectionNegE, segment_negative, warning_as_error); + return true; + + case OPT_Wsegment: + cobol_warning(IbmSectionSegmentW, cobol_segment, warning_as_error); + return true; + + case OPT_Wcobol_eject: + cobol_warning(IbmEjectE, cobol_eject, warning_as_error); + return true; + + case OPT_Woperator_space: + cobol_warning(LexSeparatorE, operator_space, warning_as_error); + return true; + + case OPT_Wstop_number: + cobol_warning(IbmStopNumber, stop_number, warning_as_error); + return true; + + case OPT_Wstray_indicator: + cobol_warning(LexIndicatorE, stray_indicator, warning_as_error); + return true; + + case OPT_Wcobol_volatile: + // If arg is true, the error becoomes a warning + cobol_warning(IbmVolatileE, cobol_volatile, warning_as_error); + cobol_warning(IbmVolatileW, cobol_volatile, warning_as_error); + return true; + + case OPT_Wcobol_resume: + cobol_warning(IsoResume, cobol_resume, warning_as_error); + return true; + + case OPT_Wapply_commit: + cobol_warning(SynApplyCommit, apply_commit, warning_as_error); + return true; + + case OPT_Whigh_order_bit: + cobol_warning(SynHighOrderBit, high_order_bit, warning_as_error); + return true; + + case OPT_Wfile_code_set: + cobol_warning(SynFileCodeSet, file_code_set, warning_as_error); + return true; + + case OPT_Wset_locale_to: + cobol_warning(SynSetLocaleTo, set_locale_to, warning_as_error); + return true; + + case OPT_Wset_to_locale: + cobol_warning(SynSetToLocale, set_to_locale, warning_as_error); + return true; + default: break; } @@ -511,14 +708,6 @@ cobol_langhook_type_for_mode (enum machine_mode mode, int unsignedp) return NULL; } -////static tree -////cobol_langhook_type_for_size (unsigned int bits ATTRIBUTE_UNUSED, -//// int unsignedp ATTRIBUTE_UNUSED) -//// { -//// gcc_unreachable (); -//// return NULL; -//// } - /* Record a builtin function. We just ignore builtin functions. */ static tree @@ -573,7 +762,7 @@ cobol_name_mangler(const char *cobol_name_) } // Allocate enough space for a prepended underscore and a final '\0' - char *cobol_name = (char *)xmalloc(strlen(cobol_name_)+2); + char *cobol_name = static_cast<char *>(xmalloc(strlen(cobol_name_)+2)); size_t n = 0; if( cobol_name_[0] >= '0' && cobol_name_[0] <= '9' ) { diff --git a/gcc/cobol/copybook.h b/gcc/cobol/copybook.h index e509bf3..ff8c6d0 100644 --- a/gcc/cobol/copybook.h +++ b/gcc/cobol/copybook.h @@ -62,10 +62,10 @@ class copybook_elem_t { struct copybook_loc_t { YYLTYPE loc; const char *name; - copybook_loc_t() : name(NULL) {} + copybook_loc_t() : loc(), name(NULL) {} } source, library; bool suppress; - static const char *extensions; + static std::list<const char *> suffixes; public: struct { bool source, library; } literally; int fd; @@ -74,12 +74,11 @@ class copybook_elem_t { copybook_elem_t() : suppress(false) + , literally() , fd(-1) , nsubexpr(0) , regex_text(NULL) - { - literally = {}; - } + {} void clear() { suppress = false; @@ -91,7 +90,6 @@ class copybook_elem_t { } int open_file( const char dir[], bool literally = false ); - void extensions_add( const char ext[], const char alt[] ); static inline bool is_quote( const char ch ) { return ch == '\'' || ch == '"'; @@ -102,7 +100,7 @@ class copybook_elem_t { } static char * dequote( const char orig[] ) { gcc_assert(quoted(orig)); - auto name = (char*)xcalloc(1, strlen(orig)); + auto name = static_cast<char*>(xcalloc(1, strlen(orig))); gcc_assert(name); char *tgt = name; @@ -131,7 +129,7 @@ private: class uppername_t { std::string upper; public: - uppername_t( const std::string input ) : upper(input) { + explicit uppername_t( const std::string& input ) : upper(input) { std::transform(input.begin(), input.end(), upper.begin(), []( char ch ) { return TOUPPER(ch); } ); } @@ -185,12 +183,10 @@ class copybook_t { this->source(loc, name); for( auto dir : directories ) { - if( true ) { - dbgmsg("copybook_t::open '%s' OF '%s' %s", - book.source.name, - dir? dir: ".", - book.literally.source? ", literally" : "" ); - } + dbgmsg("copybook_t::open '%s' OF '%s' %s", + book.source.name, + dir? dir: ".", + book.literally.source? ", literally" : "" ); if( (fd = book.open_file(dir, book.literally.source)) != -1 ) break; } return fd; diff --git a/gcc/cobol/dts.h b/gcc/cobol/dts.h index c345dc7..c900c45 100644 --- a/gcc/cobol/dts.h +++ b/gcc/cobol/dts.h @@ -33,14 +33,15 @@ namespace dts { : input(input) , first(NULL), second(NULL), matched(false) { - static regmatch_t empty = { -1, -1 }; - regmatch_t& self(*this); + static regmatch_t empty; + empty.rm_so = empty.rm_eo = -1; + regmatch_t& self(*this); // cppcheck-suppress constVariableReference self = empty; } csub_match( const char input[], const regmatch_t& m ) : input(input) { - regmatch_t& self(*this); + regmatch_t& self(*this); // cppcheck-suppress constVariableReference self = m; matched = rm_so != -1; first = rm_so == -1? NULL : input + rm_so; @@ -67,7 +68,6 @@ namespace dts { #if __cpp_exceptions throw std::logic_error(msg); #else - pattern = NULL; cbl_errx("%s", msg); #endif } @@ -77,7 +77,7 @@ namespace dts { size_t size() const { return nsubexpr; } bool ready() const { return pattern != NULL; } private: - regex( const regex& ) {} + regex( const regex& ) = default; }; inline bool regex_search( const char input[], const char *eoinput, @@ -86,24 +86,30 @@ namespace dts { #if __cpp_exceptions static const char msg[] = "input not NUL-terminated"; throw std::domain_error( msg ); -#else - eoinput = strchr(input, '\0'); #endif } - if( eoinput == NULL ) eoinput = strchr(input, '\0'); auto ncm = re.size(); cm.resize(ncm); std::vector <regmatch_t> cms(ncm); - int erc = regexec( &re, input, ncm, cms.data(), 0 ); if( erc != 0 ) return false; +#if __cpp_exceptions + // This is not correct at all, but current use depends on current behavior. + // The following line is excluded from the GCC build, which is compiled + // without __cpp_exceptions. parse_copy_directive (for one) depends on + // regex_search returning true even if the match is beyond eoinput. + if( eoinput < cm[0].second ) return false; + // Correct behavior would return match only between input and eoinput. + // Because regex(3) uses a NUL terminator, it may match text between + // eoinput and the NUL. +#endif std::transform( cms.begin(), cms.end(), cm.begin(), [input]( const regmatch_t& m ) { return csub_match( input, m ); } ); return true; } -}; +} diff --git a/gcc/cobol/except.cc b/gcc/cobol/except.cc index 5374201..31112d1 100644 --- a/gcc/cobol/except.cc +++ b/gcc/cobol/except.cc @@ -32,7 +32,6 @@ #include "cobol-system.h" #include "coretypes.h" #include "tree.h" -#define HOWEVER_GCC_DEFINES_TREE 1 #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -44,6 +43,7 @@ #include "gengen.h" #include "../../libgcobol/exceptl.h" #include "util.h" +#include "genutil.h" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" @@ -51,7 +51,7 @@ static const ec_descr_t * ec_type_descr( ec_type_t type ) { auto p = std::find( __gg__exception_table, __gg__exception_table_end, type ); if( p == __gg__exception_table_end ) { - cbl_internal_error("no such exception: 0x%04x", type); + cbl_internal_error("no such exception: 0x%x", type); } return p; } @@ -75,107 +75,130 @@ ec_level( ec_type_t ec ) { return 3; } -cbl_enabled_exceptions_t enabled_exceptions; - void cbl_enabled_exceptions_t::dump() const { + extern int yydebug; + int debug = 1; + std::swap(debug, yydebug); // dbgmsg needs yydebug + if( empty() ) { - cbl_message(2, "cbl_enabled_exceptions_t: no exceptions" ); + dbgmsg("cbl_enabled_exceptions_t: no exceptions" ); + std::swap(debug, yydebug); return; } int i = 1; - for( auto& elem : *this ) { - cbl_message(2, "cbl_enabled_exceptions_t: %2d {%s, %s, %s, %zu}", + for( auto& elem : *this ) { // cppcheck-suppress constVariableReference + dbgmsg("cbl_enabled_exceptions_t: %2d {%s, %s, %lu}", i++, - elem.enabled? " enabled" : "disabled", - elem.location? "location" : " none", + elem.location? "with location" : " no location", ec_type_str(elem.ec), - elem.file ); + gb4(elem.file) ); } + std::swap(debug, yydebug); } +// cppcheck-suppress-begin [useStlAlgorithm] because why? +uint32_t +cbl_enabled_exceptions_t::status() const { + uint32_t status_word = 0; + for( const auto& ena : *this ) { + status_word |= (EC_ALL_E & ena.ec ); + } + return status_word; +} +// cppcheck-suppress-end useStlAlgorithm + +std::vector<uint64_t> +cbl_enabled_exceptions_t::encode() const { + std::vector<uint64_t> encoded; + auto p = std::back_inserter(encoded); + for( const auto& ec : *this ) { + *p++ = ec.location; + *p++ = ec.ec; + *p++ = ec.file; + } + return encoded; +} -bool +void cbl_enabled_exceptions_t::turn_on_off( bool enabled, bool location, ec_type_t type, - std::set<size_t> files ) + const std::set<size_t>& files ) { - // A Level 3 EC is added unilaterally; it can't knock out a lower level. + // Update current enabled ECs tree on leaving this function. + class update_parser_t { + const cbl_enabled_exceptions_t& ecs; + public: + explicit update_parser_t(const cbl_enabled_exceptions_t& ecs) : ecs(ecs) {} + ~update_parser_t() { + tree ena = parser_compile_ecs(ecs.encode()); + current_enabled_ecs(ena); + } + } update_parser(*this); + + // A Level 3 EC is added unilaterally; it can't affect a higher level. if( ec_level(type) == 3 ) { if( files.empty() ) { - auto elem = cbl_enabled_exception_t(enabled, location, type); - apply(elem); - return true; + auto elem = cbl_enabled_exception_t(location, type); + apply(enabled, elem); + return; } for( size_t file : files ) { - auto elem = cbl_enabled_exception_t(enabled, location, type, file); - apply(elem); + auto elem = cbl_enabled_exception_t(location, type, file); + apply(enabled, elem); } - return true; + return; } - /* - * std::remove_if cannot be used with std::set because its elements are const. - * std::set::erase_if became available only in C++20. - */ - if( enabled ) { // remove any disabled + // A new Level 1 or Level 2 EC is likewise simply added. + if( enabled ) { if( files.empty() ) { - auto p = begin(); - while( end() != (p = std::find_if( begin(), end(), - [ec = type]( const auto& elem ) { - return - !elem.enabled && - ec_cmp(ec, elem.ec); } )) ) { - erase(p); - } - } else { - for( size_t file: files ) { - auto p = begin(); - while( end() != (p = std::find_if( begin(), end(), - [ec = type, file]( const auto& elem ) { - return - !elem.enabled && - file == elem.file && - ec_cmp(ec, elem.ec); } )) ) { - erase(p); - } - } + auto elem = cbl_enabled_exception_t(location, type); + apply(enabled, elem); + return; } - auto elem = cbl_enabled_exception_t(enabled, location, type); - apply(elem); - return true; + for( size_t file: files ) { + auto elem = cbl_enabled_exception_t(location, type, file); + apply(enabled, elem); + } + return; } + assert(!enabled); assert(ec_level(type) < 3); + /* + * >> TURN EC [files] CHECKING OFF + */ + if( files.empty() ) { + // A Level 1 EC with no files disables all ECs if( type == ec_all_e ) { clear(); - return true; + return; } - // Remove any matching Level-2 or Level-3 ECs, regardless of their files. + // Because TURN CHECKING OFF mentioned no files, Remove any matching + // Level-2 or Level-3 ECs, regardless of their files. auto p = begin(); while( end() != (p = std::find_if( begin(), end(), [ec = type]( const auto& elem ) { return - elem.enabled && elem.ec != ec_all_e && ec_cmp(ec, elem.ec); } )) ) { erase(p); } - // Keep the EC as an exception if a higher-level would othewise apply. + // Keep the EC as an override if a higher-level would othewise apply. p = std::find_if( begin(), end(), [ec = type]( const auto& elem ) { return - elem.enabled && (elem.ec == ec_all_e || elem.ec < ec) && elem.file == 0 && ec_cmp(ec, elem.ec); } ); if( p != end() ) { - auto elem = cbl_enabled_exception_t(enabled, location, type); - apply(elem); + auto elem = cbl_enabled_exception_t(location, type); + apply(enabled, elem); } } else { // Remove any matching or lower-level EC for the same file. @@ -184,47 +207,34 @@ cbl_enabled_exceptions_t::turn_on_off( bool enabled, while( end() != (p = std::find_if( begin(), end(), [ec = type, file]( const auto& elem ) { return - elem.enabled && // ec is higher level and matches (ec == ec_all_e || ec <= elem.ec) && file == elem.file && ec_cmp(ec, elem.ec); } )) ) { erase(p); } - // Keep the EC as an exception if a higher-level would othewise apply. + // Keep the EC as an override if a higher-level would othewise apply. p = std::find_if( begin(), end(), [ec = type, file]( const auto& elem ) { return - elem.enabled && (elem.ec == ec_all_e || elem.ec < ec) && file == elem.file && ec_cmp(ec, elem.ec); } ); if( p != end() ) { - auto elem = cbl_enabled_exception_t(enabled, location, type, file); - apply(elem); + auto elem = cbl_enabled_exception_t(location, type, file); + apply(enabled, elem); } } } - - return true; + return; } const cbl_enabled_exception_t * -cbl_enabled_exceptions_t::match( ec_type_t type, size_t file ) { +cbl_enabled_exceptions_t::match( ec_type_t type, size_t file ) const { auto output = enabled_exception_match( begin(), end(), type, file ); return output != end()? &*output : NULL; } -class choose_declarative { - size_t program; - public: - choose_declarative( size_t program ) : program(program) {} - - bool operator()( const cbl_declarative_t& dcl ) { - return dcl.global || program == symbol_at(dcl.section)->program; - } -}; - bool sort_supers_last( const cbl_declarative_t& a, const cbl_declarative_t& b ) { if( symbol_at(a.section)->program == symbol_at(b.section)->program ) { @@ -234,66 +244,6 @@ sort_supers_last( const cbl_declarative_t& a, const cbl_declarative_t& b ) { } cbl_field_t * new_temporary_decl(); - -/* - * For a program, create a "DECLARATIVES" entry in the symbol table, - * representing eligible declarative sections in priorty order: - * in-program first, followed by any global declaratives in parent - * programs. These decribe the USE criteria declared for each - * declarative section. - * - * The field's initial value is actually an array of - * cbl_declarartive_t, in which the first element is unused, except - * that array[0].section represents the number of elements, starting - * at array[1]. - * - * The returned value is the declarative's symbol index. It is passed - * to match_exception, which scans it for a declarative whose criteria - * match the raised exception. That function returns the - * cbl_declarative_t::section, which the program then uses to PERFORM - * that section. - */ -size_t -symbol_declaratives_add( size_t program, - const std::list<cbl_declarative_t>& dcls ) -{ - auto n = dcls.size(); - if( n == 0 ) return 0; - - auto blob = new cbl_declarative_t[ 1 + n ]; - - auto pend = std::copy_if( dcls.begin(), dcls.end(), blob + 1, - choose_declarative(program) ); - - std::sort( blob + 1, pend, sort_supers_last ); - - // Overload blob[0].section to be the count. - blob[0].section = (pend - blob) - 1; - - size_t len = reinterpret_cast<char*>(pend) - - reinterpret_cast<char*>(blob); - assert(len == (blob[0].section + 1) * sizeof(blob[0])); - - // Construct a "blob" in the symbol table. - static int blob_count = 1; - char achBlob[32]; - sprintf(achBlob, "_DECLARATIVE_BLOB%d_", blob_count++); - - cbl_field_data_t data = {}; - data.memsize = capacity_cast(len); - data.capacity = capacity_cast(len); - data.initial = reinterpret_cast<char*>(blob); - data.picture = reinterpret_cast<char*>(blob); - cbl_field_t field = { 0, FldBlob, FldInvalid, constant_e, - 0, 0, 0, cbl_occurs_t(), 0, "", - 0, {}, data, NULL }; - strcpy(field.name, achBlob); - - auto e = symbol_field_add(program, &field); - parser_symbol_add(cbl_field_of(e)); - return symbol_index(e); -} - /* * Generate the code to evaluate declaratives. This is the "secret * section" right after END DECLARATIVES. Its name is @@ -316,48 +266,60 @@ size_t current_file_index(); file_status_t current_file_handled_status(); void -declarative_runtime_match( cbl_field_t *declaratives, cbl_label_t *lave ) { - if( getenv("SHOW_PARSE") ) +declarative_runtime_match( const std::list<cbl_declarative_t>& declaratives, + cbl_label_t *lave ) +{ + if( getenv("GCOBOL_SHOW") ) { fprintf(stderr, "( %d ) %s: \n", cobol_location().first_line, __func__); } - if( getenv("TRACE1") ) + if( getenv("GCOBOL_TRACE") ) { - gg_printf(">>>>>>( %d )(%s) declaratives:%s lave:%s\n", + gg_printf(">>>>>>( %d )(%s) declaratives: lave:%s\n", build_int_cst_type(INT, cobol_location().first_line), gg_string_literal(__func__), - gg_string_literal(declaratives->name), gg_string_literal(lave->name), NULL_TREE); } static auto yes = new_temporary(FldConditional); - static auto psection = new_temporary(FldNumericBin5); + static auto isection = new_temporary(FldNumericBin5); + static auto index = new_temporary(FldNumericBin5); - // Send blob, get declarative section index. - auto index = new_temporary(FldNumericBin5); - parser_match_exception(index, declaratives); - - auto p = declaratives->data.initial; - const auto dcls = reinterpret_cast<const cbl_declarative_t *>(p); - size_t ndcl = dcls[0].section; // overloaded - - // Compare returned index to each section index. - for( auto p = dcls + 1; p < dcls + 1 + ndcl; p++ ) { - parser_set_numeric( psection, p->section ); - parser_relop( yes, index, eq_op, psection ); - parser_if( yes ); - auto section = cbl_label_of(symbol_at(p->section)); - parser_perform(section); - parser_label_goto(lave); - parser_else(); - parser_fi(); + /* + * Generate a sequence of COBOL IF statements to match the Declarative's + * symbol table index to its performable section. The entire sequence is + * guarded by a runtime IF that evaluates to TRUE only if the "current EC" is + * nonzero. This way, when _DECLARATIVES_EVAL is performed, it does nothing + * if no EC was raised. + */ + IF( var_decl_exception_code, ne_op, integer_zero_node ) { + // Get declarative section index matching any raised EC. + parser_match_exception(index); + + // Compare returned index to each section index. + for( const auto& dcl : declaratives ) { + parser_set_numeric( isection, dcl.section ); + parser_relop( yes, index, eq_op, isection ); + parser_if( yes ); + auto section = cbl_label_of(symbol_at(dcl.section)); + parser_push_exception(); + parser_perform(section); + parser_pop_exception(); + parser_label_goto(lave); + parser_else(); + parser_fi(); + } } - - parser_label_label(lave); - - // A performed declarative may clear the raised exception with RESUME. - // If not cleared and fatal, the default handler will exit. - parser_check_fatal_exception(); + ELSE { + if( getenv("GCOBOL_TRACE") ) + { + gg_printf(">>>>>>( %d )(%s) __gg__exception_code is zero\n", + build_int_cst_type(INT, cobol_location().first_line), + gg_string_literal(__func__), + NULL_TREE); + } + } + ENDIF } ec_type_t diff --git a/gcc/cobol/exceptg.h b/gcc/cobol/exceptg.h index 4500c0f..6869f22 100644 --- a/gcc/cobol/exceptg.h +++ b/gcc/cobol/exceptg.h @@ -36,26 +36,67 @@ extern const char * ec_type_str( ec_type_t type ); extern ec_disposition_t ec_type_disposition( ec_type_t type ); -extern void declarative_runtime_match(cbl_field_t *declaratives, - cbl_label_t *lave ); +extern void declarative_runtime_match( const std::list<cbl_declarative_t>& declaratives, + cbl_label_t *lave ); static inline ec_disposition_t ec_implemented( ec_disposition_t disposition ) { return ec_disposition_t( size_t(disposition) & ~0x80 ); } - // >>TURN arguments -struct cbl_exception_files_t { - ec_type_t type; - size_t nfile; - size_t *files; - bool operator<( const cbl_exception_files_t& that ) { - return type < that.type; +class exception_turn_t; +bool apply_cdf_turn( const exception_turn_t& turn ); + +class exception_turn_t { + friend bool apply_cdf_turn( const exception_turn_t& turn ); + typedef std::list<size_t> filelist_t; + typedef std::map<ec_type_t, filelist_t> ec_filemap_t; + ec_filemap_t exceptions; + bool enabled, location; + public: + + exception_turn_t() : enabled(false), location(false) {}; + + explicit exception_turn_t( ec_type_t ec, bool enabled = true ) + : enabled(enabled), location(false) + { + add_exception(ec); + } + + bool enable( bool enabled ) { + return this->enabled = enabled; + } + bool enable( bool enabled, bool location ) { + this->location = location; + return this->enabled = enabled; + } + + const ec_filemap_t& exception_files() const { return exceptions; } + + bool add_exception( ec_type_t type, const filelist_t& files = filelist_t() ) { + ec_disposition_t disposition = ec_type_disposition(type); + if( disposition != ec_implemented(disposition) ) { + cbl_unimplementedw(EcUnknownW, "exception %qs", ec_type_str(type)); + } + auto elem = exceptions.find(type); + if( elem != exceptions.end() ) return false; // cannot add twice + + exceptions[type] = files; + return true; + } + + void clear() { + for( auto& ex : exceptions ) { + ex.second.clear(); + } + exceptions.clear(); + enabled = location = false; } -}; -size_t symbol_declaratives_add( size_t program, - const std::list<cbl_declarative_t>& dcls ); +}; #endif + + + diff --git a/gcc/cobol/gcobc b/gcc/cobol/gcobc index 93e1bd302..fa9f609 100755 --- a/gcc/cobol/gcobc +++ b/gcc/cobol/gcobc @@ -35,6 +35,10 @@ ## output set the mode variable. Everything else is appended to the ## opts variable. ## +## - -fPIC is added to the command line if $mode is "-shared". That +## option applies only to "certain machines", per the gcc info +## manual. For this script to be portable across machines, -fPIC +## would have to be set more judiciously. if [ "$COBCPY" ] then @@ -73,7 +77,7 @@ fi exit_status=0 skip_arg= -opts="$copydir ${dialect:--dialect mf} $includes" +opts="$copydir $includes" mode=-shared incomparable="has no comparable gcobol option" @@ -103,6 +107,9 @@ $0 recognizes the following GnuCOBOL cobc output mode options: $0 recognizes the following GnuCOBOL cobc compilation options: -C -d, --debug + -D + -A + -Q -E -g --coverage @@ -112,11 +119,12 @@ $0 recognizes the following GnuCOBOL cobc compilation options: --fixed -F, --free -fimplicit-init - -h, --help - -save-temps= - -save-temps - -std=mvs - -std=mf + -h, --help + -save-temps= + -save-temps + -std=mvs -std=mvs-strict + -std=mf -std=mf-strict + -std=cobol85 -std=cobol2002 -std=cobol2014 Options that are the same in gcobol and cobc are passed through verbatim. Options that have no analog in gcobol produce a warning message. To produce this message, use -HELP. @@ -127,11 +135,14 @@ To override, set the gcobol environment variable. EOF } +dialect="mf gnu" +out_set="" +first="" + # -# Simply iterate over the command-line tokens. We can't use getopts -# here because it's not designed for single-dash words (e.g. -shared). +# Iterate over the command-line tokens. We can't use getopts here +# because it's not designed for single-dash words (e.g. -shared). # - for opt in "$@" do if [ "$skip_arg" ] @@ -142,41 +153,57 @@ do if [ "$pending_arg" ] then - opts="$opts $pending_arg $opt" + case $pending_arg in + -o) output_name="$opt" # capture named output file + ;; + esac + + opts="$opts $pending_arg$opt" pending_arg= continue fi case $opt in - -A | -Q) warn "$opt" - ;; + + # pass next parameter to GCC + -A) + pending_arg=" " + ;; + + # pass next parameter to linker + -Q) + pending_arg=-Wl, + ;; + -b) mode="-shared" ;; -c) mode="-c" ;; --conf=*) warn "$opt" - ;; - -C) error "$opt $incomparable" - ;; - -d | --debug) opts="$opts -fcobol-exceptions=EC-ALL" - warn "$opt implies -fstack-check:" - ;; - # -D - -E) opts="$opts $opt -fsyntax-only" + ;; + -C) error "$opt $incomparable" + ;; + -d | -debug | --debug) opts="$opts -fcobol-exceptions=EC-ALL" + warn "$opt implies -fstack-check:" + ;; + # define for preprocessor, note: -D* is directly passed + -D) + pending_arg=$opt ;; - -echo) echo="echo" + -E) opts="$opts $opt -fsyntax-only" + ;; + -echo) echo="echo" ;; -fec=* | -fno-ec=*) opt="$(echo "$opt" | sed -E 's/-f(no-)?ec=/-f\1cobol-exceptions=EC-/g')" - opts="$opts $opt" - ;; - -ext) - pending_arg=$opt - ;; - -ext=*) opts="$opts $(echo "$opt" | sed 's/-ext=/-copyext ./')" - ;; - + opts="$opts $opt" + ;; + -ext) + pending_arg="$opt " + ;; + -ext=*) opts="$opts $(echo "$opt" | sed 's/-ext=/-copyext ./')" + ;; # A.3 Compiler options -fsign=*) warn "$opt" ;; -ffold-copy=*) warn "$opt" ;; @@ -354,19 +381,18 @@ do -fnot-register=*) warn "$opt" ;; -fregister=*) warn "$opt" ;; - -fformat=auto ) ;; # gcobol and gnucobol default + -fformat=auto) ;; # gcobol and gnucobol default -fixed | --fixed | -fformat=fixed | -fformat=variable | -fformat=xcard) # note: variable + xcard are only _more similar_ to fixed than free, # (with changing right-column to 250/255, which isn't supported in gcobol, yet) - opts="$opts -ffixed-form" - ;; - - -F | -free | --free | -fformat=free | -fformat=* ) - # note: "all other formats" are only _more similar_ to free than fixed - opts="$opts -ffree-form" - ;; + opts="$opts -ffixed-form" + ;; + -F | -free | --free | -fformat=free | -fformat=*) + # note: "all other formats" are only _more similar_ to free than fixed + opts="$opts -ffree-form" + ;; -h | --help) opts="$opts --help" ;; @@ -392,7 +418,13 @@ do ;; # -main # -nomain - # -o + + -o) pending_arg=$opt + ;; + -o*) output_name=$opt ## non-empty means do not infer + opts="$opts $opt" + ;; + # -O0, -Ox -O | -O2 | -Os) warn "$opt" ;; @@ -402,24 +434,37 @@ do export GCOBOL_TEMPDIR="$opt" ;; -save-temps) export GCOBOL_TEMPDIR="${PWD:-$(pwd)}" - ;; - # -shared is identical + ;; + # -shared is identical - -std=mvs) opts="$opts -dialect ibm" + -std=mvs | -std=mvs-strict | -std=ibm | -std=ibm-strict) dialect=ibm ;; - -std=mf) opts="$opts -dialect mf" + -std=mf | -std=mf-strict) dialect=mf + ;; + # GnuCOBOL's default and GCC's dialect for GnuCOBOL + -std=default) dialect=gnu + ;; + # GCC COBOL targets COBOL2024 "mostly backward to COBOL85" + -std=cobol*) dialect="" + ;; + -std=*) + dialect="" + warn "$opt (unkown dialect)" ;; - -t | -T | -tlines=* | -P | -P=* | -X | --Xref) - warn "$opt (no listing)" + -P | -P=* | -X | --Xref) + warn "$opt (no listing)" + ;; + -t | -T) + # note: -P has an _optional_ arg, so we leave it above + ignore_arg "$opt (no listing)" + ;; + -q | --brief) warn "$opt" + ;; + -v | --verbose) opts="$opts -V" + ;; + # note: we want -dumpversion to be passed to gcc + -V | --version | -version) opts="$opts --version" ;; - -q | --brief) warn "$opt" - ;; - -v | --verbose) opts="$opts -V" - ;; - # note: we want -dumpversion to be passed to gcc - -V | --version | -version) opts="$opts --version" - ;; - # pass through, strangely -Wall is not supported -w | -W | -Wextra) opts="$opts $opt" ;; @@ -432,7 +477,25 @@ do -x) mode= ;; - *) opts="$opts $opt" # pass through + -) output_name=a.out # nonnull to prevent overriding gcc default + opts="$opts /dev/stdin" + ;; + + # First file name argument is default output filename. + *) if [ -z "$output_name" -a -e "$opt" ] + then + output_name=$(basename "${opt%.*}") + case $mode in + -c) output_name="$output_name".o + ;; + -shared) + output_name="$output_name".so + opts="$opts -fPIC" + ;; + esac + opts="$opts -o $output_name" + fi + opts="$opts $opt" # pass through ;; esac done @@ -451,6 +514,11 @@ fi # To override the default gcobol, set the "gcobol" environment variable. gcobol="${gcobol:-${0%/*}/gcobol}" +if [ "$dialect" ] +then + dialect=$(echo $dialect | sed -E 's/[[:alnum:]]+/-dialect &/g') +fi + if [ "$echo" ] then echo $gcobol $mode $opts @@ -462,4 +530,4 @@ then set -x fi -exec $gcobol $mode $opts +exec $gcobol $mode $dialect $opts diff --git a/gcc/cobol/gcobol.1 b/gcc/cobol/gcobol.1 index 64c017c..92b2226 100644 --- a/gcc/cobol/gcobol.1 +++ b/gcc/cobol/gcobol.1 @@ -1,4 +1,4 @@ -.ds lang COBOL + .ds lang COBOL .ds gcobol GCC\ \*[lang]\ Front-end .ds isostd ISO/IEC 1989:2023 .Dd \& February 2025 @@ -31,15 +31,61 @@ .Op Fl preprocess Ar preprocess-filter .Op Fl fflex-debug .Op Fl fyacc-debug +.\" warnings +.Op Fl Wno-apply-commit +.Op Fl Wno-file-code-set +.Op Fl Wno-high-order-bit +.Op Fl Wno-bad-line-directive +.Op Fl Wno-bad-numeric +.Op Fl Wno-binary-long-long +.Op Fl Wno-call-giving +.Op Fl Wno-cdf-dollar +.Op Fl Wno-cdf-invalid-parameter +.Op Fl Wno-cdf-name-not-found +.Op Fl Wno-cobol-eject +.Op Fl Wno-cobol-resume +.Op Fl Wno-cobol-volatile +.Op Fl Wno-comp-6 +.Op Fl Wno-comp-x +.Op Fl Wno-copybook-found +.Op Fl Wno-ec-unknown +.Op Fl Wno-entry-convention +.Op Fl Wno-iconv-error +.Op Fl Wno-include-file-found +.Op Fl Wno-include-file-not-found +.Op Fl Wno-inspect-trailing +.Op Fl Wno-length-of +.Op Fl Wno-level-1-occurs +.Op Fl Wno-level-78 +.Op Fl Wno-level-78-defined +.Op Fl Wno-literal-concat +.Op Fl Wno-locale-error +.Op Fl Wno-move-corresponding +.Op Fl Wno-move-pointer +.Op Fl Wno-nllanginfo-error +.Op Fl Wno-operator-space +.Op Fl Wno-preprocessor-error +.Op Fl Wno-procedure-not-found +.Op Fl Wno-procedure-pointer +.Op Fl Wno-replace-error +.Op Fl Wno-returning-number +.Op Fl Wno-segment-error +.Op Fl Wno-segment-negative +.Op Fl Wno-stop-number +.Op Fl Wno-stray-indicator +.Op Fl Wno-usage-typename +.Op Fl Wno-recording-mode +.Op Fl Wno-set-locale-to +.Op Fl Wno-set-to-locale .Ar filename Op ... . .Sh DESCRIPTION .Nm -compiles \*[lang] source code to object code, and optionally produces an -executable binary or shared object. As a GCC component, it accepts +compiles \*[lang] source code to object code, and optionally produces +an executable binary or shared object. As a GCC component, it accepts all options that affect code-generation and linking. Options specific to \*[lang] are listed below. -.Bl -tag -width \0\0debug +.Bl -tag -width "\0\0debug" .It Fl main Ar filename .Nm will generate a @@ -97,8 +143,9 @@ Define a CDF name (for use with to have the value of .Ar expr . .It Fl E -Write the CDF-processed \*[lang] input to standard output in free-form -reference format. Certain non-\*[lang] markers are included in the +Write the CDF-processed \*[lang] input to standard output in +.Em "free-form reference format". +Certain non-\*[lang] markers are included in the output to indicate where copybook files were included. For line-number consistency with the input, blank lines are retained. .Pp @@ -147,13 +194,13 @@ and in that order. .It Fl ffixed-form Use strict -.Em "Reference Format" +.Em "fixed-form reference format" in reading the \*[lang] input: 72-character lines, with a 6-character sequence area, and an indicator column. Data past column 72 are ignored. .It Fl ffree-form Force the \*[lang] input to be interpreted as -.Em "free format" . +.Em "free-form reference format". Line breaks are insignificant, except that .Ql * at the start of a line acts as a comment marker. @@ -187,12 +234,21 @@ the entire program could appear on one line. .Pp By default, .Nm -auto-detects the source code format by examining the -.Em "sequence number area" -of the first line of the first file: if those characters are all -digits or blanks, the file is assumed to be in -.Em "reference format" , +auto-detects the source code format by examining the line that +contains the text "program-id". When there are characters on past column 72 +on that line, the file is assumed to be in +.Em "extended source format", with the indicator area in column 7. +Otherwise, columns 1-6 are examined. If those characters are all digits +or blanks, the file is assumed to be in +.Em "fixed-form reference format", +also with the indicator in column 7. +If not auto-detected as +.Em "fixed-form reference format" +or +.Em "extended source format", +the file is assumed to be in +.Em "free-form reference format". .Pp . .It Fl fcobol-exceptions Ar exception Op Ns , Ns Ar exception Ns ... @@ -224,7 +280,7 @@ had appeared. Not all exception conditions are implemented. Any that are not produce a warning message. . -.It Fl fmax-errors Ar nerror +.It Fl fmax-errors Ns Li = Ns Ar nerror .Ar nerror represents the number of error messages produced. Without this option, .Nm @@ -273,30 +329,66 @@ because its value is determined at run time. By default, .Nm accepts \*[lang] syntax as defined by \*[isostd], with some -extensions for backward compatibility with COBOL-85. To make the -compiler more generally useful, some additional syntax is supported by -this option. -.Pp -The value of +extensions for backward compatibility with COBOL-85. Additional syntax is supported with this option. The value of .Ar dialect-name may be -.Bl -tag -compact +.Bl -tag .It ibm -to indicate IBM COBOL 6.3 syntax, specifically -.D1 STOP <number>. +to indicate IBM COBOL 6.4 syntax: +.Bl -bullet -compact +.It +.Sy EJECT +.It +.Sy EQUAL +as assignment operator +.It +.Sy "LENGTH OF" +.It +.Sy "PROCEDURE POINTER" +.It +.Sy SECTION +segment +.It +.Sy STOP +<number> +.It +.Sy VOLATILE +.El .It gnu -to indicate GnuCOBOL syntax +to indicate GnuCOBOL syntax, generally compatible with MicroFocus. .It mf -to indicate MicroFocus syntax, specifically +to indicate MicroFocus syntax: +.Bl -bullet -compact +.It +.Sy BINARY-LONG-LONG +.It +.Sy CALL ... GIVING +.It +.Sy CDF \[Do]IF +.It +.Sy COMPUTATIONAL-6 +.It +.Sy COMPUTATIONAL +used with +.Sy PICTURE X +.It +.Sy INSPECT ... TRAILING +.It +.Sy OCCURS +at +.Sy "LEVEL 01" +.It .Sy LEVEL 78 -constants. +constants +.It +.Sy MOVE POINTER +.It +.Sy RETURNING +<number> +.It +.Sy USAGE IS TYPENAME +.El .El -.Pp -Only a few such non-standard constructs are accepted, and -.Nm -makes no claim to emulate other compilers. But to the extent that a -feature is popular but nonstandard, this option provides a way to -support it, or add it. . .It Fl include Ar filename Process @@ -361,7 +453,6 @@ The should return a zero exit status, indicating success. If it returns a nonzero exit status, an error is reported and the compiler is not invoked. -. .It Fl fflex-debug Ns Li , Fl fyacc-debug produce messages useful for compiler development. The .Fl fflex-debug @@ -369,6 +460,111 @@ option prints the tokenized input stream. The .Fl fyacc-debug option shows the shift and reduce actions taken by the parser. .El +.Ss Diagnostic Messages +.Pp +Many warning options can be used to convert error messages to +warnings, or to suppress messages related to \*[lang] dialects. The +user may mix and match. A group of features may be enabled by +indicating a dialect (or more than one dialect) and individual +features may be enabled as a warning, or error, or suppressed. +.Bl -tag -width Wno-cdf-name-not-found\0\0 -compact +.It Fl Wno-apply-commit +Warn if APPLY COMMIT is used. +.It Fl Wno-bad-line-directive +Warn if malformed %<#line%> directive is encountered. +.It Fl Wno-binary-long-long +Warn if BINARY-LONG-LONG is used. +.It Fl Wno-call_giving +Warn if CALL ... GIVING is used. +.It Fl Wno-cdf-dollar +Warn if CDF \[Do]IF is used. +.It Fl Wno-comp-6 +Warn if COMPUTATIONAL-6 is used. +.It Fl Wno-comp-x +Warn if COMPUTATIONAL is used with PICTURE X. +.It Fl Wno-file-code-set +Warn if FILE CODE SET is used. +.It Fl Wno-inspect-trailing +Warn if INSPECT ... TRAILING is used. +.It Fl Wno-level-1-occurs +Warn if Level 01 is used with OCCURS. +.It Fl Wno-level-78-defined +Warn if CDF defines Level 78 constant. +.It Fl Wno-move-pointer +Warn if MOVE POINTER is used. +.It Fl Wno-returning-number +Warn if RETURNING <number> is used. +.It Fl Wno-usage-typename +Warn if USAGE IS TYPENAME is used. +.It Fl Wno-bad-numeric +Warn if numeric string is invalid. +.It Fl Wno-cdf-invalid-parameter +Warn if referenced CDF PARAMETER is not defined. +.It Fl Wno-cdf-name-not-found +Warn if referenced CDF name is not defined. +.It Fl Wno-cobol-eject +Warn if IBM-style EJECT is used (instead of error). +.It Fl Wno-cobol-resume +Warn if ISO RESUME is used with \-dialect ibm (instead of error). +.It Fl Wno-cobol-volatile +Warn if VOLATILE is used (instead of error if -dialect ibm). +.It Fl Wno-copybook-found +Print message when copybook is processed. +.It Fl Wno-ec-unknown +Warn if unimplemented/unknown exception condition is referenced. +.It Fl Wno-entry-convention +Print message when ENTRY CONVENTION is specified. +.It Fl Wno-high-order-bit +Warn if HIGH-ORDER-LEFT HIGH-ORDER-RIGHT is used. +.It Fl Wno-include-file-found +Print message when include file is processed. +.It Fl Wno-length-of +Warn if LENGTH OF is used. +.It Fl Wno-level-78 +Warn if Level 78 is used. +.It Fl Wno-literal-concat +Warn if concatenated literals use different encodings. +.It Fl Wno-locale-error +Warn if locale(3) fails. +.It Fl Wno-move-corresponding +Warn if COBOL MOVE has no corresponding fields. +.It Fl Wno-nllanginfo-error +Warn if nlanglanginfo(3) fails. +.It Fl Wno-recording-mode +Warn if RECORDING MODE is used. +.It Fl Wno-segment +Warn if SECTION segments are used. +.It Fl Wno-set-locale-to +Warn if SET LOCALE ... TO is used. +.It Fl Wno-set-to-locale +Warn if SET ... TO LOCALE is used. +. +`.\" convert errors to warnings +Warn if a line directive is malformed (instead of error). +.It Fl Wno-iconv-error +Warn if iconv(3) cannot convert between encodings (instead of error). +.It Fl Wno-include-file-not-found +Warn if include file is not found (instead of error). +.It Fl Wno-operator-space +Warn if relational operator not followed by space (instead of error). +.It Fl Wno-preprocessor-error +Warn if a preprocessor fails (instead of error). +.It Fl Wno-procedure-pointer +Warn if PROCEDURE POINTER is used. +.It Fl Wno-procedure-not-found +Warn if a referenced procedure is not found (instead of error). +.It Fl Wno-replace-error +Warn if REPLACE cannot be processed (instead of error). +.It Fl Wno-segment-error +Warn if a SEGMENT section is invalid (instead of error). +.It Fl Wno-segment-negative +Warn if a SEGMENT range is negative (instead of error). +.It Fl Wno-stop-number +Warn if IBM-style STOP <number> is used (instead of error). +.It Fl Wno-stray-indicator +Warn if indicator column has no recognized meaning (instead of error). + +.El . .Sh COMPILATION SCENARIOS .D1 gcobol Ar xyz.cob @@ -577,6 +773,21 @@ statement, regardless of compile-time constants. .. . .Ss Implemented Exception Conditions +By default, per ISO, no EC is enabled. Implemented ECs may be enabled +on the command line or via the +.Sy TURN +directive. Any attempt to enable an EC that is not implemented is +treated as an error. +.Pp +An enabled EC not handled by a +.Sy DECLARATIVE +is written to the system log and to standard error. (The authors +intend to make that an option.) A fatal EC not handled with +.Sy RESUME +ends with a call to +.Xr abort 3 +and process termination. +.Pp Not all Exception Conditions are implemented. Any attempt to enable an EC that that is not implemented produces a warning message. The following are implemented: @@ -753,6 +964,30 @@ resolution of .Ar filename is deferred until runtime, when the name must appear in the program's environment. +.Ss XML PARSE +.Nm +emulates the IBM +.Sy "XML PARSE" +statement. The following values for +.Sy XML-EVENT +are defined: +.Bl -tag -compact +.It Sy COMMENT +Text of a comment between "<!--" and "-->" +.It Sy CONTENT-CHARACTERS +Some or all of the character content of the element between start and end tags. +.It Sy END-OF-ELEMENT +End-element tag, with name if present in the input. +.It Sy PROCESSING-INSTRUCTION-DATA +Processing instruction (after the target name), excluding "?>". +.It Sy PROCESSING-INSTRUCTION-TARGET +The processing instruction target name appears in +.Sy XML-TEXT +or +.Sy XML-NTEXT . +.It Sy START-OF-ELEMENT +Name of the start element tag or empty element tag. +.El . .Sh ISO \*[lang] Implementation Status .Ss USAGE Data Types @@ -903,11 +1138,90 @@ or has no effect; the two are interchangeable. .. . -.Ss Compiler-Directing Facility (CDF) +.Sh COMPILER-DIRECTING FACILITY The CDF should be used with caution because no comprehensive test -suite has been identified. +suite has been identified. +. +.Ss CDF Text Manipulation +.Bl -tag -width >>DEFINE +.It Sy COPY Ar copybook Li Oo OF|BY Ar library Oc Oo Sy REPLACING ... Oc +If +.Ar copybook +is a literal, it treated a literal filename, which either does or does not exist. If +.Ar copybook +is a \*[lang] word, +.Nm +looks first for an environment variable named +.Va copybook +and, if found, uses the contents of that variable as the name of the +copybook file. If that file does not exist, it continues looking for +a file named one of: +.sp +.Bl -bullet -compact -offset 5n +.It +.Pa copybook +(literally) +.It +.Pa copybook.cpy +.It +.Pa copybook.CPY +.It +.Pa copybook.cbl +.It +.Pa copybook.CBL +.It +.Pa copybook.cob +.It +.Pa copybook.COB +.El +.sp +in that order. It looks first in the same directory as the source +code file, and then in any +.Ar copybook-path +named with the +.Fl I +option. +. +.\" FIXME: need escape mechanism for directories with ':' in the name. +.Ar copybook-path +may (like the shell's +.Ev PATH +variable) be a colon-separated list. +The +.Fl I +option may occur multiple times on the command line. Each successive +.Ar copybook-path +is concatenated to previous ones. +Relative paths (having no leading +.Ql / Ns +\&) +are searched relative to the compiler's current working directory. +.Pp +For example, +.D1 \& +.D1 Fl I Li /usr/local/include:include +.D1 \& +searches first the directory where the \*[lang] program is found, next in +.Pa /usr/local/include , +and finally in an +.Pa include +subdirectory of the directory from which +.Nm +was invoked. +.Pp +For the +.Sy REPLACING +phrase, both the modern pseudo-text and the \*[lang]/85 forms are +recognized. (The older forms are used in the NIST CCVS/85 test suite.) +.It Sy REPLACE ... +.Nm +supports the full ISO +.Sy REPLACE +syntax. +.El . -.Ss Conditional Compilation +.Ss CDF Directives +.\"Bl -tag -width >>PROPAGATE .Bl -tag -width >>DEFINE .It >> Ns Sy DEFINE Ar name Sy AS Bro Ar expression Li | Sy PARAMETER Brc Op Sy OVERRIDE Define @@ -949,10 +1263,6 @@ is supported. Boolean literals are not supported. . .It >> Ns Sy EVALUATE Not implemented. -.El -. -.Ss Other CDF Directives -.Bl -tag -width >>PROPAGATE .It >> Ns Sy CALL-CONVENTION Ar convention .Ar convention may be one of: @@ -1000,9 +1310,12 @@ to standard error as a warning message. may be one of: .Bl -tag -compact .It Sy FIXED -Source conforms to \*[lang] Reference Format with unlimited line length. +Source conforms to \*[lang] +.Em "fixed-form reference format" +with unlimited line length. .It Sy FREE -Line endings and indentation are ignored by the compiler, except that a +Source conforms to \*[lang] +.Em "free-form reference format". .Ql "*" at the beginning of a line is recognized as a comment. .El @@ -1023,6 +1336,24 @@ Not implemented. Not implemented. .It >> Ns Sy PROPAGATE Not implemented. +.It >> Ns Sy PUSH Ar directive +.It >> Ns Sy POP Ar directive +With +.Sy PUSH , +push CDF state onto a stack. +With +.Sy POP , +return to the prior pushed state. +.Ar directive +may be one of +.Bl -tag -compact +.It Sy CALL-CONVENTION +.It Sy COBOL-WORDS +.It Sy DEFINE +.It Sy SOURCE FORMAT +.It Sy TURN +.El +. .It >> Ns Sy TURN Oo .Ar ec Oo Ar file Li ... Oc ... .Oc Sy CHECKING Bro Oo Sy ON Oc Oo Oo Sy WITH Oc Sy LOCATION Oc | Sy OFF Brc @@ -1073,76 +1404,6 @@ the directive must appear before .Pp To test a feature-set variable, use .Dl >>IF Ar feature Li DEFINED -.. -.Ss Copybooks -.Nm -supports the CDF -.Sy COPY -statement, with or without its -.Sy REPLACING -component. For any statement -.sp -.D1 COPY Ar copybook -.sp -.Nm -looks first for an environment variable named -.Va copybook -and, if found, uses the contents of that variable as the name of the -copybook file. If that file does not exist, it continues looking for -a file named one of: -.sp -.Bl -bullet -compact -offset 5n -.It -.Pa copybook -(literally) -.It -.Pa copybook.cpy -.It -.Pa copybook.CPY -.It -.Pa copybook.cbl -.It -.Pa copybook.CBL -.It -.Pa copybook.cob -.It -.Pa copybook.COB -.El -.sp -in that order. It looks first in the same directory as the source -code file, and then in any -.Ar copybook-path -named with the -.Fl I -option. -. -.\" FIXME: need escape mechanism for directories with ':' in the name. -.Ar copybook-path -may (like the shell's -.Ev PATH -variable) be a colon-separated list. -. -The -.Fl I -option may occur multiple times on the command line. Each successive -.Ar copybook-path -is concatenated to previous ones. -Relative paths (having no leading -.Ql / Ns -\&) -are searched relative to the compiler's current working directory. -.Pp -For example, -.D1 \& -.D1 Fl I Li /usr/local/include:include -.D1 \& -searches first the directory where the \*[lang] program is found, next in -.Pa /usr/local/include , -and finally in an -.Pa include -subdirectory of the directory from which -.Nm -was invoked. . .Ss Intrinsic functions .Nm @@ -1152,54 +1413,54 @@ others. They are listed alphabetically below. .It ABS ACOS ANNUITY ASIN ATAN .It -BASECONVERT BIT_OF BIT_TO_CHAR BOOLEAN_OF_INTEGER BYTE_LENGTH +BASECONVERT BIT-OF BIT-TO-CHAR BOOLEAN-OF-INTEGER BYTE-LENGTH .It -CHAR CHAR_NATIONAL COMBINED_DATETIME CONCAT CONVERT COS CURRENT_DATE +CHAR CHAR-NATIONAL COMBINED-DATETIME CONCAT CONVERT COS CURRENT-DATE .It -DATE_OF_INTEGER DATE_TO_YYYYMMDD DAY_OF_INTEGER DAY_TO_YYYYDDD DISPLAY_OF +DATE-OF-INTEGER DATE-TO-YYYYMMDD DAY-OF-INTEGER DAY-TO-YYYYDDD DISPLAY-OF .It -E EXCEPTION_FILE -EXCEPTION_FILE_N EXCEPTION_LOCATION EXCEPTION_LOCATION_N -EXCEPTION_STATEMENT EXCEPTION_STATUS EXP EXP10 +E EXCEPTION-FILE +EXCEPTION-FILE-N EXCEPTION-LOCATION EXCEPTION-LOCATION-N +EXCEPTION-STATEMENT EXCEPTION-STATUS EXP EXP10 .It -FACTORIAL FIND_STRING -FORMATTED_CURRENT_DATE FORMATTED_DATE FORMATTED_DATETIME -FORMATTED_TIME FRACTION_PART +FACTORIAL FIND-STRING +FORMATTED-CURRENT-DATE FORMATTED-DATE FORMATTED-DATETIME +FORMATTED-TIME FRACTION-PART .It -HEX_OF HEX_TO_CHAR HIGHEST_ALGEBRAIC +HEX-OF HEX-TO-CHAR HIGHEST-ALGEBRAIC .It -INTEGER INTEGER_OF_BOOLEAN INTEGER_OF_DATE INTEGER_OF_DAY -INTEGER_OF_FORMATTED_DATE INTEGER_PART +INTEGER INTEGER-OF-BOOLEAN INTEGER-OF-DATE INTEGER-OF-DAY +INTEGER-OF-FORMATTED-DATE INTEGER-PART .It -LENGTH LOCALE_COMPARE -LOCALE_DATE LOCALE_TIME LOCALE_TIME_FROM_SECONDS LOG LOG10 LOWER_CASE -LOWEST_ALGEBRAIC +LENGTH LOCALE-COMPARE +LOCALE-DATE LOCALE-TIME LOCALE-TIME-FROM-SECONDS LOG LOG10 LOWER-CASE +LOWEST-ALGEBRAIC .It -MAX MEAN MEDIAN MIDRANGE MIN MOD MODULE_NAME +MAX MEAN MEDIAN MIDRANGE MIN MOD MODULE-NAME .It -NATIONAL_OF NUMVAL NUMVAL_C NUMVAL_F ORD +NATIONAL-OF NUMVAL NUMVAL-C NUMVAL-F ORD .It -ORD_MAX ORD_MIN +ORD-MAX ORD-MIN .It -PI PRESENT_VALUE +PI PRESENT-VALUE .It RANDOM RANGE REM REVERSE .It -SECONDS_FROM_FORMATTED_TIME -SECONDS_PAST_MIDNIGHT SIGN SIN SMALLEST_ALGEBRAIC SQRT -STANDARD_COMPARE STANDARD_DEVIATION SUBSTITUTE SUM +SECONDS-FROM-FORMATTED-TIME +SECONDS-PAST-MIDNIGHT SIGN SIN SMALLEST-ALGEBRAIC SQRT +STANDARD-COMPARE STANDARD-DEVIATION SUBSTITUTE SUM .It -TAN TEST_DATE_YYYYMMDD TEST_DAY_YYYYDDD TEST_FORMATTED_DATETIME -TEST_NUMVAL TEST_NUMVAL_C TEST_NUMVAL_F TRIM +TAN TEST-DATE-YYYYMMDD TEST-DAY-YYYYDDD TEST-FORMATTED-DATETIME +TEST-NUMVAL TEST-NUMVAL-C TEST-NUMVAL-F TRIM .It -ULENGTH UPOS UPPER_CASE +ULENGTH UPOS UPPER-CASE USUBSTR USUPPLEMENTARY UUID4 UVALID UWIDTH .It VARIANCE .It -WHEN_COMPILED +WHEN-COMPILED .It -YEAR_TO_YYYY +YEAR-TO-YYYY .El . .Ss Binary floating point DISPLAY @@ -1279,7 +1540,7 @@ stores and converts numbers. Converting the floating-point value to the numeric display value 0055110 is done by multiplying 55.109999...\& by 1,000 and then truncating the result to an integer. And it turns out that even -though 55.11 can’t be represented in floating-point as an exact value, +though 55.11 can't be represented in floating-point as an exact value, the product of the multiplication, 55110, is an exact value. .Pp In cases where it is important for conversions to have predictable @@ -1310,7 +1571,7 @@ specified for a calculation, then the intermediate result becomes a . .Ss A warning about binary floating point comparison The cardinal rule when doing comparisons involving floating-point -values: Never, ever, test for equality. It’s just not worth the hassle. +values: Never, ever, test for equality. It's just not worth the hassle. .Pp For example: .Bd -literal @@ -1346,7 +1607,7 @@ and you really test the code. And then avoid it anyway. .Pp Finally, it is observably the case that the .Nm -implementations of floating-point conversions and comparisons don’t +implementations of floating-point conversions and comparisons don't precisely match the behavior of other \*[lang] compilers. .Pp You have been warned. @@ -1366,6 +1627,15 @@ it may contain several directory names separated by a colon .Ev COBPATH is searched first, followed by .Ev LD_LIBRARY_PATH . +Note that +.Ev COBPATH does not change where the runtime linker looks for +.Pa libgcobol.so +itself. +How the runtime linker searches for +.Pa libgcobol.so +when the executable loads is controlled by +.Xr ld.so 8 , +not libgcobol. .Pp Each directory is searched for files whose name ends in .Ql ".so" . @@ -1420,6 +1690,18 @@ error. This feature is meant to help diagnose mysterious copybook errors. .El . +.Ss Variables for Developers +.Bl -tag -compact +.It Ev GCOBOL_SHOW +produces a trace of the internal calls made by the parser to prepare +the GENERIC tree. +.It Ev GCOBOL_TRACE +used at compile time, produces an executable that traces the +execution, mapping it back the same code-creation functions as +.Ev GCOBOL_SHOW , +as well as the values of data items and branch conditions. +.El +. .Sh FILES Executables produced by .Nm diff --git a/gcc/cobol/gcobolspec.cc b/gcc/cobol/gcobolspec.cc index 4ae8e2c..1f1b463 100644 --- a/gcc/cobol/gcobolspec.cc +++ b/gcc/cobol/gcobolspec.cc @@ -57,10 +57,6 @@ along with GCC; see the file COPYING3. If not see int lang_specific_extra_outfiles = 0; -#ifndef MATH_LIBRARY -#define MATH_LIBRARY "m" -#endif - #ifndef DL_LIBRARY #define DL_LIBRARY "dl" #endif @@ -73,16 +69,20 @@ int lang_specific_extra_outfiles = 0; #define COBOL_LIBRARY "gcobol" #endif +#define SPEC_FILE "libgcobol.spec" + /* The original argument list and related info is copied here. */ static const struct cl_decoded_option *original_options; /* The new argument list will be built here. */ static std::vector<cl_decoded_option>new_opt; +static bool need_libgcobol = true; + // #define NOISY 1 static void -append_arg(const struct cl_decoded_option arg) +append_arg(const cl_decoded_option& arg) { #ifdef NOISY static int counter = 1; @@ -116,8 +116,8 @@ add_arg_lib(const char *library, bool force_static ATTRIBUTE_UNUSED) { append_option (OPT_Wl_, LD_STATIC_OPTION, 1); } - append_option (OPT_l, library, 1); #endif + append_option (OPT_l, library, 1); #ifdef HAVE_LD_STATIC_DYNAMIC if( force_static ) { @@ -126,41 +126,6 @@ add_arg_lib(const char *library, bool force_static ATTRIBUTE_UNUSED) #endif } -static void -append_rdynamic() - { - // This is a bit ham-handed, but I was in a hurry. - struct cl_decoded_option decoded = {}; - decoded.opt_index = OPT_rdynamic; - decoded.orig_option_with_args_text = "-rdynamic"; - decoded.canonical_option[0] = "-rdynamic"; - decoded.canonical_option_num_elements = 1; - decoded.value = 1; - append_arg(decoded); - return; - } - -static void -append_allow_multiple_definition() - { - append_option (OPT_Wl_, "--allow-multiple-definition", 1); - return; - } - -static void -append_fpic() - { - // This is a bit ham-handed, but I was in a hurry. - struct cl_decoded_option decoded = {}; - decoded.opt_index = OPT_rdynamic; - decoded.orig_option_with_args_text = "-fPIC"; - decoded.canonical_option[0] = "-fPIC"; - decoded.canonical_option_num_elements = 1; - decoded.value = 1; - append_arg(decoded); - return; - } - void lang_specific_driver (struct cl_decoded_option **in_decoded_options, unsigned int *in_decoded_options_count, @@ -177,9 +142,6 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, int n_infiles = 0; int n_outfiles = 0; - // The number of input files when the language is "none" or "cobol" - int n_cobol_files = 0; - // saw_OPT_no_main means "don't expect -main" bool saw_OPT_no_main = false; @@ -188,20 +150,13 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, bool saw_OPT_c = false; bool saw_OPT_shared = false; - bool saw_OPT_pic = false; - bool saw_OPT_PIC = false; bool verbose = false; // These flags indicate whether we need various libraries - bool need_libgcobol = true; - bool need_libmath = (MATH_LIBRARY[0] != '\0'); bool need_libdl = (DL_LIBRARY[0] != '\0'); bool need_libstdc = (STDCPP_LIBRARY[0] != '\0'); - // bool need_libquadmath = (QUADMATH_LIBRARY[0] != '\0'); - bool need_rdynamic = true; - bool need_allow_multiple_definition = true; // Separate flags for a couple of static libraries bool static_libgcobol = false; @@ -276,11 +231,6 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, case OPT_SPECIAL_input_file: no_files_error = false; n_infiles += 1; - if( strcmp(language, "none") == 0 - || strcmp(language, "cobol") == 0 ) - { - n_cobol_files += 1; - } if( strstr(decoded_options[i].orig_option_with_args_text, "libgcobol.a") ) { // We have been given an explicit libgcobol.a. We need to note that. @@ -292,37 +242,10 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, saw_OPT_shared = true; break; - case OPT_fpic: - saw_OPT_pic = true; - break; - - case OPT_fPIC: - saw_OPT_PIC = true; - break; - - case OPT_c: - // With this option, no libraries need be loaded + case OPT_c: + // Note -c specially. saw_OPT_c = true; - need_libgcobol = false; - need_libmath = false; - need_libdl = false; - need_libstdc = false; - // need_libquadmath = false; - need_rdynamic = false; - break; - - case OPT_rdynamic: - need_rdynamic = false; - break; - - case OPT_Wl_: - if( strstr(decoded_options[i].orig_option_with_args_text, - "--allow-multiple-definitions") ) - { - need_allow_multiple_definition = false; - } - break; - + // FALLTHROUGH case OPT_nostdlib: case OPT_nodefaultlibs: case OPT_r: @@ -331,27 +254,18 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, case OPT_E: // With these options, no libraries need be loaded need_libgcobol = false; - need_libmath = false; need_libdl = false; need_libstdc = false; - // need_libquadmath = false; - need_rdynamic = false; break; case OPT_static_libgcobol: -#ifdef HAVE_LD_STATIC_DYNAMIC static_libgcobol = true; need_libgcobol = true; -#endif break; case OPT_l: n_infiles += 1; - if(strcmp(decoded_options[i].arg, MATH_LIBRARY) == 0) - { - need_libmath = false; - } - else if(strcmp(decoded_options[i].arg, DL_LIBRARY) == 0) + if(strcmp(decoded_options[i].arg, DL_LIBRARY) == 0) { need_libdl = false; } @@ -387,8 +301,8 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, case OPT_print_multi_os_directory: case OPT_print_multiarch: case OPT_print_sysroot_headers_suffix: - no_files_error = false; - break; + no_files_error = false; + break; case OPT_v: no_files_error = false; @@ -457,10 +371,8 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, if( n_infiles == 0 ) { need_libgcobol = false; - need_libmath = false; need_libdl = false; need_libstdc = false; - // need_libquadmath = false; } /* Second pass through arglist, transforming arguments as appropriate. */ @@ -500,15 +412,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, if( prior_main ) { - char ach[128]; - if( entry_point ) - { - strcpy(ach, entry_point); - } + const char *ach; + if (entry_point) + ach = entry_point; else - { - strcpy(ach, decoded_options[i].arg); - } + ach = decoded_options[i].arg; append_option(OPT_main_, ach, 1); prior_main = false; entry_point = NULL; @@ -548,7 +456,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, break; case OPT_static_libgcobol: - // Don't pass this one on to cobol1 +#if !defined (HAVE_LD_STATIC_DYNAMIC) + // Allow the target to use spec substitution. + append_arg(decoded_options[i]); +#endif + // Else don't pass this one on to cobol1 break; ////#ifdef __x86_64__ @@ -558,7 +470,10 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, //// break; ////#endif case OPT_static: +#if defined (HAVE_LD_STATIC_DYNAMIC) + append_arg(decoded_options[i]); static_in_general = true; +#endif break; default: @@ -586,36 +501,23 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, need_libgcobol = false; } - if( need_libgcobol ) + if( static_in_general ) { - add_arg_lib(COBOL_LIBRARY, static_libgcobol); - } - if( need_libmath) - { - add_arg_lib(MATH_LIBRARY, static_in_general); - } - if( need_libdl ) - { - add_arg_lib(DL_LIBRARY, static_in_general); - } - if( need_libstdc && static_in_general ) - { - add_arg_lib(STDCPP_LIBRARY, static_in_general); + // These two options interfere with each other. + static_libgcobol = false; } - if( saw_OPT_shared && !saw_OPT_pic && !saw_OPT_PIC ) + if( need_libgcobol ) { - append_fpic(); + add_arg_lib(COBOL_LIBRARY, static_libgcobol); } - - if( need_rdynamic ) + if( need_libdl ) { - append_rdynamic(); + add_arg_lib(DL_LIBRARY, false); } - - if( need_allow_multiple_definition && (n_infiles || n_outfiles) ) + if( need_libstdc ) { - append_allow_multiple_definition(); + add_arg_lib(STDCPP_LIBRARY, false); } if( prior_main ) @@ -628,7 +530,8 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, // cl_decoded_option size_t new_option_count = new_opt.size(); - struct cl_decoded_option *new_options = XNEWVEC (struct cl_decoded_option, new_option_count); + struct cl_decoded_option *new_options = XNEWVEC (struct cl_decoded_option, + new_option_count); for(size_t i=0; i<new_option_count; i++) { @@ -638,15 +541,16 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, #ifdef NOISY verbose = true; #endif - if( verbose && new_options != original_options ) + if( verbose && new_options != original_options ) // cppcheck-suppress knownConditionTrueFalse { - fprintf(stderr, _("Driving: (%ld)\n"), new_option_count); + fprintf(stderr, _("Driving: (" HOST_SIZE_T_PRINT_DEC ")\n"), + (fmt_size_t)new_option_count); for(size_t i=0; i<new_option_count; i++) { fprintf(stderr, - " [%2ld] %4ld %s\n", - i, - new_options[i].opt_index, + " [%2" GCC_PRISZ "d] %4" GCC_PRISZ "d %s\n", + (fmt_size_t)i, + (fmt_size_t)new_options[i].opt_index, new_options[i].orig_option_with_args_text); } fprintf (stderr, "\n"); @@ -656,14 +560,12 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, *in_decoded_options = new_options; } -/* - * Called before linking. - * Returns 0 on success and -1 on failure. - * Unused. - */ +/* Called before linking. Returns 0 on success and -1 on failure. */ int -lang_specific_pre_link( void ) - { - return 0; - } +lang_specific_pre_link (void) +{ + if (need_libgcobol) + do_spec ("%:include(libgcobol.spec)"); + return 0; +} diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc index a4abbd1..ee325fc 100644 --- a/gcc/cobol/genapi.cc +++ b/gcc/cobol/genapi.cc @@ -27,6 +27,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #include "cobol-system.h" #include "coretypes.h" @@ -34,8 +35,7 @@ #include "tree-iterator.h" #include "stringpool.h" #include "diagnostic-core.h" - -#define HOWEVER_GCC_DEFINES_TREE 1 +#include "target.h" #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" @@ -50,17 +50,19 @@ #include "genmath.h" #include "structs.h" #include "../../libgcobol/gcobolio.h" -#include "../../libgcobol/libgcobol.h" #include "../../libgcobol/charmaps.h" #include "../../libgcobol/valconv.h" #include "show_parse.h" +#include "fold-const.h" +#include "realmpfr.h" extern int yylineno; #define TSI_BACK (tsi_last(current_function->statement_list_stack.back())) extern char *cobol_name_mangler(const char *cobol_name); -static tree gg_attribute_bit_get(struct cbl_field_t *var, cbl_field_attr_t bits); +static tree gg_attribute_bit_get( struct cbl_field_t *var, + cbl_field_attr_t bits); static tree label_list_out_goto; static tree label_list_out_label; @@ -76,10 +78,12 @@ static int pseudo_label = 1; static bool suppress_cobol_entry_point = false; static char ach_cobol_entry_point[256] = ""; -bool bSHOW_PARSE = getenv("SHOW_PARSE"); +bool bSHOW_PARSE = getenv("GCOBOL_SHOW"); bool show_parse_sol = true; int show_parse_indent = 0; +static bool sv_is_i_o = false; + #define DEFAULT_LINE_NUMBER 2 #ifdef LINE_TICK @@ -114,17 +118,17 @@ typedef struct TREEPLET static void -treeplet_fill_source(TREEPLET &treeplet, cbl_refer_t &refer) +treeplet_fill_source(TREEPLET &treeplet, const cbl_refer_t &refer) { treeplet.pfield = gg_get_address_of(refer.field->var_decl_node); - treeplet.offset = refer_offset_source(refer); + treeplet.offset = refer_offset(refer); treeplet.length = refer_size_source(refer); } tree file_static_variable(tree type, const char *v) { - // This routine returns a reference to an already-defined file_static variable - // You need to know the type that was used for the definition. + // This routine returns a reference to an already-defined file_static + // variable. You need to know the type that was used for the definition. return gg_declare_variable(type, v, NULL, vs_file_static); } @@ -140,9 +144,9 @@ static void move_helper(tree size_error, // INT // set using -f-trace-debug, defined in lang.opt int f_trace_debug; -// When doing WRITE statements, the IBM Language Reference and the ISO/IEC_2014 -// standard specify that when the ADVANCING clause is omitted, the default is -// AFTER ADVANCING 1 LINE. +// When doing WRITE statements, the IBM Language Reference and the +// ISO/IEC_2014 standard specify that when the ADVANCING clause is omitted, the +// default isAFTER ADVANCING 1 LINE. // // MicroFocus and GnuCOBOL state that the default is BEFORE ADVANCING 1 LINE // @@ -187,6 +191,9 @@ const char *gv_trace_switch = NULL; char const *bTRACE1 = NULL; tree trace_handle; tree trace_indent; + +// This variable is set to true when the output cursor is known to be at the +// start-of-line. bool cursor_at_sol = true; static void @@ -196,10 +203,10 @@ trace1_init() if( first_time ) { first_time = false; - trace_handle = gg_define_variable(INT, "trace_handle", vs_static); - trace_indent = gg_define_variable(INT, "trace_indent", vs_static); + trace_handle = gg_define_variable(INT, "_trace_handle", vs_static); + trace_indent = gg_define_variable(INT, "_trace_indent", vs_static); - bTRACE1 = getenv("TRACE1") ? getenv("TRACE1") : gv_trace_switch; + bTRACE1 = getenv("GCOBOL_TRACE") ? getenv("GCOBOL_TRACE") :gv_trace_switch; if( bTRACE1 && strcmp(bTRACE1, "0") != 0 ) { @@ -226,6 +233,13 @@ trace1_init() } } +static +void +insert_nop(int n) + { + gg_assign(var_decl_nop, build_int_cst_type(INT, n)); + } + static void create_cblc_string_variable(const char *var_name, const char *var_contents) { @@ -263,13 +277,22 @@ build_main_that_calls_something(const char *something) SHOW_PARSE_END } - gg_set_current_line_number(DEFAULT_LINE_NUMBER); + tree function_decl = gg_define_function( INT, + "main", + "main", + INT, "argc", + build_pointer_type(CHAR_P), "argv", + NULL_TREE); - gg_define_function( INT, - "main", - INT, "argc", - build_pointer_type(CHAR_P), "argv", - NULL_TREE); + // Modify the default settings for main(), as empirically determined from + // examining C/C+_+ compilations. (See the comment for gg_build_fn_decl()). + TREE_ADDRESSABLE(function_decl) = 0; + TREE_USED(function_decl) = 0; + TREE_NOTHROW(function_decl) = 0; + TREE_STATIC(function_decl) = 1; + DECL_EXTERNAL (function_decl) = 0; + TREE_PUBLIC (function_decl) = 1; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1; // Pick up pointers to the input parameters: // First is the INT which is the number of argv[] entries @@ -307,7 +330,6 @@ build_main_that_calls_something(const char *something) argc, argv, NULL_TREE))); - strncpy(ach_cobol_entry_point, psz, sizeof(ach_cobol_entry_point)-1); free(psz); gg_finalize_function(); } @@ -344,11 +366,15 @@ static char * level_88_helper(size_t parent_capacity, const cbl_domain_elem_t &elem, - size_t &returned_size) + size_t &returned_size, + cbl_encoding_t encoding) { // We return a MALLOCed return value, which the caller must free. - char *retval = (char *)xmalloc(parent_capacity + 64); - char *builder = (char *)xmalloc(parent_capacity + 64); + char *retval = static_cast<char *>(xmalloc(parent_capacity + 64)); + gcc_assert(retval); + char *builder = static_cast<char *>(xmalloc(parent_capacity + 64)); + gcc_assert(builder); + size_t nbuild = 0; cbl_figconst_t figconst = cbl_figconst_of( elem.name()); @@ -389,15 +415,25 @@ level_88_helper(size_t parent_capacity, // Pick up the string size_t first_name_length = elem.size(); - char *first_name = (char *)xmalloc(first_name_length + 1); + char *first_name = static_cast<char *>(xmalloc(first_name_length + 1)); + gcc_assert(first_name); memcpy(first_name, elem.name(), first_name_length); first_name[first_name_length] = '\0'; - // Convert it to EBCDIC, when necessary; leave it alone when not necessary. - for(size_t i=0; i<first_name_length; i++) + /* By rights, the parser should have given us this string in the target + encoding. When I discovered that it was not, Jim Lowden was out of + town for a week, and I didn't feel like figuring out where in the + parser the fix should be. + + So, I am doing the conversion here. Eventually that will be fixed, and + chaos will reign here. When that happens, remove the following + conversion. */ + charmap_t *charmap = __gg__get_charmap(encoding); + for(size_t i=0; i<strlen(first_name); i++) { - first_name[i] = ascii_to_internal(first_name[i]); + first_name[i] = charmap->mapped_character(first_name[i]); } + ///////////////// end of conversion if( parent_capacity == 0 ) { @@ -427,7 +463,8 @@ level_88_helper(size_t parent_capacity, nbuild += first_name_length; } } - returned_size = sprintf(retval, "%zdA", nbuild); + returned_size = sprintf(retval, HOST_SIZE_T_PRINT_DEC "A", + (fmt_size_t)nbuild); memcpy(retval + returned_size, builder, nbuild); returned_size += nbuild; free(first_name); @@ -465,7 +502,7 @@ get_level_88_domain(size_t parent_capacity, cbl_field_t *var, size_t &returned_s // Numerics are converted to strings, and handled as above size_t retval_capacity = 64; - char *retval = (char *)xmalloc(retval_capacity); + char *retval = static_cast<char *>(xmalloc(retval_capacity)); size_t output_index = 0; // Loop through the provided domains: @@ -478,30 +515,46 @@ get_level_88_domain(size_t parent_capacity, cbl_field_t *var, size_t &returned_s char *stream; // Do the first element of the domain - stream = level_88_helper(parent_capacity, domain->first, stream_len); + stream = level_88_helper( parent_capacity, + domain->first, + stream_len, + var->codeset.encoding); if( output_index + stream_len > retval_capacity ) { retval_capacity *= 2; - retval = (char *)xrealloc(retval, retval_capacity); + retval = static_cast<char *>(xrealloc(retval, retval_capacity)); } + gcc_assert(retval); memcpy(retval + output_index, stream, stream_len); output_index += stream_len; returned_size += stream_len; free(stream); // Do the second element of the domain - stream = level_88_helper(parent_capacity, domain->last, stream_len); + stream = level_88_helper( parent_capacity, + domain->last, + stream_len, + var->codeset.encoding); if( output_index + stream_len > retval_capacity ) { retval_capacity *= 2; - retval = (char *)xrealloc(retval, retval_capacity); + retval = static_cast<char *>(xrealloc(retval, retval_capacity)); } + gcc_assert(retval); memcpy(retval + output_index, stream, stream_len); output_index += stream_len; returned_size += stream_len; free(stream); domain += 1; } + + if( returned_size >= retval_capacity) + { + retval_capacity *= 2; + retval = static_cast<char *>(xrealloc(retval, retval_capacity)); + } + + gcc_assert(returned_size < retval_capacity); retval[returned_size++] = '\0'; return retval; } @@ -554,21 +607,15 @@ get_class_condition_string(cbl_field_t *var) uint8_t value1; uint8_t value2; - char achFirstName[256]; - char achLastName[256]; - size_t first_name_length = domain->first.size() ? domain->first.size() : strlen(domain->first.name()); - size_t last_name_length = domain->last.size() - ? domain->last.size() - : strlen(domain->last.name()); if( domain->first.is_numeric ) { if( strlen(ach) > sizeof(ach) - 1000 ) { - cbl_internal_error("Nice try, but you can't fire me. I quit!"); + cbl_internal_error("Nice try, but you cannot fire me."); } // We are working with unquoted strings that contain the values 1 through @@ -592,26 +639,11 @@ get_class_condition_string(cbl_field_t *var) { // Since the first.name is a single character, we can do this as // a single-character pair. + uint8_t ch1; + uint8_t ch2; - // Keep in mind that the single character might be a two-byte UTF-8 - // codepoint - uint8_t ch1 = domain->first.name()[0]; - uint8_t ch2 = domain->last.name()[0]; - - gcc_assert(first_name_length <= 2); - gcc_assert(last_name_length <= 2); - - char *p2; - size_t one; - p2 = achFirstName; - one = 8; - raw_to_internal(&p2, &one, domain->last.name(), last_name_length); - ch2 = achFirstName[0]; - - p2 = achLastName; - one = 8; - raw_to_internal(&p2, &one, domain->first.name(), first_name_length); - ch1 = achLastName[0]; + ch2 = domain->last.name()[0]; + ch1 = domain->first.name()[0]; if( ch1 < ch2 ) { @@ -638,15 +670,12 @@ get_class_condition_string(cbl_field_t *var) // We are working with a string larger than 1 character. The COBOL // spec says there can't be a THROUGH, so we ignore the last.name: - char *p2; - size_t one; - p2 = achFirstName; - one = 8; - raw_to_internal(&p2, &one, domain->last.name(), last_name_length); - + // size_t first_name_length = domain->first.size() + // ? domain->first.size() + // : strlen(domain->first.name()); for(size_t i=0; i<first_name_length; i++) { - p += sprintf(p, "%2.2X ", (unsigned char)achFirstName[i]); + p += sprintf(p, "%2.2X ", (unsigned char)domain->first.name()[i]); } } domain += 1; @@ -691,30 +720,42 @@ struct called_tree_t { tree node; public: - match_tree( tree node ) : node(node) {} + explicit match_tree( tree node ) : node(node) {} bool operator()( const called_tree_t& that ) const { return this->node == that.node; } }; }; -static std::map<program_reference_t, std::list<called_tree_t> > call_targets; +static std::map<program_reference_t, std::list<tree> > call_targets; static std::map<tree, cbl_call_convention_t> called_targets; -static void -parser_call_target( tree func ) +static +void +set_call_convention(tree function_decl, cbl_call_convention_t convention) { - cbl_call_convention_t convention = current_call_convention(); - const char *name = IDENTIFIER_POINTER( DECL_NAME(func) ); - program_reference_t key(current_program_index(), name); - - // Each func is unique and inserted only once. - assert( called_targets.find(func) == called_targets.end() ); - called_targets[func] = convention; + called_targets[function_decl] = convention; + } - called_tree_t value(func, convention); - auto& p = call_targets[key]; - p.push_back(value); +static +void +parser_call_target( const char *name, tree call_expr ) + { + /* This routine gets called when parser_call() has been invoked with a + literal target. That target is a COBOL name like "prog_2". However, + there is the case when "prog_2" is a forward reference to a contained + program nested inside "prog_1". In that case, the actual definition + of "prog_2" will end up with a name like "prog_2.62", and eventually + the target of the call will have to be modified from "prog_2" to + "prog_2.62". + + We save the call expression for this call, and then we update it later, + after we know whether or not it was a forward reference to a local + function. */ + + program_reference_t key(current_program_index(), name); + auto& p = call_targets[key]; + p.push_back(call_expr); } /* @@ -726,28 +767,37 @@ parser_call_target( tree func ) cbl_call_convention_t parser_call_target_convention( tree func ) { - auto p = called_targets.find(func); - if( p != called_targets.end() ) return p->second; + auto p = called_targets.find(func); + if( p != called_targets.end() ) + { + // This was found in our list of call targets + return p->second; + } - return cbl_call_cobol_e; + return cbl_call_cobol_e; } void parser_call_targets_dump() { - dbgmsg( "call targets for #%zu", current_program_index() ); + dbgmsg( "call targets for #" HOST_SIZE_T_PRINT_UNSIGNED " NOT dumping", + (fmt_size_t)current_program_index() ); +#if 0 // A change to call_targets rendered this routine useless. Until we get + // around to repairing it, this code is left for reference. for( const auto& elem : call_targets ) { const auto& k = elem.first; const auto& v = elem.second; - fprintf(stderr, "\t#%-3zu %s calls %s ", - k.caller, cbl_label_of(symbol_at(k.caller))->name, k.called); + fprintf(stderr, "\t#%-3" GCC_PRISZ "u %s calls %s ", + (fmt_size_t)k.caller, cbl_label_of(symbol_at(k.caller))->name, + k.called); char ch = '['; for( auto func : v ) { - fprintf( stderr, "%c %s", ch, IDENTIFIER_POINTER(DECL_NAME(func.node)) ); + fprintf( stderr, "%c %s", ch, IDENTIFIER_POINTER(DECL_NAME(func)) ); ch = ','; } fprintf(stderr, " ]\n"); } +#endif } size_t @@ -755,21 +805,28 @@ parser_call_target_update( size_t caller, const char plain_name[], const char mangled_name[] ) { - auto key = program_reference_t(caller, plain_name); - auto p = call_targets.find(key); - if( p == call_targets.end() ) return 0; + auto key = program_reference_t(caller, plain_name); + auto p = call_targets.find(key); + if( p == call_targets.end() ) return 0; - for( auto func : p->second ) - { - func.convention = cbl_call_verbatim_e; - DECL_NAME(func.node) = get_identifier(mangled_name); - } - return p->second.size(); + for( auto call_expr : p->second ) + { + tree fndecl_type = build_varargs_function_type_array( COBOL_FUNCTION_RETURN_TYPE, + 0, // No parameters yet + NULL); // And, hence, no types + + // Fetch the FUNCTION_DECL for that FUNCTION_TYPE + tree function_decl = gg_build_fn_decl(mangled_name, fndecl_type); + tree function_address = gg_get_address_of(function_decl); + + TREE_OPERAND(call_expr, 1) = function_address; + } + return p->second.size(); } static tree -function_handle_from_name(cbl_refer_t &name, - tree function_return_type) +function_pointer_from_name(const cbl_refer_t &name, + tree function_return_type) { Analyze(); @@ -777,74 +834,80 @@ function_handle_from_name(cbl_refer_t &name, function_return_type, 0, NULL); - tree function_pointer = build_pointer_type(function_type); - tree function_handle = gg_define_variable(function_pointer, "..function_handle.1", vs_stack); - + tree function_pointer_type = build_pointer_type(function_type); + tree function_pointer = gg_define_variable(function_pointer_type, + "..function_pointer.1", + vs_stack); if( name.field->type == FldPointer ) { // If the parameter is a pointer, just pick up the value and head for the // exit if( refer_is_clean(name) ) { - gg_memcpy(gg_get_address_of(function_handle), + gg_memcpy(gg_get_address_of(function_pointer), member(name.field->var_decl_node, "data"), - build_int_cst_type(SIZE_T, sizeof(void *))); + sizeof_pointer); } else { - gg_memcpy(gg_get_address_of(function_handle), - qualified_data_source(name), - build_int_cst_type(SIZE_T, sizeof(void *))); + gg_memcpy(gg_get_address_of(function_pointer), + qualified_data_location(name), + sizeof_pointer); } - return function_handle; + return function_pointer; } else if( use_static_call() && is_literal(name.field) ) { - // It's a literal, and we are using static calls. Generate the CALL, and - // pass the address expression to parser_call_target(). That will cause - // parser_call_target_update() to replace any nested CALL "foo" with the - // local "foo.60" name. - - // We create a reference to it, which is later resolved by the linker. - tree addr_expr = gg_get_function_address( function_return_type, - name.field->data.initial); - gg_assign(function_handle, addr_expr); - - tree func = TREE_OPERAND(addr_expr, 0); - parser_call_target(func); // add function to list of call targets + tree fndecl_type = build_varargs_function_type_array( function_return_type, + 0, // No parameters yet + NULL); // And, hence, no types + + // Fetch the FUNCTION_DECL for that FUNCTION_TYPE + char *tname = static_cast<char *>(xmalloc(name.field->data.capacity+1)); + memcpy(tname, name.field->data.original(), name.field->data.capacity); + tname[name.field->data.capacity] = '\0'; + tree function_decl = gg_build_fn_decl(tname, + fndecl_type); + free(tname); + // Take the address of the function decl: + tree address_of_function = gg_get_address_of(function_decl); + gg_assign(function_pointer, address_of_function); } else { - // This is not a literal or static + // We are not using static calls. if( name.field->type == FldLiteralA ) { - gg_assign(function_handle, + gg_assign(function_pointer, gg_cast(build_pointer_type(function_type), - gg_call_expr(VOID_P, - "__gg__function_handle_from_literal", - build_int_cst_type(INT, current_function->our_symbol_table_index), - gg_string_literal(name.field->data.initial), - NULL_TREE))); + gg_call_expr( VOID_P, + "__gg__function_handle_from_literal", + build_int_cst_type(INT, + current_function->our_symbol_table_index), + gg_string_literal(name.field->data.original()), + NULL_TREE))); } else { - gg_assign(function_handle, + gg_assign(function_pointer, gg_cast(build_pointer_type(function_type), gg_call_expr( VOID_P, - "__gg__function_handle_from_name", - build_int_cst_type(INT, current_function->our_symbol_table_index), - gg_get_address_of(name.field->var_decl_node), - refer_offset_source(name), - refer_size_source( name), - NULL_TREE))); + "__gg__function_handle_from_name", + build_int_cst_type(INT, + current_function->our_symbol_table_index), + gg_get_address_of(name.field->var_decl_node), + refer_offset(name), + refer_size_source( name), + NULL_TREE))); } } - return function_handle; + return function_pointer; } void -parser_initialize_programs(size_t nprogs, struct cbl_refer_t *progs) +parser_initialize_programs( size_t nprogs, + const struct cbl_refer_t *progs) { Analyze(); SHOW_PARSE @@ -860,7 +923,7 @@ parser_initialize_programs(size_t nprogs, struct cbl_refer_t *progs) if( progs[i].field->type == FldLiteralA ) { SHOW_PARSE_TEXT("\"") - SHOW_PARSE_TEXT(progs[i].field->data.initial) + SHOW_PARSE_TEXT(progs[i].field->data.original()) SHOW_PARSE_TEXT("\"") } else @@ -874,40 +937,272 @@ parser_initialize_programs(size_t nprogs, struct cbl_refer_t *progs) for( size_t i=0; i<nprogs; i++ ) { - tree function_handle = function_handle_from_name( progs[i], - COBOL_FUNCTION_RETURN_TYPE); + tree function_pointer = function_pointer_from_name( progs[i], + COBOL_FUNCTION_RETURN_TYPE); gg_call(VOID, "__gg__to_be_canceled", - gg_cast(SIZE_T, function_handle), + gg_cast(SIZE_T, function_pointer), NULL_TREE); } } -void parser_statement_begin() +static +tree +array_of_long_long(const char *name, const std::vector<uint64_t>& vals) + { + // We need to create a file-static static array of 64-bit integers: + tree array_of_ulonglong_type = build_array_type_nelts(ULONGLONG, vals.size()+1); + tree array_of_ulonglong = gg_define_variable( array_of_ulonglong_type, + name, + vs_file_static); + // We have the array. Now we need to build the constructor for it + tree constr = make_node(CONSTRUCTOR); + TREE_TYPE(constr) = array_of_ulonglong_type; + TREE_STATIC(constr) = 1; + TREE_CONSTANT(constr) = 1; + + // The first element of the array contains the number of elements to follow + CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(constr), + build_int_cst_type(SIZE_T, 0), + build_int_cst_type(ULONGLONG, vals.size()) ); + for(size_t i=0; i<vals.size(); i++) + { + CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(constr), + build_int_cst_type(SIZE_T, i+1), + build_int_cst_type(ULONGLONG, vals[i]) ); + } + DECL_INITIAL(array_of_ulonglong) = constr; + return array_of_ulonglong; + } + +/* + * As ECs are enabled and disabled with >>TURN, the compiler updates its list + * of enabled ECs (and any files they apply to). It encodes this list as an + * array of integers. parser_compile_ecs converts that array as a static + * compile-time vector, which it returns to the compiler. + * + * Before each statement, the compiler determines what possible EC handling the + * program can do. If there's an overlap between potential ECs and + * Declaratives, it passes the current pair of static arrays to + * parser_statement_begin(), which installs them, for that statement, in the + * library. + * + * After each statement, to effect EC handling, the statement epilog calls uses + * parser_match_exception to invoke __gg_match_exception(), which returns the + * symbol table index of the matched Declarative, if any. That "ladder" + * Performs the matched declarative, and execution continues with the next + * statement. + */ +tree +parser_compile_ecs( const std::vector<uint64_t>& ecs ) + { + if( ecs.empty() ) + { + SHOW_IF_PARSE(nullptr) + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT("ecs is empty"); + SHOW_PARSE_END + } + return NULL_TREE; + } + + char ach[64]; + static int counter = 1; + sprintf(ach, "_ecs_table_%d", counter++); + tree retval = array_of_long_long(ach, ecs); + SHOW_IF_PARSE(nullptr) + { + SHOW_PARSE_HEADER + snprintf(ach, sizeof(ach), " Size is %lu; retval is %p", + gb4(ecs.size()), as_voidp(retval)); + SHOW_PARSE_TEXT(ach) + SHOW_PARSE_END + } + TRACE1 + { + TRACE1_HEADER + snprintf(ach, sizeof(ach), " Size is %lu; retval is %p", + gb4(ecs.size()), as_voidp(retval)); + TRACE1_TEXT_ABC("", ach, ""); + TRACE1_END + } + return retval; + } + +/* + * At the beginning of Procedure Division, we may encounter DECLARATIVES + * SECTION. If so, the compiler composes a list of zero or more Declaratives + * as cbl_declarative_t, representing the USE statement of each + * Declarative. These are encoded as an array of integers, which are returned + * to the compiler for use by parser_statement_begin(). Although the list of + * declaratives never changes for a program, CALL may change which program is + * invoked, and thus the set of active Declaratives. By passing them for each + * statement, code generation is relieved of referring to global variable. + */ +tree +parser_compile_dcls( const std::vector<uint64_t>& dcls ) + { + if( dcls.empty() ) + { + SHOW_IF_PARSE(nullptr) + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT("dcls is empty"); + SHOW_PARSE_END + } + return NULL_TREE; + } + + char ach[64]; + static int counter = 1; + sprintf(ach, "_dcls_table_%d", counter++); + tree retval = array_of_long_long(ach, dcls); + SHOW_IF_PARSE(nullptr) + { + SHOW_PARSE_HEADER + snprintf(ach, sizeof(ach), " Size is %lu; retval is %p", + gb4(dcls.size()), as_voidp(retval)); + SHOW_PARSE_TEXT(ach); + SHOW_PARSE_END + } + TRACE1 + { + TRACE1_HEADER + snprintf(ach, sizeof(ach), " Size is %lu; retval is %p", + gb4(dcls.size()), as_voidp(retval)); + TRACE1_TEXT_ABC("", ach, ""); + TRACE1_END + } + return retval; + } + +static void +store_location_stuff(const cbl_name_t statement_name) + { + if( exception_location_active && !current_declarative_section_name() ) + { + // We need to establish some stuff for EXCEPTION- function processing + + gg_assign(var_decl_exception_program_id, + gg_string_literal(current_function->our_unmangled_name)); + + if( strstr(current_function->current_section->label->name, "_implicit") + != current_function->current_section->label->name ) + { + gg_assign(var_decl_exception_section, + gg_string_literal(current_function->current_section->label->name)); + } + else + { + gg_assign(var_decl_exception_section, + gg_cast(build_pointer_type(CHAR_P),null_pointer_node)); + } + + if( strstr(current_function->current_paragraph->label->name, "_implicit") + != current_function->current_paragraph->label->name ) + { + gg_assign(var_decl_exception_paragraph, + gg_string_literal(current_function->current_paragraph->label->name)); + } + else + { + gg_assign(var_decl_exception_paragraph, + gg_cast(build_pointer_type(CHAR_P), null_pointer_node)); + } + + gg_assign(var_decl_exception_source_file, + gg_string_literal(current_filename.back().c_str())); + gg_assign(var_decl_exception_line_number, build_int_cst_type(INT, + CURRENT_LINE_NUMBER)); + gg_assign(var_decl_exception_statement, gg_string_literal(statement_name)); + } + } + +static +void +set_exception_environment( tree ecs, tree dcls ) + { + gg_call(VOID, + "__gg__set_exception_environment", + ecs ? gg_get_address_of(ecs) : null_pointer_node, + dcls ? gg_get_address_of(dcls) : null_pointer_node, + NULL_TREE); + } + +void +parser_statement_begin( const cbl_name_t statement_name, + tree ecs, + tree dcls ) { SHOW_PARSE { SHOW_PARSE_HEADER char ach[64]; - snprintf (ach, sizeof(ach), + snprintf( ach, sizeof(ach), " yylineno %d first/last %d/%d", yylineno, cobol_location().first_line, cobol_location().last_line ); SHOW_PARSE_TEXT(ach); + if( true || ecs || dcls ) + { + SHOW_PARSE_INDENT + snprintf( ach, sizeof(ach), + "Sending ecs/dcls %p / %p", as_voidp(ecs), as_voidp(dcls)); + SHOW_PARSE_TEXT(ach); + } SHOW_PARSE_END } + TRACE1 + { + TRACE1_HEADER + char ach[64]; + snprintf(ach, sizeof(ach), " ecs/dcls %p / %p", as_voidp(ecs), as_voidp(dcls)); + TRACE1_TEXT_ABC("", ach, ""); + TRACE1_END + } + + gcc_assert( gg_trans_unit.function_stack.size() ); + // In the cases where enabled_exceptions.size() is non-zero, or when + // there is a possibility of an EC-I-O exception because this is a file + // operation, we need to store the location information and do the exception + // overhead: - if( gg_get_current_line_number() == DEFAULT_LINE_NUMBER ) + static const std::set<std::string> file_ops = { - // This code is prevents anomolies when the first line of a program is - // a PERFORM <proc> ... TEST AFTER ... UNTIL ... - gg_set_current_line_number(CURRENT_LINE_NUMBER-1); - gg_assign(var_decl_nop, build_int_cst_type(INT, 106)); + "OPEN", + "CLOSE", + "READ", + "WRITE", + "DELETE", + "REWRITE", + "START", + }; + + // Performance note: By doing exception processing only when necessary + // the execution time of a program doing two-billion simple adds in an inner + // loop dropped from 3.8 seconds to 0.175 seconds. + + bool exception_processing = cdf_enabled_exceptions().size() ; + + if( !exception_processing ) + { + exception_processing = file_ops.find(statement_name) != file_ops.end(); } - gg_set_current_line_number(CURRENT_LINE_NUMBER); + // At this point, if any exception is enabled, we store the location stuff. + // Each file I-O routine calls store_location_stuff explicitly, because + // those exceptions can't be defeated. + + if( exception_processing ) + { + store_location_stuff(statement_name); + set_exception_environment(ecs, dcls); + } + + sv_is_i_o = false; } static void @@ -920,10 +1215,9 @@ initialize_variable_internal( cbl_refer_t refer, // gg_string_literal(refer.field->name), // NULL_TREE); cbl_field_t *parsed_var = refer.field; - - if( parsed_var->type == FldLiteralA ) + if( !parsed_var ) { - return; + cbl_internal_error("%s should not be null", "parsed_var"); } if( parsed_var->is_key_name() ) @@ -934,12 +1228,7 @@ initialize_variable_internal( cbl_refer_t refer, return; } - if( is_register_field( parsed_var) ) - { - return; - } - - if( parsed_var && parsed_var->type == FldBlob ) + if( parsed_var->attr & register_e ) { return; } @@ -1043,7 +1332,9 @@ initialize_variable_internal( cbl_refer_t refer, default: { char ach[128]; - strfromf128(ach, sizeof(ach), "%.16E", parsed_var->data.value_of()); + real_to_decimal (ach, + TREE_REAL_CST_PTR (parsed_var->data.value_of()), + sizeof(ach), 16, 0); SHOW_PARSE_TEXT(ach); break; } @@ -1055,15 +1346,13 @@ initialize_variable_internal( cbl_refer_t refer, SHOW_PARSE_END } - CHECK_FIELD(parsed_var); - // When initializing a variable, we have to ignore any DEPENDING ON clause // that might otherwise apply suppress_dest_depends = true; bool is_redefined = false; - cbl_field_t *family_tree = parsed_var; + const cbl_field_t *family_tree = parsed_var; while(family_tree) { if( symbol_redefines(family_tree) ) @@ -1084,7 +1373,7 @@ initialize_variable_internal( cbl_refer_t refer, if( parsed_var->data.initial ) { bool a_parent_initialized = false; - cbl_field_t *parent = parent_of(parsed_var); + const cbl_field_t *parent = parent_of(parsed_var); while( parent ) { if( parent->attr & has_value_e ) @@ -1114,7 +1403,7 @@ initialize_variable_internal( cbl_refer_t refer, flag_bits |= wsclear() ? DEFAULTBYTE_BIT + (*wsclear() & DEFAULT_BYTE_MASK) : 0; - flag_bits |= (refer.nsubscript << NSUBSCRIPT_SHIFT) & NSUBSCRIPT_MASK; + flag_bits |= (refer.nsubscript() << NSUBSCRIPT_SHIFT) & NSUBSCRIPT_MASK; flag_bits |= just_once ? JUST_ONCE_BIT : 0 ; suppress_dest_depends = false; // Set this to false so that refer_is_clean is valid @@ -1125,7 +1414,7 @@ initialize_variable_internal( cbl_refer_t refer, gg_call(VOID, "__gg__initialize_variable", gg_get_address_of(refer.field->var_decl_node), - refer_offset_dest(refer), + refer_offset(refer), build_int_cst_type(INT, flag_bits), NULL_TREE); } @@ -1227,7 +1516,40 @@ initialize_variable_internal( cbl_refer_t refer, } else { - TRACE1_FIELD_VALUE("", parsed_var, "") + // Convert strings of spaces to "<SPACES>" + tree spaces = gg_define_int(0); + if( parsed_var->type == FldGroup + || parsed_var->type == FldAlphanumeric + || parsed_var->type == FldAlphaEdited + || parsed_var->type == FldLiteralA ) + { + gg_assign(spaces, integer_one_node); + tree counter = gg_define_int(parsed_var->data.capacity); + WHILE(counter, gt_op, integer_zero_node) + { + gg_decrement(counter); + IF( gg_indirect(member(parsed_var->var_decl_node, "data"), counter), + ne_op, + build_int_cst_type(UCHAR, ' ') ) + { + gg_assign(spaces, integer_zero_node); + } + ELSE + { + } + ENDIF + } + WEND + } + IF(spaces, eq_op, integer_one_node) + { + TRACE1_TEXT(" <SPACES>") + } + ELSE + { + TRACE1_FIELD_VALUE("", parsed_var, "") + } + ENDIF } TRACE1_END } @@ -1246,7 +1568,7 @@ initialize_variable_internal( cbl_refer_t refer, // } void -parser_initialize(cbl_refer_t refer, bool like_parser_symbol_add) +parser_initialize(const cbl_refer_t& refer, bool like_parser_symbol_add) { //gg_printf("parser_initialize %s\n", gg_string_literal(refer.field->name), NULL_TREE); if( like_parser_symbol_add ) @@ -1263,7 +1585,7 @@ parser_initialize(cbl_refer_t refer, bool like_parser_symbol_add) static void get_binary_value_from_float(tree value, - cbl_refer_t &dest, + const cbl_refer_t &dest, cbl_field_t *source, tree source_offset ) @@ -1298,8 +1620,8 @@ get_binary_value_from_float(tree value, gg_assign(fvalue, gg_multiply(fvalue, gg_float(ftype, - build_int_cst_type(INT, - get_power_of_ten(rdigits))))); + wide_int_to_tree(INT, + get_power_of_ten(rdigits))))); // And we need to throw away any digits to the left of the leftmost digits: // At least, we need to do so in principl. I am deferring this problem until @@ -1337,48 +1659,29 @@ gg_attribute_bit_set(struct cbl_field_t *var, cbl_field_attr_t bits) } #pragma GCC diagnostic pop -static void -gg_default_qualification(struct cbl_field_t * /*var*/) - { -// gg_attribute_bit_clear(var, refmod_e); - } - -static void -gg_get_depending_on_value(tree depending_on, cbl_field_t *current_sizer) +static +void +depending_on_value(tree depending_on, cbl_field_t *current_sizer) { // We have to deal with the possibility of a DEPENDING_ON variable, // and we have to apply array bounds whether or not there is a DEPENDING_ON // variable: - tree occurs_lower = gg_define_variable(LONG, "_lower"); - tree occurs_upper = gg_define_variable(LONG, "_upper"); - - gg_assign(occurs_lower, build_int_cst_type(LONG, current_sizer->occurs.bounds.lower)); - gg_assign(occurs_upper, build_int_cst_type(LONG, current_sizer->occurs.bounds.upper)); +// tree occurs_lower = gg_define_variable(LONG, "_lower"); +// tree occurs_upper = gg_define_variable(LONG, "_upper"); +// +// gg_assign(occurs_lower, build_int_cst_type(LONG, current_sizer->occurs.bounds.lower)); +// gg_assign(occurs_upper, build_int_cst_type(LONG, current_sizer->occurs.bounds.upper)); + gcc_assert(current_sizer); if( current_sizer->occurs.depending_on ) { - // Get the current value of the depending_on data-item: - tree value = gg_define_int128(); - get_binary_value( value, - NULL, - cbl_field_of(symbol_at(current_sizer->occurs.depending_on)), - size_t_zero_node); - gg_assign(depending_on, gg_cast(LONG, value)); - IF( depending_on, lt_op, occurs_lower ) - // depending_is can be no less than occurs_lower: - gg_assign(depending_on, occurs_lower ); - ELSE - ENDIF - IF( depending_on, gt_op, occurs_upper ) - // depending_is can be no greater than occurs_upper: - gg_assign(depending_on, occurs_upper ); - ELSE - ENDIF + get_depending_on_value_from_odo(depending_on, current_sizer); } else { - gg_assign(depending_on, occurs_upper); + gg_assign(depending_on, + build_int_cst_type(LONG, current_sizer->occurs.bounds.upper)); } } @@ -1481,7 +1784,7 @@ get_bytes_needed(cbl_field_t *field) } default: - cbl_internal_error("%s(): Knows not the variable type %s for %s", + cbl_internal_error("%s: Knows not the variable type %s for %s", __func__, cbl_field_type_str(field->type), field->name ); @@ -1514,16 +1817,12 @@ normal_normal_compare(bool debugging, NULL_TREE); } - bool needs_adjusting; if( !left_intermediate && !right_intermediate ) { // Yay! Both sides have fixed rdigit values. - // Flag needs_adjusting as false, because we are going to do it here: - needs_adjusting = false; int adjust = get_scaled_rdigits(left_side_ref->field) - get_scaled_rdigits(right_side_ref->field); - if( adjust > 0 ) { // We need to make right_side bigger to match the scale of left_side @@ -1538,6 +1837,7 @@ normal_normal_compare(bool debugging, else { // At least one side is right_intermediate + bool needs_adjusting; tree adjust; if( !left_intermediate && right_intermediate ) @@ -1774,8 +2074,8 @@ compare_binary_binary(tree return_int, { gg_printf("compare_binary_binary(): using int64\n", NULL_TREE); } - left_side = gg_define_variable( left_side_ref->field->attr & signable_e ? LONG : ULONG ); - right_side = gg_define_variable(right_side_ref->field->attr & signable_e ? LONG : ULONG ); + left_side = gg_define_variable( left_side_ref->field->has_attr(signable_e) ? LONG : ULONG ); + right_side = gg_define_variable(right_side_ref->field->has_attr(signable_e) ? LONG : ULONG ); } //tree dummy = gg_define_int(); @@ -1785,13 +2085,14 @@ compare_binary_binary(tree return_int, get_binary_value(left_side, NULL, left_side_ref->field, - refer_offset_source(*left_side_ref), + refer_offset(*left_side_ref), hilo_left); get_binary_value(right_side, NULL, right_side_ref->field, - refer_offset_source(*right_side_ref), + refer_offset(*right_side_ref), hilo_right); + IF( hilo_left, eq_op, integer_one_node ) { // left side is hi-value @@ -1948,23 +2249,27 @@ cobol_compare( tree return_int, case FldLiteralA: { // Comparing a FldLiteralN to an alphanumeric - // It is the case that data.initial is in the original form seen - // in the source code, which means that even in EBCDIC mode the - // characters are in the "ASCII" state. - - static size_t buffer_size = 0; - static char *buffer = NULL; - raw_to_internal(&buffer, - &buffer_size, - lefty->field->data.initial, - strlen(lefty->field->data.initial)); + // This next conversion may be overkill. But just in case + // the encodings of the two variables are different, we are + // going to convert left-side text to the right-side encoding + cbl_encoding_t enc_left = lefty->field->codeset.encoding; + cbl_encoding_t enc_right = righty->field->codeset.encoding; + size_t outlength; + size_t inlength = strlen(lefty->field->data.initial); + char *converted = __gg__iconverter( + enc_left, + enc_right, + lefty->field->data.initial, + inlength, + &outlength ); gg_assign( return_int, gg_call_expr( INT, "__gg__literaln_alpha_compare", - gg_string_literal(buffer), + build_string_literal(strlen(lefty->field->data.initial)+1, + converted), gg_get_address_of(righty->field->var_decl_node), - refer_offset_source(*righty), + refer_offset(*righty), refer_size_source( *righty), build_int_cst_type(INT, (righty->all ? REFER_T_MOVE_ALL : 0)), @@ -2027,34 +2332,38 @@ cobol_compare( tree return_int, { // None of our explicit comparisons up above worked, so we revert to the // general case: - int leftflags = (left_side_ref.all ? REFER_T_MOVE_ALL : 0) - + (left_side_ref.addr_of ? REFER_T_ADDRESS_OF : 0); - int rightflags = (right_side_ref.all ? REFER_T_MOVE_ALL : 0) - + (right_side_ref.addr_of ? REFER_T_ADDRESS_OF : 0); + int leftflags = (left_side_ref.all ? REFER_T_MOVE_ALL : 0) + + (left_side_ref.addr_of ? REFER_T_ADDRESS_OF : 0) + + (left_side_ref.refmod.from ? REFER_T_REFMOD : 0); + int rightflags = (right_side_ref.all ? REFER_T_MOVE_ALL : 0) + + (right_side_ref.addr_of ? REFER_T_ADDRESS_OF : 0) + + (right_side_ref.refmod.from ? REFER_T_REFMOD : 0); gg_assign( return_int, gg_call_expr( INT, "__gg__compare", gg_get_address_of(left_side_ref.field->var_decl_node), - refer_offset_source(left_side_ref), + refer_offset(left_side_ref), refer_size_source( left_side_ref), build_int_cst_type(INT, leftflags), gg_get_address_of(right_side_ref.field->var_decl_node), - refer_offset_source(right_side_ref), + refer_offset(right_side_ref), refer_size_source( right_side_ref), build_int_cst_type(INT, rightflags), integer_zero_node, NULL_TREE)); + // compared = true; // Commented out to quiet cppcheck } - -// gg_printf(" result is %d\n", return_int, NULL_TREE); } static void move_tree( cbl_field_t *dest, tree offset, - tree psz_source, - tree length_bump=integer_zero_node) // psz_source is a null-terminated string + tree psz_source, // psz_source is a null-terminated string + tree length_bump=integer_zero_node) { + // This routine assumes that the psz_source is in the same codeset as the + // dest. + Analyze(); SHOW_PARSE { @@ -2063,6 +2372,8 @@ move_tree( cbl_field_t *dest, SHOW_PARSE_END } + CHECK_FIELD(dest); + bool moved = true; tree source_length = gg_define_size_t(); @@ -2097,15 +2408,20 @@ move_tree( cbl_field_t *dest, { case FldGroup: case FldAlphanumeric: + { // Space out the alphanumeric destination: + charmap_t *charmap = __gg__get_charmap(dest->codeset.encoding); + gg_memset( location, - build_int_cst_type(INT, internal_space), + build_int_cst_type(INT, + charmap->mapped_character(ascii_space)), length ); // Copy the alphanumeric result over. gg_memcpy( location, psz_source, min_length ); break; + } case FldNumericDisplay: case FldNumericEdited: @@ -2119,8 +2435,9 @@ move_tree( cbl_field_t *dest, gg_assign(value, gg_call_expr( INT128, - "__gg__dirty_to_binary_internal", + "__gg__dirty_to_binary", psz_source, + build_int_cst_type(INT, dest->codeset.encoding), source_length, gg_get_address_of(rdigits), NULL_TREE)); @@ -2141,12 +2458,13 @@ move_tree( cbl_field_t *dest, case FldAlphaEdited: { gg_call(VOID, - "__gg__string_to_alpha_edited_ascii", + "__gg__string_to_alpha_edited", location, + build_int_cst_type(INT, DEFAULT_SOURCE_ENCODING), psz_source, min_length, member(dest->var_decl_node, "picture"), - NULL); + NULL_TREE); break; } @@ -2167,10 +2485,10 @@ move_tree( cbl_field_t *dest, if( !moved ) { - dbgmsg("###### %10s in %s:%d\n", __func__, __FILE__, __LINE__ ); - cbl_internal_error( "I don't know how to MOVE an alphabetical string to %s(%s) \n", - cbl_field_type_str(dest->type), - dest->name + dbgmsg("%10s in %s:%d", __func__, __FILE__, __LINE__ ); + cbl_internal_error( "I don%'t know how to MOVE an alphabetical string to %s(%s)", + cbl_field_type_str(dest->type), + dest->name ); return; } @@ -2179,6 +2497,7 @@ move_tree( cbl_field_t *dest, static void move_tree_to_field(cbl_field_t *field, tree psz) { + // psz has to be in the same encoding as field move_tree(field, integer_zero_node, psz); } @@ -2201,7 +2520,7 @@ get_string_from(cbl_field_t *field) gg_cast(CHAR_P, gg_malloc(build_int_cst_type(SIZE_T, field->data.capacity+1)))); - char *litstring = get_literal_string(field); + const char *litstring = get_literal_string(field); gg_memcpy(psz, gg_string_literal(litstring), build_int_cst_type(SIZE_T, field->data.capacity+1)); @@ -2236,7 +2555,7 @@ get_string_from(cbl_field_t *field) default: cbl_internal_error( - "%s(): field->type %s must be literal or alphanumeric", + "%s: %<field->type%> %s must be literal or alphanumeric", __func__, cbl_field_type_str(field->type)); break; } @@ -2249,12 +2568,12 @@ get_string_from(cbl_field_t *field) } static char * -combined_name(cbl_label_t *label) +combined_name(const cbl_label_t *label) { // This routine returns a pointer to a static, so make sure you use the result // before calling the routine again - char *para_name = nullptr; - char *sect_name = nullptr; + const char *para_name = nullptr; + const char *sect_name = nullptr; const char *program_name = current_function->our_unmangled_name; if( label->type == LblParagraph ) @@ -2264,7 +2583,7 @@ combined_name(cbl_label_t *label) if( label->parent ) { // It's possible for implicit - cbl_label_t *section_label = cbl_label_of(symbol_at(label->parent)); + const cbl_label_t *section_label = cbl_label_of(symbol_at(label->parent)); sect_name = section_label->name; } } @@ -2274,10 +2593,10 @@ combined_name(cbl_label_t *label) } static size_t retval_size = 256; - static char *retval= (char *)xmalloc(retval_size); + static char *retval= static_cast<char *>(xmalloc(retval_size)); char *paragraph = cobol_name_mangler(para_name); - char *section = cobol_name_mangler(sect_name); + char *section = cobol_name_mangler(sect_name); char *mangled_program_name = cobol_name_mangler(program_name); while( retval_size < (paragraph ? strlen(paragraph) : 0 ) @@ -2286,8 +2605,9 @@ combined_name(cbl_label_t *label) + 24 ) { retval_size *= 2; - retval = (char *)xrealloc(retval, retval_size); + retval = static_cast<char *>(xrealloc(retval, retval_size)); } + gcc_assert(retval); *retval = '\0'; char ach[24]; @@ -2305,9 +2625,11 @@ combined_name(cbl_label_t *label) { strcat(retval, mangled_program_name); } - sprintf(ach, ".%ld", current_function->program_id_number); + sprintf(ach, "." HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)current_function->program_id_number); strcat(retval, ach); - sprintf(ach, ".%ld", symbol_label_id(label)); + sprintf(ach, "." HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)symbol_label_id(label)); strcat(retval, ach); free(mangled_program_name); free(section); @@ -2332,8 +2654,9 @@ assembler_label(const char *label) { length = strlen(label) + strlen(local_text) + 1; free(build); - build = (char *)xmalloc(length); + build = static_cast<char *>(xmalloc(length)); } + gcc_assert(build); strcpy(build, label); strcat(build, local_text); @@ -2347,41 +2670,31 @@ section_label(struct cbl_proc_t *procedure) // With nested programs, you can have multiple program/section pairs with the // the same names; we use a deconflictor to avoid collisions - gg_set_current_line_number(CURRENT_LINE_NUMBER); - size_t deconflictor = symbol_label_id(procedure->label); cbl_label_t *label = procedure->label; // The _initialize_program section isn't relevant. - static size_t psz_length = 256; - static char *psz = (char *)xmalloc(psz_length); - sprintf(psz, - "# SECTION %s in %s (%ld)", - label->name, - current_function->our_unmangled_name, - deconflictor); + char *psz = xasprintf("%s SECTION %s in %s (" HOST_SIZE_T_PRINT_DEC ")", + ASM_COMMENT_START, + label->name, + current_function->our_unmangled_name, + (fmt_size_t)deconflictor); gg_insert_into_assembler(psz); + free(psz); // The label has to start with an underscore. I tried a period, but those // don't seem to show up in GDB's internal symbol tables. - char *combined = combined_name(procedure->label); - if( psz_length < strlen(combined) + 36 + 1 ) - { - free(psz); - psz_length = strlen(combined) + 36 + 1; - psz = (char *)xmalloc(psz_length); - } - sprintf(psz, - "_sect.%s", - combined_name(procedure->label)); + char *psz2 = xasprintf( "_sect.%s", + combined_name(procedure->label)); SHOW_PARSE { SHOW_PARSE_HEADER - SHOW_PARSE_TEXT(psz); + SHOW_PARSE_TEXT(psz2); SHOW_PARSE_END } - assembler_label(psz); - gg_assign(var_decl_nop, build_int_cst_type(INT, 108)); + assembler_label(psz2); + free(psz2); + insert_nop(108); } static void @@ -2396,8 +2709,6 @@ paragraph_label(struct cbl_proc_t *procedure) // are not referenced by the program. We provide a deconflictor to // separate such labels. - gg_set_current_line_number(CURRENT_LINE_NUMBER); - cbl_label_t *paragraph = procedure->label; cbl_label_t *section = nullptr; @@ -2409,41 +2720,54 @@ paragraph_label(struct cbl_proc_t *procedure) char *para_name = paragraph->name; char *section_name = section ? section->name : nullptr; - static size_t psz_length = 256; - static char *psz = (char *)xmalloc(psz_length); + size_t deconflictor = symbol_label_id(procedure->label); + + char *psz1 = + xasprintf( + "%s PARAGRAPH %s of %s in %s (" HOST_SIZE_T_PRINT_DEC ")", + ASM_COMMENT_START, + para_name ? para_name: "" , + section_name ? section_name: "(null)" , + current_function->our_unmangled_name ? current_function->our_unmangled_name: "" , + (fmt_size_t)deconflictor ); - static size_t deconflictor = symbol_label_id(procedure->label); + // (0) is wrong, so back up one - sprintf(psz, - "# PARAGRAPH %s of %s in %s (%ld)", - para_name, - section_name, - current_function->our_unmangled_name, - deconflictor); - gg_insert_into_assembler(psz); + gg_insert_into_assembler(psz1); SHOW_PARSE { SHOW_PARSE_HEADER - SHOW_PARSE_TEXT(psz); + SHOW_PARSE_TEXT(psz1); SHOW_PARSE_END } + free(psz1); // The label has to start with an underscore. I tried a period, but those // don't seem to show up in GDB's internal symbol tables. - char *combined = combined_name(procedure->label); - if( psz_length < strlen(combined) + 36 + 1 ) - { - free(psz); - psz_length = strlen(combined) + 36 + 1; - psz = (char *)xmalloc(psz_length); - } - - sprintf(psz, - "_para.%s", - combined_name(procedure->label)); - assembler_label(psz); - gg_assign(var_decl_nop, build_int_cst_type(INT, 109)); + char *psz2 = xasprintf( "_para.%s", + combined_name(procedure->label)); + assembler_label(psz2); + free(psz2); + + // We are inserting a NOP after having created a label for the procedure. + // This means that when using GDC_COBOL to step into a procedure, the + // execution will stop there and show "123 para-name." at the stopped point. + // + // Note that because there is no user-specified executable code at that point + // the user can't set a working breakpoint with "break 123". But because + // GDB will pick up the psz2 text and set a breakpoint there (which is the + // location of the NOP) "break para-name" will actually stop and show line + // 123. + // + // This really only makes sense when you look at the assembly language. Keep + // in mind as you read it that issuing a "break 123" causes GDB to set a + // breakpoint at the first executable machine language code following the + // first ".loc 123" directive. + // + // Yes, trying to understand this causes headaches for many people who read + // this. Take an aspirin. + insert_nop(109); } static void @@ -2487,6 +2811,7 @@ pseudo_return_pop(cbl_proc_t *procedure) NULL_TREE); } + token_location_override(current_location_minus_one()); IF( var_decl_exit_address, eq_op, procedure->exit.addr ) { TRACE1 @@ -2496,11 +2821,13 @@ pseudo_return_pop(cbl_proc_t *procedure) // The top of the stack is us! // Pick up the return address from the pseudo_return stack: + token_location_override(current_location_minus_one()); gg_assign(current_function->void_star_temp, gg_call_expr( VOID_P, "__gg__pseudo_return_pop", NULL_TREE)); // And do the return: + token_location_override(current_location_minus_one()); gg_goto(current_function->void_star_temp); } ELSE @@ -2534,13 +2861,15 @@ leave_procedure(struct cbl_proc_t *procedure, bool /*section*/) // procedure->bottom.label); // Procedure can be null, for example at the beginning of a // new program, or after somebody else has cleared it out. + gg_append_statement(procedure->exit.label); - char ach[256]; - sprintf(ach, - "_procret.%ld:", - symbol_label_id(procedure->label)); - gg_insert_into_assembler(ach); + char *psz; + psz = xasprintf("_procret." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)symbol_label_id(procedure->label)); + token_location_override(current_location_minus_one()); + gg_insert_into_assembler(psz); + free(psz); pseudo_return_pop(procedure); gg_append_statement(procedure->bottom.label); } @@ -2649,10 +2978,11 @@ find_procedure(cbl_label_t *label) if( !retval ) { static int counter=1; - char ach[2*sizeof(cbl_name_t)]; // This is a new section or paragraph; we need to create its values: - retval = (struct cbl_proc_t *)xmalloc(sizeof(struct cbl_proc_t)); + retval = static_cast<struct cbl_proc_t *> + (xmalloc(sizeof(struct cbl_proc_t))); + gcc_assert(retval); retval->label = label; gg_create_goto_pair(&retval->top.go_to, @@ -2680,8 +3010,9 @@ find_procedure(cbl_label_t *label) // If this procedure is a paragraph, and it becomes the target of // an ALTER statement, alter_location will be used to make that change - sprintf(ach, "_%s_alter_loc_%d", label->name, counter); - retval->alter_location = gg_define_void_star(ach, vs_static); + char *psz = xasprintf("_%s_alter_loc_%d", label->name, counter); + retval->alter_location = gg_define_void_star(psz, vs_static); + free(psz); DECL_INITIAL(retval->alter_location) = null_pointer_node; counter +=1 ; @@ -2707,6 +3038,8 @@ parser_enter_section(cbl_label_t *label) { SHOW_PARSE_HEADER SHOW_PARSE_LABEL(" ", label) + SHOW_PARSE_INDENT + linemap_dump_location( line_table, current_token_location(), stderr ); SHOW_PARSE_END } @@ -2714,8 +3047,7 @@ parser_enter_section(cbl_label_t *label) // This NOP is needed to give GDB a line number for the entry point of // paragraphs - gg_set_current_line_number(CURRENT_LINE_NUMBER); - gg_assign(var_decl_nop, build_int_cst_type(INT, 101)); + insert_nop(101); struct cbl_proc_t *procedure = find_procedure(label); gg_append_statement(procedure->top.label); @@ -2742,6 +3074,8 @@ parser_enter_paragraph(cbl_label_t *label) { SHOW_PARSE_HEADER SHOW_PARSE_LABEL(" ", label) + SHOW_PARSE_INDENT + linemap_dump_location( line_table, current_token_location(), stderr ); SHOW_PARSE_END } @@ -2883,10 +3217,10 @@ parser_goto( cbl_refer_t value_ref, size_t narg, cbl_label_t * const labels[] ) // We need to create a static array of pointers to locations: static int comp_gotos = 1; - char ach[32]; - sprintf(ach, "_comp_goto_%d", comp_gotos++); + char *psz = xasprintf("_comp_goto_%d", comp_gotos++); tree array_of_pointers_type = build_array_type_nelts(VOID_P, narg); - tree array_of_pointers = gg_define_variable(array_of_pointers_type, ach, vs_static); + tree array_of_pointers = gg_define_variable(array_of_pointers_type, psz, vs_static); + free(psz); // We have the array. Now we need to build the constructor for it tree constr = make_node(CONSTRUCTOR); @@ -2912,7 +3246,7 @@ parser_goto( cbl_refer_t value_ref, size_t narg, cbl_label_t * const labels[] ) get_binary_value( value, NULL, value_ref.field, - refer_offset_source(value_ref)); + refer_offset(value_ref)); // Convert it from one-based to zero-based: gg_decrement(value); // Check to see if the value is in the range 0...narg-1: @@ -2957,16 +3291,20 @@ parser_goto( cbl_refer_t value_ref, size_t narg, cbl_label_t * const labels[] ) void parser_perform(cbl_label_t *label, bool suppress_nexting) { - label->used = yylineno; Analyze(); SHOW_PARSE { SHOW_PARSE_HEADER SHOW_PARSE_LABEL(" ", label) char ach[32]; - sprintf(ach, " label is at %p", (void*)label); + sprintf(ach, " label is at %p", static_cast<void*>(label)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " label->proc is %p", (void*)label->structs.proc); + if( label ) + { + sprintf(ach, + " label->proc is %p", + static_cast<void*>(label->structs.proc)); + } SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -2979,6 +3317,7 @@ parser_perform(cbl_label_t *label, bool suppress_nexting) } CHECK_LABEL(label); + label->used = yylineno; struct cbl_proc_t *procedure = find_procedure(label); @@ -3007,23 +3346,24 @@ parser_perform(cbl_label_t *label, bool suppress_nexting) // pairs were created, the locations of the goto instruction and the label // were not known. - char *para_name = nullptr; - char *sect_name = nullptr; + const char *para_name = nullptr; + const char *sect_name = nullptr; const char *program_name = current_function->our_unmangled_name; size_t deconflictor = symbol_label_id(label); char ach[256]; if( label->type == LblParagraph ) { - cbl_label_t *section_label = cbl_label_of(symbol_at(label->parent)); + const cbl_label_t *sec_label = cbl_label_of(symbol_at(label->parent)); para_name = label->name; - sect_name = section_label->name; + sect_name = sec_label->name; sprintf(ach, - "# PERFORM %s of %s of %s (%ld)", + "%s PERFORM %s of %s of %s (" HOST_SIZE_T_PRINT_DEC ")", + ASM_COMMENT_START, para_name, sect_name, program_name, - deconflictor); + (fmt_size_t)deconflictor); gg_insert_into_assembler(ach); } @@ -3031,18 +3371,19 @@ parser_perform(cbl_label_t *label, bool suppress_nexting) { sect_name = label->name; sprintf(ach, - "# PERFORM %s of %s (%ld)", + "%s PERFORM %s of %s (" HOST_SIZE_T_PRINT_DEC ")", + ASM_COMMENT_START, sect_name, program_name, - deconflictor); + (fmt_size_t)deconflictor); gg_insert_into_assembler(ach); } if( !suppress_nexting ) { sprintf(ach, - "_proccall.%ld.%d:", - symbol_label_id(label), + "_proccall." HOST_SIZE_T_PRINT_DEC ".%d:", + (fmt_size_t)symbol_label_id(label), call_counter++); gg_insert_into_assembler( ach ); } @@ -3075,9 +3416,9 @@ parser_perform_times( cbl_label_t *proc_1, cbl_refer_t count ) SHOW_PARSE_REF(" ", count) SHOW_PARSE_TEXT(" TIMES") char ach[32]; - sprintf(ach, " proc_1 is at %p", (void*)proc_1); + sprintf(ach, " proc_1 is at %p", static_cast<void*>(proc_1)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " proc_1->proc is %p", (void*)proc_1->structs.proc); + sprintf(ach, " proc_1->proc is %p", static_cast<void*>(proc_1->structs.proc)); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -3090,8 +3431,8 @@ parser_perform_times( cbl_label_t *proc_1, cbl_refer_t count ) char ach[256]; size_t our_pseudo_label = pseudo_label++; sprintf(ach, - "_proccallb.%ld:", - our_pseudo_label); + "_proccallb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); gg_insert_into_assembler( ach ); tree counter = gg_define_variable(LONG); @@ -3100,7 +3441,7 @@ parser_perform_times( cbl_label_t *proc_1, cbl_refer_t count ) get_binary_value( counter, NULL, count.field, - refer_offset_source(count)); + refer_offset(count)); // Make sure the initial count is valid: WHILE( counter, gt_op, gg_cast(LONG, integer_zero_node) ) @@ -3112,8 +3453,9 @@ parser_perform_times( cbl_label_t *proc_1, cbl_refer_t count ) WEND sprintf(ach, - "_procretb.%ld:", - our_pseudo_label); + "_procretb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); + token_location_override(current_location_minus_one()); gg_insert_into_assembler(ach); } @@ -3128,17 +3470,22 @@ internal_perform_through( cbl_label_t *proc_1, SHOW_PARSE_HEADER SHOW_PARSE_LABEL(" ", proc_1); char ach[32]; - sprintf(ach, " proc_1 is at %p", (void*)proc_1); + sprintf(ach, " proc_1 is at %p", static_cast<void*>(proc_1)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " proc_1->proc is %p", (void*)proc_1->structs.proc); + if( proc_1 ) + { + sprintf(ach, + " proc_1->proc is %p", + static_cast<void*>(proc_1->structs.proc)); + } SHOW_PARSE_TEXT(ach) if( proc_2 ) { SHOW_PARSE_INDENT - SHOW_PARSE_LABEL("", proc_2); - sprintf(ach, " proc_2 is at %p", (void*)proc_2); + SHOW_PARSE_LABEL_OK("", proc_2); + sprintf(ach, " proc_2 is at %p", static_cast<void*>(proc_2)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " proc_2->proc is %p", (void*)proc_2->structs.proc); + sprintf(ach, " proc_2->proc is %p", static_cast<void*>(proc_2->structs.proc)); SHOW_PARSE_TEXT(ach) } SHOW_PARSE_END @@ -3151,14 +3498,12 @@ internal_perform_through( cbl_label_t *proc_1, CHECK_LABEL(proc_1); - if(!proc_2) + if( !proc_2 ) { parser_perform(proc_1, suppress_nexting); return; } - CHECK_LABEL(proc_2); - struct cbl_proc_t *proc1 = find_procedure(proc_1); struct cbl_proc_t *proc2 = find_procedure(proc_2); @@ -3183,15 +3528,15 @@ internal_perform_through( cbl_label_t *proc_1, pseudo_return_push(proc2, return_addr); // Create the code that will launch the first procedure - gg_insert_into_assembler("# PERFORM %s THROUGH %s", - proc_1->name, proc_2->name); + gg_insert_into_assemblerf("%s PERFORM %s THROUGH %s", + ASM_COMMENT_START, proc_1->name, proc_2->name); if( !suppress_nexting ) { char ach[256]; sprintf(ach, - "_proccall.%ld.%d:", - symbol_label_id(proc_2), + "_proccall." HOST_SIZE_T_PRINT_DEC ".%d:", + (fmt_size_t)symbol_label_id(proc_2), call_counter++); gg_insert_into_assembler(ach); } @@ -3213,17 +3558,22 @@ internal_perform_through_times( cbl_label_t *proc_1, SHOW_PARSE_HEADER SHOW_PARSE_LABEL(" ", proc_1); char ach[32]; - sprintf(ach, " proc_1 is at %p", (void*)proc_1); + sprintf(ach, " proc_1 is at %p", static_cast<void*>(proc_1)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " proc_1->proc is %p", (void*)proc_1->structs.proc); + if( proc_1 ) + { + sprintf(ach, + " proc_1->proc is %p", + static_cast<void*>(proc_1->structs.proc)); + } SHOW_PARSE_TEXT(ach) if( proc_2 ) { SHOW_PARSE_INDENT - SHOW_PARSE_LABEL("", proc_2); - sprintf(ach, " proc_2 is at %p", (void*)proc_2); + SHOW_PARSE_LABEL_OK("", proc_2); + sprintf(ach, " proc_2 is at %p", static_cast<void*>(proc_2)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " proc_2->proc is %p", (void*)proc_2->structs.proc); + sprintf(ach, " proc_2->proc is %p", static_cast<void*>(proc_2->structs.proc)); SHOW_PARSE_TEXT(ach) } SHOW_PARSE_REF(" ", count); @@ -3240,15 +3590,15 @@ internal_perform_through_times( cbl_label_t *proc_1, char ach[256]; sprintf(ach, - "_proccallb.%ld:", - our_pseudo_label); + "_proccallb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); gg_insert_into_assembler( ach ); tree counter = gg_define_variable(LONG); get_binary_value( counter, NULL, count.field, - refer_offset_source(count)); + refer_offset(count)); WHILE( counter, gt_op, gg_cast(LONG, integer_zero_node) ) { internal_perform_through(proc_1, proc_2, true); // true means suppress_nexting @@ -3257,8 +3607,9 @@ internal_perform_through_times( cbl_label_t *proc_1, WEND sprintf(ach, - "_procretb.%ld:", - our_pseudo_label); + "_procretb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); + token_location_override(current_location_minus_one()); gg_insert_into_assembler( ach ); } @@ -3333,17 +3684,16 @@ parser_first_statement( int lineno ) } } -#define linemap_add(...) - void parser_enter_file(const char *filename) { SHOW_PARSE { SHOW_PARSE_HEADER - char ach[32]; - sprintf(ach, " entering level:%d %s", file_level+1, filename); - SHOW_PARSE_TEXT(ach); + char *psz; + psz = xasprintf(" entering level:%d %s", file_level+1, filename); + SHOW_PARSE_TEXT(psz); + free(psz); SHOW_PARSE_END } @@ -3365,9 +3715,6 @@ parser_enter_file(const char *filename) } } - // Let the linemap routine know we are working on a new file: - linemap_add(line_table, LC_ENTER, 0, filename, 1); - if( file_level == 0 ) { // Build a translation_unit_decl: @@ -3388,8 +3735,6 @@ parser_enter_file(const char *filename) A = gg_declare_variable(B, C, NULL_TREE, vs_external_reference) SET_VAR_DECL(var_decl_exception_code , INT , "__gg__exception_code"); - SET_VAR_DECL(var_decl_exception_handled , INT , "__gg__exception_handled"); - SET_VAR_DECL(var_decl_exception_file_number , INT , "__gg__exception_file_number"); SET_VAR_DECL(var_decl_exception_file_status , INT , "__gg__exception_file_status"); SET_VAR_DECL(var_decl_exception_file_name , CHAR_P , "__gg__exception_file_name"); SET_VAR_DECL(var_decl_exception_statement , CHAR_P , "__gg__exception_statement"); @@ -3401,7 +3746,6 @@ parser_enter_file(const char *filename) SET_VAR_DECL(var_decl_default_compute_error , INT , "__gg__default_compute_error"); SET_VAR_DECL(var_decl_rdigits , INT , "__gg__rdigits"); - SET_VAR_DECL(var_decl_odo_violation , INT , "__gg__odo_violation"); SET_VAR_DECL(var_decl_unique_prog_id , SIZE_T , "__gg__unique_prog_id"); SET_VAR_DECL(var_decl_entry_location , VOID_P , "__gg__entry_pointer"); @@ -3432,6 +3776,7 @@ parser_enter_file(const char *filename) SET_VAR_DECL(var_decl_treeplet_4s , SIZE_T_P , "__gg__treeplet_4s" ); SET_VAR_DECL(var_decl_nop , INT , "__gg__nop" ); SET_VAR_DECL(var_decl_main_called , INT , "__gg__main_called" ); + SET_VAR_DECL(var_decl_entry_label , VOID_P , "__gg__entry_label" ); } } @@ -3443,16 +3788,38 @@ parser_leave_file() { SHOW_PARSE_HEADER char ach[256]; - sprintf(ach, "leaving level:%d %s", file_level, current_filename.back().c_str()); + sprintf(ach, + "leaving level:%d %s", + file_level, + current_filename.back().c_str()); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } - if( file_level > 0) - { - linemap_add(line_table, LC_LEAVE, false, NULL, 0); - } file_level -= 1; current_filename.pop_back(); + + if( file_level == 0 ) + { + // We are leaving the top-level file, which means this compilation is + // done, done, done. + + // There is, however, one thing left to do. If the command line says + // that this module needs a main entry point, then this is where + // we create a main() function. We build it at the end, so that all of + // the .loc directives associated with it appear at the end of the + // source code. We used to create the main() entry point at the beginning, + // but that created confusion for GDB when trying to debug the generated + // executable. + if( main_entry_point ) + { + next_program_is_main = false; + build_main_that_calls_something(main_entry_point); + free(main_entry_point); + main_entry_point = NULL; + } + + gg_leaving_the_source_code_file(); + } } void @@ -3467,15 +3834,16 @@ enter_program_common(const char *funcname, const char *funcname_) // have no parameters. We'll chain the parameters on in parser_division(), // when we process PROCEDURE DIVISION USING... - gg_define_function_with_no_parameters( COBOL_FUNCTION_RETURN_TYPE, - funcname, - funcname_); + gg_define_function(COBOL_FUNCTION_RETURN_TYPE, + funcname, + funcname_, + NULL_TREE); current_function->first_time_through = - gg_define_variable(INT, - "_first_time_through", - vs_static, - integer_one_node); + gg_define_variable(INT, + "_first_time_through", + vs_static, + integer_one_node); gg_create_goto_pair(¤t_function->skip_init_goto, ¤t_function->skip_init_label); @@ -3500,27 +3868,6 @@ enter_program_common(const char *funcname, const char *funcname_) current_function->current_section = NULL; current_function->current_paragraph = NULL; - current_function->is_truly_nested = false; - - // Text conversion must be initialized before the code generated by - // parser_symbol_add runs. - - // The text_conversion_override exists both in the library and in the compiler - - __gg__set_internal_codeset(internal_codeset_is_ebcdic()); - gg_call(VOID, - "__gg__set_internal_codeset", - internal_codeset_is_ebcdic() - ? integer_one_node : integer_zero_node, - NULL_TREE); - - __gg__text_conversion_override(td_default_e, cs_default_e); - gg_call(VOID, - "__gg__text_conversion_override", - build_int_cst_type(INT, td_default_e), - build_int_cst_type(INT, cs_default_e), - NULL_TREE); - gg_call(VOID, "__gg__codeset_figurative_constants", NULL_TREE); @@ -3561,19 +3908,22 @@ parser_enter_program( const char *funcname_, // The first thing we have to do is mangle this name. This is safe even // though the end result will be mangled again, because the mangler doesn't // change a mangled name. + char *mangled_name = cobol_name_mangler(funcname_); size_t parent_index = current_program_index(); - char funcname[128]; + char *funcname; if( parent_index ) { // This is a nested function. Tack on the parent_index to the end of it. - sprintf(funcname, "%s.%ld", mangled_name, parent_index); + funcname = xasprintf( "%s." HOST_SIZE_T_PRINT_DEC, + mangled_name, + (fmt_size_t)parent_index); } else { // This is a top-level function; just use the straight mangled name - strcpy(funcname, mangled_name); + funcname = xstrdup(mangled_name); } free(mangled_name); @@ -3587,31 +3937,28 @@ parser_enter_program( const char *funcname_, if( !is_function && !parent_index ) { - // This is a top_level program, and not a function + // This is a top_level program-id, and not a function if( next_program_is_main ) { + // This is the first top-level program-id. next_program_is_main = false; - if(main_entry_point) - { - build_main_that_calls_something(main_entry_point); - free(main_entry_point); - main_entry_point = NULL; - } - else + if( !main_entry_point ) { - build_main_that_calls_something(funcname); + // Because no explicit main_entry_point was specified, this program-id, + // the first in the file, becomes the target of the main() function + // that will be created at parser_leave_file time. + main_entry_point = xstrdup(funcname); + + char *psz = cobol_name_mangler(main_entry_point); + strncpy(ach_cobol_entry_point, psz, sizeof(ach_cobol_entry_point)-1); + free(psz); } } } - // Call this after build_main_that_calls_something, because it manipulates - // the current line number to DEFAULT_LINE_NUMBER. We have to manipulate it - // back afterward. - gg_set_current_line_number(CURRENT_LINE_NUMBER); - if( strcmp(funcname_, "main") == 0 && this_module_has_main ) { - // setting 'retval' to 1 let's the caller know that we are being told + // Setting 'retval' to 1 lets the caller know that we are being told // both to synthesize a main() entry point to duplicate GCC's default // behavior, and to create an explicit entry point named "main". This will // eventually result in a link error (because of the duplicated entry @@ -3639,8 +3986,40 @@ parser_enter_program( const char *funcname_, TRACE1_TEXT("\"") TRACE1_END } + + free(funcname); } +static class label_verify_t { + std::set<size_t> lain, dangling; + static inline size_t index_of( const cbl_label_t *label ) { + return symbol_index(symbol_elem_of(label)); + } +public: + void go_to( const cbl_label_t *label ) { + auto p = lain.find(index_of(label)); + if( p == lain.end() ) { + dangling.insert(index_of(label)); + } + } + void lay( const cbl_label_t *label ) { + auto ok = lain.insert(index_of(label)); + if( ok.second ) { + dangling.erase(index_of(label)); + } + } + bool vet() const { // be always agreeable, for now. + return dangling.empty(); + } + void dump() const { + fprintf(stderr, "%u nonexistent labels called\n", unsigned(dangling.size()) ); + for( auto sym : dangling ) { + const cbl_label_t *label = cbl_label_of(symbol_at(sym)); + fprintf(stderr, "\t %s\n", label->name); + } + } +} label_verify; + void parser_end_program(const char *prog_name ) { @@ -3667,6 +4046,13 @@ parser_end_program(const char *prog_name ) TRACE1_END } + if( ! label_verify.vet() ) + { + label_verify.dump(); + gcc_unreachable(); + } + + if( gg_trans_unit.function_stack.size() ) { // The body has been created by various parser calls. It's time @@ -3734,8 +4120,8 @@ parser_init_list_size(int count_of_variables) vti_list_size = count_of_variables; char ach[48]; sprintf(ach, - "..variables_to_init_%ld", - current_function->our_symbol_table_index); + "..variables_to_init_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)current_function->our_symbol_table_index); tree array_of_variables_type = build_array_type_nelts(VOID_P, count_of_variables+1); vti_array = gg_define_variable( array_of_variables_type, @@ -3773,390 +4159,408 @@ parser_init_list() char ach[48]; sprintf(ach, - "..variables_to_init_%ld", - current_function->our_symbol_table_index); + "..variables_to_init_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)current_function->our_symbol_table_index); tree array = gg_trans_unit_var_decl(ach); gg_call(VOID, "__gg__variables_to_init", gg_get_address_of(array), - wsclear() ? gg_string_literal(wsclear()) : null_pointer_node, + wsclear() ? build_string_literal(1, (const char *)wsclear()) + : null_pointer_node, NULL_TREE); } -static void -psa_FldLiteralN(struct cbl_field_t *field ) +static +FIXED_WIDE_INT(128) +dirty_to_binary(const char *instring, + uint32_t &capacity, + uint32_t &digits, + int32_t &rdigits, + uint64_t &attr) { - Analyze(); - SHOW_PARSE + digits = 0; + rdigits = 0; + attr = 0; + + FIXED_WIDE_INT(128) value = 0; + + // We need to convert data.initial to an FIXED_WIDE_INT(128) value + const char *p = instring; + int sign = 1; + if( *p == '-' ) { - SHOW_PARSE_HEADER - SHOW_PARSE_FIELD(" ", field) - SHOW_PARSE_END + attr |= signable_e; + sign = -1; + p += 1; + } + else if( *p == '+' ) + { + // We set it signable so that the instruction DISPLAY +1 + // actually outputs "+1" + attr |= signable_e; + p += 1; } - // We are constructing a completely static constant structure, based on the - // text string in .initial -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" - __int128 value = 0; -#pragma GCC diagnostic pop + // We need to be able to handle + // 123 + // 123.456 + // 123E<exp> + // 123.456E<exp> + // where <exp> can be N, +N and -N + // + // Oh, yeah, and we're talking handling up to 32 digits, or more, so using + // library routines is off the table. - do + int rdigit_delta = 0; + int exponent = 0; + const char *exp = strchr(p, 'E'); + if( !exp ) { - // This is a false do{}while, to isolate the variables: - - // We need to convert data.initial to an __int128 value - char *p = const_cast<char *>(field->data.initial); - int sign = 1; - if( *p == '-' ) - { - field->attr |= signable_e; - sign = -1; - p += 1; - } - else if( *p == '+' ) - { - // We set it signable so that the instruction DISPLAY +1 - // actually outputs "+1" - field->attr |= signable_e; - p += 1; - } + exp = strchr(p, 'e'); + } + if(exp) + { + exponent = atoi(exp+1); + } - // We need to be able to handle - // 123 - // 123.456 - // 123E<exp> - // 123.456E<exp> - // where <exp> can be N, +N and -N - // - // Oh, yeah, and we're talking handling up to 32 digits, or more, so using - // library routines is off the table. + // We can now calculate the value, and the number of digits and rdigits. - int digits = 0; - int rdigits = 0; - int rdigit_delta = 0; - int exponent = 0; + // We count up leading zeroes as part of the attr->digits calculation. + // It turns out that certain comparisons need to know the number of digits, + // because "IF "2" EQUAL 002" is false, while "IF "2" EQUAL 2" is true. So, + // we need to count up leading zeroes. - char *exp = strchr(p, 'E'); - if( !exp ) + for(;;) + { + char ch = *p++; + if( ch == symbol_decimal_point() ) { - exp = strchr(p, 'e'); + rdigit_delta = 1; + continue; } - if(exp) + if( ch < '0' || ch > '9' ) { - exponent = atoi(exp+1); + break; } + digits += 1; + rdigits += rdigit_delta; + value *= 10; + value += ch - '0'; + } - // We can now calculate the value, and the number of digits and rdigits. - - // We count up leading zeroes as part of the attr->digits calculation. - // It turns out that certain comparisons need to know the number of digits, - // because "IF "2" EQUAL 002" is false, while "IF "2" EQUAL 2" is true. So, - // we need to count up leading zeroes. - - for(;;) + if( exponent < 0 ) + { + rdigits += -exponent; + } + else + { + while(exponent--) { - char ch = *p++; - if( ch == symbol_decimal_point() ) - { - rdigit_delta = 1; - continue; - } - if( ch < '0' || ch > '9' ) + if(rdigits) { - break; + rdigits -= 1; } - digits += 1; - rdigits += rdigit_delta; - value *= 10; - value += ch - '0'; - } - - if( exponent < 0 ) - { - rdigits += -exponent; - } - else - { - while(exponent--) + else { - if(rdigits) - { - rdigits -= 1; - } - else - { - digits += 1; - value *= 10; - } + digits += 1; + value *= 10; } } + } - if(digits < rdigits) - { - digits = rdigits; - } - field->data.digits = digits; - field->data.rdigits = rdigits; + if( (int32_t)digits < rdigits ) + { + digits = rdigits; + } - // We now need to calculate the capacity. + // We now need to calculate the capacity. - unsigned char *pvalue = (unsigned char *)&value; - int capacity; - if( *(uint64_t*)(pvalue + 8) ) - { - // Bytes 15 through 8 are non-zero - capacity = 16; - } - else if( *(uint32_t*)(pvalue + 4) ) - { - // Bytes 7 through 4 are non-zero - capacity = 8; - } - else if( *(uint16_t*)(pvalue + 2) ) - { - // Bytes 3 and 2 - capacity = 4; - } - else if( pvalue[1] ) - { - // Byte 1 is non-zero - capacity = 2; - } - else - { - // The value is zero through 0xFF - capacity = 1; - } + unsigned int min_prec = wi::min_precision(value, UNSIGNED); + if( min_prec > 64 ) + { + // Bytes 15 through 8 are non-zero + capacity = 16; + } + else if( min_prec > 32 ) + { + // Bytes 7 through 4 are non-zero + capacity = 8; + } + else if( min_prec > 16 ) + { + // Bytes 3 and 2 + capacity = 4; + } + else if( min_prec > 8 ) + { + // Byte 1 is non-zero + capacity = 2; + } + else + { + // The value is zero through 0xFF + capacity = 1; + } - value *= sign; + value *= sign; - // One last adjustment. The number is signable, so the binary value - // is going to be treated as twos complement. That means that the highest - // bit has to be 1 for negative signable numbers, and 0 for positive. If - // necessary, adjust capacity up by one byte so that the variable fits: + // One last adjustment. The number is signable, so the binary value + // is going to be treated as twos complement. That means that the highest + // bit has to be 1 for negative signable numbers, and 0 for positive. If + // necessary, adjust capacity up by one byte so that the variable fits: - if( capacity < 16 && (field->attr & signable_e) ) + if( capacity < 16 && (attr & signable_e) ) + { + FIXED_WIDE_INT(128) mask + = wi::set_bit_in_zero<FIXED_WIDE_INT(128)>(capacity * 8 - 1); + if( wi::neg_p (value) && (value & mask) == 0 ) { - if( value < 0 && (((pvalue[capacity-1] & 0x80) == 0 ))) - { - capacity *= 2; - } - else if( value >= 0 && (((pvalue[capacity-1] & 0x80) == 0x80 ))) - { - capacity *= 2; - } + capacity *= 2; } - field->data.capacity = capacity; - - }while(0); - - char base_name[257]; - char id_string[32] = ""; - - static size_t our_index = 0; - - sprintf(id_string, ".%ld", ++our_index); - strcpy(base_name, field->name); - strcat(base_name, id_string); - - tree var_type; - - if( field->data.capacity == 16 ) - { - /* GCC-13 has no provision for an int128 constructor. So, we use a - union for our necessary __int128. - - typedef union cblc_int128_t - { - unsigned char array16[16]; - __uint128 uval128; - __int128 sval128; - } cblc_int128_t; - - We build a constructor for the array16[], and then we use that - constructor in the constructor for the union. - */ - - // Build the constructor for array16 - tree array16_type = build_array_type_nelts(UCHAR, 16); - tree array_16_constructor = make_node(CONSTRUCTOR); - TREE_TYPE(array_16_constructor) = array16_type; - TREE_STATIC(array_16_constructor) = 1; - TREE_CONSTANT(array_16_constructor) = 1; - - for(int i=0; i<16; i++) + else if( !wi::neg_p (value) && (value & mask) != 0 ) { - CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(array_16_constructor), - build_int_cst_type(INT, i), - build_int_cst_type(UCHAR, - ((unsigned char *)&value)[i])); + capacity *= 2; } - - // The array16 constructor is ready to be used - - // So, we need a constructor for the union: - // Now we create the union: - var_type = cblc_int128_type_node; - - tree union_constructor = make_node(CONSTRUCTOR); - TREE_TYPE(union_constructor) = var_type; - TREE_STATIC(union_constructor) = 1; - TREE_CONSTANT(union_constructor) = 1; - - // point next_field to the first field of the union, and - // set the value to be the table constructor - tree next_field = TYPE_FIELDS(var_type); - CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(union_constructor), - next_field, - array_16_constructor ); - - tree new_var_decl = gg_define_variable( var_type, - base_name, - vs_static); - DECL_INITIAL(new_var_decl) = union_constructor; - - field->data_decl_node = member(new_var_decl, "sval128"); - TREE_READONLY(field->data_decl_node) = 1; - TREE_CONSTANT(field->data_decl_node) = 1; - - // Convert the compile-time data.value to a run-time variable decl node: - sprintf(id_string, ".%ld", ++our_index); - strcpy(base_name, field->name); - strcat(base_name, id_string); - field->literal_decl_node = gg_define_variable(DOUBLE, id_string, vs_static); - TREE_READONLY(field->literal_decl_node) = 1; - TREE_CONSTANT(field->literal_decl_node) = 1; - char ach[128]; - strfromf128(ach, sizeof(ach), "%.36E", field->data.value_of()); - REAL_VALUE_TYPE real; - real_from_string(&real, ach); - tree initer = build_real (DOUBLE, real); - DECL_INITIAL(field->literal_decl_node) = initer; - - } - else - { - // The value is 1, 2, 4, or 8 bytes, so an ordinary constructor can be used. - var_type = tree_type_from_size( field->data.capacity, - field->attr & signable_e); - tree new_var_decl = gg_define_variable( var_type, - base_name, - vs_static); - DECL_INITIAL(new_var_decl) = build_int_cst_type(var_type, value); - field->data_decl_node = new_var_decl; } + + return value; } static void -psa_FldBlob(struct cbl_field_t *var ) +psa_FldLiteralN(struct cbl_field_t *field ) { Analyze(); SHOW_PARSE { SHOW_PARSE_HEADER - SHOW_PARSE_FIELD(" ", var) + SHOW_PARSE_FIELD(" ", field) SHOW_PARSE_END } + // We are constructing a completely static constant structure, based on the + // text string in .initial - // We are constructing a completely static constant structure. We know the - // capacity. We'll create it from the data.initial. The var_decl_node will - // be a pointer to the data + CHECK_FIELD(field); + + uint32_t capacity; + uint32_t digits; + int32_t rdigits; + uint64_t attr; + FIXED_WIDE_INT(128) value = dirty_to_binary(field->data.original(), + capacity, + digits, + rdigits, + attr); + // This is a rare occurrence of a parser_xxx call changing the entry + // in the symbol table. + field->data.capacity = capacity; + field->data.digits = digits; + field->data.rdigits = rdigits; + field->attr |= attr; char base_name[257]; char id_string[32] = ""; static size_t our_index = 0; - sprintf(id_string, ".%ld", ++our_index); - strcpy(base_name, var->name); + sprintf(id_string, "." HOST_SIZE_T_PRINT_DEC, (fmt_size_t)++our_index); + strcpy(base_name, field->name); strcat(base_name, id_string); - // Build the constructor for the array of bytes - - tree array_type = build_array_type_nelts(UCHAR, var->data.capacity); - tree array_constructor = make_node(CONSTRUCTOR); - TREE_TYPE(array_constructor) = array_type; - TREE_STATIC(array_constructor) = 1; - TREE_CONSTANT(array_constructor) = 1; - - for(size_t i=0; i<var->data.capacity; i++) - { - CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(array_constructor), - build_int_cst_type(INT, i), - build_int_cst_type(UCHAR, var->data.initial[i])); - } + tree var_type; - // The array constructor is ready to be used - tree var_decl_node = gg_define_variable( array_type, + // The value is 1, 2, 4, 8 or 16 bytes, so an ordinary constructor can be + // used. + var_type = tree_type_from_size( field->data.capacity, + field->attr & signable_e); + tree new_var_decl = gg_define_variable( var_type, base_name, vs_static); - DECL_INITIAL(var_decl_node) = array_constructor; - var->var_decl_node = gg_get_address_of(var_decl_node); + DECL_INITIAL(new_var_decl) = wide_int_to_tree(var_type, value); + field->data_decl_node = new_var_decl; + + // Note that during compilation, the integer value, assuming it can be + // contained in 128-bit integers, can be accessed with + // + // wi::to_wide( DECL_INITIAL(new_var_decl) ) } void -parser_accept( struct cbl_refer_t refer, - enum special_name_t special_e ) +parser_accept(const struct cbl_refer_t &tgt, + special_name_t special_e, + cbl_label_t *error, + cbl_label_t *not_error ) { - Analyze(); SHOW_PARSE { SHOW_PARSE_HEADER - SHOW_PARSE_REF(" ", refer); + if( error ) + { + SHOW_PARSE_LABEL(" error ", error) + } + if( not_error ) + { + SHOW_PARSE_LABEL(" not_error ", not_error) + } SHOW_PARSE_END } - TRACE1 - { - TRACE1_HEADER - TRACE1_END - } - - /* - enum special_name_t - { - SYSIN_e, - SYSIPT_e, - SYSOUT_e, - SYSLIST_e, - SYSLST_e, - SYSPUNCH_e, - SYSPCH_e, - CONSOLE_e, - C01_e, C02_e, C03_e, C04_e, C05_e, C06_e, - C07_e, C08_e, C09_e, C10_e, C11_e, C12_e, - CSP_e, - S01_e, S02_e, S03_e, S04_e, S05_e, - AFP_5A_e, - }; - */ // The ISO spec describes the valid special names for ACCEPT as implementation // dependent. We are following IBM's lead. tree environment = build_int_cst_type(INT, special_e); - switch( special_e ) + const char *function_to_call = NULL; + + switch(special_e) { + case STDIN_e: case CONSOLE_e: case SYSIPT_e: case SYSIN_e: - break; - default: - dbgmsg("%s(): We don't know what to do with special_name_t %d,", __func__, special_e); - dbgmsg("%s(): so we are ignoring it.", __func__); - yywarn("unrecognized SPECIAL NAME ignored"); + // This is ordinary input from from the stdin: + gg_call(VOID, + "__gg__accept", + environment, + gg_get_address_of(tgt.field->var_decl_node), + refer_offset(tgt), + refer_size_dest(tgt), + NULL_TREE); return; break; - } - gg_call(VOID, - "__gg__accept", - environment, - gg_get_address_of(refer.field->var_decl_node), - refer_offset_dest(refer), - refer_size_dest(refer), - NULL_TREE); + case C01_e: + case C02_e: + case C03_e: + case C04_e: + case C05_e: + case C06_e: + case C07_e: + case C08_e: + case C09_e: + case C10_e: + case C11_e: + case C12_e: + case CSP_e: + case S01_e: + case S02_e: + case S03_e: + case S04_e: + case S05_e: + case AFP_5A_e: + case STDOUT_e: + case SYSOUT_e: + case SYSLIST_e: + case SYSLST_e: + case STDERR_e: + case SYSPUNCH_e: + case SYSPCH_e: + case SYSERR_e: + cbl_internal_error("Not valid for ACCEPT statement."); + break; + + case ARG_NUM_e: + // This ACCEPT statement wants the number of argv values: + gg_call(VOID, + "__gg__get_argc", + gg_get_address_of(tgt.field->var_decl_node), + refer_offset(tgt), + refer_size_source(tgt), + NULL_TREE); + return; + break; + + case ENV_NAME_e: + // This fetches the environment name set by DISPLAY... UPON ENV_NAME_e + gg_call(VOID, + "__gg__get_env_name", + gg_get_address_of(tgt.field->var_decl_node), + refer_offset(tgt), + refer_size_source(tgt), + NULL_TREE); + return; + break; + + case ENV_VALUE_e: + // This fetches the environment value associated with the previously + // esablished name + function_to_call = "__gg__get_env_value"; + break; + + case ARG_VALUE_e: + // We are fetching the variable whose index was established by a prior + // DISPLAY UPON ARGUMENT-NUMBER. After the fetch, the value will be + // incremented by one. + function_to_call = "__gg__accept_arg_value"; + break; + } + if( function_to_call ) + { + tree erf = gg_define_int(); + gg_assign(erf, + gg_call_expr( INT, + function_to_call, + gg_get_address_of(tgt.field->var_decl_node), + refer_offset(tgt), + refer_size_dest(tgt), + NULL_TREE)); + if( error ) + { + // There is an ON EXCEPTION phrase: + IF( erf, ne_op, integer_zero_node ) + { + SHOW_PARSE + { + SHOW_PARSE_INDENT + SHOW_PARSE_TEXT("Laying down GOTO error->INTO for_argv") + SHOW_PARSE_LABEL_OK(" ", error) + } + gg_append_statement( error->structs.arith_error->into.go_to ); + } + ELSE + { + } + ENDIF + } + if( not_error ) + { + // There is an NOT ON EXCEPTION phrase: + IF( erf, eq_op, integer_zero_node ) + { + SHOW_PARSE + { + SHOW_PARSE_INDENT + SHOW_PARSE_TEXT("Laying down GOTO not_error->INTO for_argv") + SHOW_PARSE_LABEL_OK(" ", not_error) + } + gg_append_statement( not_error->structs.arith_error->into.go_to ); + } + ELSE + { + } + ENDIF + } + if( error ) + { + SHOW_PARSE + { + SHOW_PARSE_INDENT + SHOW_PARSE_TEXT("Laying down LABEL error->bottom") + SHOW_PARSE_LABEL_OK(" ", error) + } + gg_append_statement( error->structs.arith_error->bottom.label ); + } + if( not_error ) + { + SHOW_PARSE + { + SHOW_PARSE_INDENT + SHOW_PARSE_TEXT("Laying down LABEL not_error->bottom") + SHOW_PARSE_LABEL_OK(" ", not_error) + SHOW_PARSE_END + } + gg_append_statement( not_error->structs.arith_error->bottom.label ); + } + } } // TODO: update documentation. @@ -4168,7 +4572,6 @@ parser_accept_exception( cbl_label_t *accept_label ) // We are entering either SIZE ERROR or NOT SIZE ERROR code RETURN_IF_PARSE_ONLY; - set_up_on_exception_label(accept_label); SHOW_PARSE { @@ -4181,6 +4584,9 @@ parser_accept_exception( cbl_label_t *accept_label ) SHOW_PARSE_END } + CHECK_LABEL(accept_label); + set_up_on_exception_label(accept_label); + // Jump over the [NOT] ON EXCEPTION code that is about to be laid down gg_append_statement( accept_label->structs.arith_error->over.go_to ); // Create the label that allows the following code to be executed at @@ -4207,6 +4613,8 @@ parser_accept_exception_end( cbl_label_t *accept_label ) SHOW_PARSE_END } + CHECK_LABEL(accept_label); + // Jump to the end of the arithmetic code: gg_append_statement( accept_label->structs.arith_error->bottom.go_to ); // Lay down the label that allows the ERROR/NOT ERROR instructions @@ -4216,8 +4624,8 @@ parser_accept_exception_end( cbl_label_t *accept_label ) } void -parser_accept_command_line( cbl_refer_t tgt, - cbl_refer_t source, +parser_accept_command_line( const cbl_refer_t &tgt, + const cbl_refer_t &source, cbl_label_t *error, cbl_label_t *not_error ) { @@ -4245,7 +4653,7 @@ parser_accept_command_line( cbl_refer_t tgt, gg_call_expr( INT, "__gg__get_command_line", gg_get_address_of(tgt.field->var_decl_node), - refer_offset_dest(tgt), + refer_offset(tgt), refer_size_dest(tgt), NULL_TREE)); if( error ) @@ -4257,7 +4665,7 @@ parser_accept_command_line( cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down GOTO error->INTO for_command_line") - SHOW_PARSE_LABEL(" ", error) + SHOW_PARSE_LABEL_OK(" ", error) } gg_append_statement( error->structs.arith_error->into.go_to ); } @@ -4275,7 +4683,7 @@ parser_accept_command_line( cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down GOTO not_error->INTO for command_line") - SHOW_PARSE_LABEL(" ", not_error) + SHOW_PARSE_LABEL_OK(" ", not_error) } gg_append_statement( not_error->structs.arith_error->into.go_to ); } @@ -4292,10 +4700,10 @@ parser_accept_command_line( cbl_refer_t tgt, gg_call_expr( INT, "__gg__get_argv", gg_get_address_of(tgt.field->var_decl_node), - refer_offset_dest(tgt), + refer_offset(tgt), refer_size_dest(tgt), gg_get_address_of(source.field->var_decl_node), - refer_offset_dest(source), + refer_offset(source), refer_size_dest(source), NULL_TREE)); if( error ) @@ -4307,7 +4715,7 @@ parser_accept_command_line( cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down GOTO error->INTO for_argv") - SHOW_PARSE_LABEL(" ", error) + SHOW_PARSE_LABEL_OK(" ", error) } gg_append_statement( error->structs.arith_error->into.go_to ); } @@ -4325,7 +4733,7 @@ parser_accept_command_line( cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down GOTO not_error->INTO for_argv") - SHOW_PARSE_LABEL(" ", not_error) + SHOW_PARSE_LABEL_OK(" ", not_error) } gg_append_statement( not_error->structs.arith_error->into.go_to ); } @@ -4341,7 +4749,7 @@ parser_accept_command_line( cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down LABEL error->bottom") - SHOW_PARSE_LABEL(" ", error) + SHOW_PARSE_LABEL_OK(" ", error) } gg_append_statement( error->structs.arith_error->bottom.label ); } @@ -4351,7 +4759,7 @@ parser_accept_command_line( cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down LABEL not_error->bottom") - SHOW_PARSE_LABEL(" ", not_error) + SHOW_PARSE_LABEL_OK(" ", not_error) SHOW_PARSE_END } gg_append_statement( not_error->structs.arith_error->bottom.label ); @@ -4359,7 +4767,7 @@ parser_accept_command_line( cbl_refer_t tgt, } void -parser_accept_command_line_count( cbl_refer_t tgt ) +parser_accept_command_line_count( const cbl_refer_t &tgt ) { Analyze(); SHOW_PARSE @@ -4375,16 +4783,16 @@ parser_accept_command_line_count( cbl_refer_t tgt ) gg_call( VOID, "__gg__get_argc", gg_get_address_of(tgt.field->var_decl_node), - refer_offset_dest(tgt), + refer_offset(tgt), refer_size_dest(tgt), NULL_TREE); } void -parser_accept_envar(struct cbl_refer_t tgt, - struct cbl_refer_t envar, - cbl_label_t *error, - cbl_label_t *not_error ) +parser_accept_envar(const struct cbl_refer_t &tgt, + const struct cbl_refer_t &envar, + cbl_label_t *error, + cbl_label_t *not_error ) { Analyze(); @@ -4413,10 +4821,10 @@ parser_accept_envar(struct cbl_refer_t tgt, gg_call_expr( INT, "__gg__accept_envar", gg_get_address_of(tgt.field->var_decl_node), - refer_offset_dest(tgt), + refer_offset(tgt), refer_size_dest(tgt), gg_get_address_of(envar.field->var_decl_node), - refer_offset_source(envar), + refer_offset(envar), refer_size_source(envar), NULL_TREE)); if( error ) @@ -4449,7 +4857,7 @@ parser_accept_envar(struct cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down LABEL error->bottom") - SHOW_PARSE_LABEL(" ", error) + SHOW_PARSE_LABEL_OK(" ", error) } gg_append_statement( error->structs.arith_error->bottom.label ); } @@ -4459,7 +4867,7 @@ parser_accept_envar(struct cbl_refer_t tgt, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT("Laying down LABEL not_error->bottom") - SHOW_PARSE_LABEL(" ", not_error) + SHOW_PARSE_LABEL_OK(" ", not_error) SHOW_PARSE_END } gg_append_statement( not_error->structs.arith_error->bottom.label ); @@ -4467,7 +4875,8 @@ parser_accept_envar(struct cbl_refer_t tgt, } void -parser_set_envar( struct cbl_refer_t name, struct cbl_refer_t value ) +parser_set_envar( const struct cbl_refer_t &name, + const struct cbl_refer_t &value ) { Analyze(); SHOW_PARSE @@ -4485,10 +4894,10 @@ parser_set_envar( struct cbl_refer_t name, struct cbl_refer_t value ) gg_call(BOOL, "__gg__set_envar", gg_get_address_of(name.field->var_decl_node), - refer_offset_source(name), + refer_offset(name), refer_size_source(name), gg_get_address_of(value.field->var_decl_node), - refer_offset_source(value), + refer_offset(value), refer_size_source(value), NULL_TREE); } @@ -4508,8 +4917,8 @@ parser_accept_date_yymmdd( struct cbl_field_t *target ) tree pointer = gg_define_char_star(); gg_assign(pointer, gg_call_expr(CHAR_P, "__gg__get_date_yymmdd", + gg_get_address_of(target->var_decl_node), NULL_TREE)); - gg_default_qualification(target); move_tree_to_field( target, pointer); @@ -4538,8 +4947,8 @@ parser_accept_date_yyyymmdd( struct cbl_field_t *target ) tree pointer = gg_define_char_star(); gg_assign(pointer, gg_call_expr(CHAR_P, "__gg__get_date_yyyymmdd", + gg_get_address_of(target->var_decl_node), NULL_TREE)); - gg_default_qualification(target); move_tree_to_field( target, pointer); @@ -4568,8 +4977,8 @@ parser_accept_date_yyddd( struct cbl_field_t *target ) tree pointer = gg_define_char_star(); gg_assign(pointer, gg_call_expr(CHAR_P, "__gg__get_date_yyddd", + gg_get_address_of(target->var_decl_node), NULL_TREE)); - gg_default_qualification(target); move_tree_to_field( target, pointer); @@ -4598,8 +5007,8 @@ parser_accept_date_yyyyddd( struct cbl_field_t *target ) tree pointer = gg_define_char_star(); gg_assign(pointer, gg_call_expr(CHAR_P, "__gg__get_yyyyddd", + gg_get_address_of(target->var_decl_node), NULL_TREE)); - gg_default_qualification(target); move_tree_to_field( target, pointer); @@ -4628,8 +5037,8 @@ parser_accept_date_dow( struct cbl_field_t *target ) tree pointer = gg_define_char_star(); gg_assign(pointer, gg_call_expr(CHAR_P, "__gg__get_date_dow", + gg_get_address_of(target->var_decl_node), NULL_TREE)); - gg_default_qualification(target); move_tree_to_field( target, pointer); @@ -4658,8 +5067,8 @@ parser_accept_date_hhmmssff( struct cbl_field_t *target ) tree pointer = gg_define_char_star(); gg_assign(pointer, gg_call_expr(CHAR_P, "__gg__get_date_hhmmssff", + gg_get_address_of(target->var_decl_node), NULL_TREE)); - gg_default_qualification(target); move_tree_to_field( target, pointer); @@ -4682,43 +5091,62 @@ parser_accept_date_hhmmssff( struct cbl_field_t *target ) * * The parameter is always a reference to an element in the symbol table. */ + void -parser_alphabet( cbl_alphabet_t& alphabet ) +parser_alphabet( const cbl_alphabet_t& alphabet ) { Analyze(); SHOW_PARSE { SHOW_PARSE_HEADER - fprintf(stderr, "%s\n", alphabet.name); + char *psz = xasprintf(" %s ", alphabet.name); + SHOW_PARSE_TEXT(psz); + free(psz); switch(alphabet.encoding) { + case iconv_CP1252_e: + psz = xasprintf("CP1252"); + break; case ASCII_e: - fprintf(stderr, "ASCII\n"); + psz = xasprintf("ASCII"); break; case iso646_e: - fprintf(stderr, "ISO646\n"); + psz = xasprintf("ISO646"); break; case EBCDIC_e: - fprintf(stderr, "EBCDIC\n"); + psz = xasprintf("EBCDIC"); + break; + case UTF8_e: + psz = xasprintf("UTF8"); break; case custom_encoding_e: - fprintf(stderr, "%s\n", alphabet.name); + psz = xasprintf("%s", alphabet.name); break; + default: + { const char * p = __gg__encoding_iconv_name( alphabet.encoding ); + psz = xasprintf("%s", p? p : "[unknown]"); + } } + SHOW_PARSE_TEXT(" "); + SHOW_PARSE_TEXT(psz); + free(psz); SHOW_PARSE_END } - size_t alphabet_index = symbol_index(symbol_elem_of(&alphabet)); - switch(alphabet.encoding) { + case iconv_CP1252_e: case ASCII_e: case iso646_e: case EBCDIC_e: + case UTF8_e: break; case custom_encoding_e: { +#pragma message "Verify program-id is disambiguated" + size_t alphabet_index = symbol_unique_index(symbol_elem_of(&alphabet)); + unsigned char ach[256]; tree table_type = build_array_type_nelts(UCHAR, 256); @@ -4726,27 +5154,35 @@ parser_alphabet( cbl_alphabet_t& alphabet ) for( int i=0; i<256; i++ ) { // character i has the ordinal alphabet[i] - unsigned char ch = ascii_to_internal(i); + unsigned char ch = i; - ach[ch] = (alphabet.alphabet[i]); + ach[ch] = (alphabet.collation_sequence[i]); gg_assign( gg_array_value(table256, ch), - build_int_cst_type(UCHAR, (alphabet.alphabet[i])) ); + build_int_cst_type(UCHAR, (alphabet.collation_sequence[i])) ); } + + unsigned int low_char = alphabet.low_char; + unsigned int high_char = alphabet.high_char; __gg__alphabet_create(alphabet.encoding, alphabet_index, ach, - alphabet.low_index, - alphabet.high_index); + low_char, + high_char); gg_call(VOID, "__gg__alphabet_create", build_int_cst_type(INT, alphabet.encoding), build_int_cst_type(SIZE_T, alphabet_index), gg_get_address_of(table256), - build_int_cst_type(INT, alphabet.low_index), - build_int_cst_type(INT, alphabet.high_index), + build_int_cst_type(INT, low_char), + build_int_cst_type(INT, high_char), NULL_TREE ); break; } + default: + fprintf(stderr, "%s: Program ID %s:\n", + cobol_filename(), + cbl_label_of(symbol_at(current_program_index()))->name); + gcc_unreachable(); } } @@ -4757,21 +5193,34 @@ parser_alphabet_use( cbl_alphabet_t& alphabet ) SHOW_PARSE { SHOW_PARSE_HEADER + char *psz = xasprintf(" %s ", alphabet.name); + SHOW_PARSE_TEXT(psz); + free(psz); switch(alphabet.encoding) { + case iconv_CP1252_e: + psz = xasprintf("CP1252"); + break; case ASCII_e: - fprintf(stderr, "ASCII\n"); + psz = xasprintf("ASCII"); break; case iso646_e: - fprintf(stderr, "ISO646\n"); + psz = xasprintf("ISO646"); break; case EBCDIC_e: - fprintf(stderr, "EBCDIC\n"); + psz = xasprintf("EBCDIC"); + break; + case UTF8_e: + psz = xasprintf("UTF8"); break; case custom_encoding_e: - fprintf(stderr, "%s\n", alphabet.name); + psz = xasprintf("%s", alphabet.name); break; + default: + gcc_unreachable(); } + SHOW_PARSE_TEXT(psz); + free(psz); SHOW_PARSE_END } @@ -4779,13 +5228,19 @@ parser_alphabet_use( cbl_alphabet_t& alphabet ) switch(alphabet.encoding) { + default: + gcc_unreachable(); + case iconv_CP1252_e: case ASCII_e: case iso646_e: case EBCDIC_e: + case UTF8_e: __gg__low_value_character = DEGENERATE_LOW_VALUE; __gg__high_value_character = DEGENERATE_HIGH_VALUE; gg_call(VOID, "__gg__alphabet_use", + build_int_cst_type(INT, current_encoding(display_encoding_e)), + build_int_cst_type(INT, current_encoding(national_encoding_e)), build_int_cst_type(INT, alphabet.encoding), null_pointer_node, NULL_TREE); @@ -4801,6 +5256,8 @@ parser_alphabet_use( cbl_alphabet_t& alphabet ) gg_call(VOID, "__gg__alphabet_use", + build_int_cst_type(INT, current_encoding(display_encoding_e)), + build_int_cst_type(INT, current_encoding(national_encoding_e)), build_int_cst_type(INT, alphabet.encoding), build_int_cst_type(SIZE_T, alphabet_index), NULL_TREE); @@ -4864,58 +5321,93 @@ parser_display_internal(tree file_descriptor, gg_call(VOID, "__gg__display_string", file_descriptor, + build_int_cst_type(INT, refer.field->codeset.encoding), build_string_literal(refer.field->data.capacity, refer.field->data.initial), build_int_cst_type(SIZE_T, refer.field->data.capacity), advance ? integer_one_node : integer_zero_node, NULL_TREE ); - cursor_at_sol = advance; } else if( refer.field->type == FldLiteralN ) { // The parser found the string of digits from the source code and converted - // it to a _Float128. + // it to a 128-bit binary floating point number. // The bad news is that something like 555.55 can't be expressed exactly; // internally it is 555.5499999999.... - // The good news is that we know any string of 33 or fewer digits is - // converted to _Float128 and then converted back again, you get the same - // string. + // The good news is that we know any string of 33 or fewer decimal digits + // can be converted to and from IEEE 754 binary128 without being changes // We make use of that here char ach[128]; - strfromf128(ach, sizeof(ach), "%.33E", refer.field->data.value_of()); - char *p = strchr(ach, 'E'); + real_to_decimal (ach, TREE_REAL_CST_PTR (refer.field->data.value_of()), + sizeof(ach), 33, 0); + char *p = strchr(ach, 'e'); if( !p ) { // Probably INF -INF NAN or -NAN, so ach has our result + // Except that real_to_decimal prints -0.0 and 0.0 like that with + // no e. + if( ach[0] == '0' || ( ach[0] == '-' && ach[1] == '0' )) + __gg__remove_trailing_zeroes(ach); } else { - p += 1; - int exp = atoi(p); + int exp = atoi(p+1); if( exp >= 6 || exp <= -5 ) { // We are going to stick with the E notation, so ach has our result + // Except that real_to_decimal prints with e notation rather than E + // and doesn't guarantee at least two exponent digits. + *p = 'E'; + if( exp < 0 && exp >= -9 ) + { + p[1] = '-'; + p[2] = '0'; + p[3] = '0' - exp; + p[4] = '\0'; + } + else if( exp >= 0 && exp <= 9 ) + { + p[1] = '+'; + p[2] = '0'; + p[3] = '0' + exp; + p[4] = '\0'; + } } - else + else if (exp == 0) + { + p[-1] = '\0'; + } + else if (exp < 0) + { + p[-1] = '\0'; + char *q = strchr (ach, '.'); + char dig = q[-1]; + q[-1] = '\0'; + char tem[132]; + snprintf (tem, 132, "%s0.%0*d%c%s", ach, -exp - 1, 0, dig, q + 1); + strcpy (ach, tem); + } + else // if (exp > 0) { - int precision = 32 - exp; - char achFormat[24]; - sprintf(achFormat, "%%.%df", precision); - strfromf128(ach, sizeof(ach), achFormat, refer.field->data.value_of()); + p[-1] = '\0'; + char *q = strchr (ach, '.'); + for (int i = 0; i != exp; ++i) + q[i] = q[i + 1]; + q[exp] = '.'; } __gg__remove_trailing_zeroes(ach); } if( symbol_decimal_point() == ',' ) { - char *p = strchr(ach, '.' ); - if( p ) + char *pdot = strchr(ach, '.' ); + if( pdot ) { - *p = symbol_decimal_point(); + *pdot = symbol_decimal_point(); } } @@ -4950,7 +5442,7 @@ parser_display_internal(tree file_descriptor, gg_call(VOID, "__gg__display", gg_get_address_of(refer.field->var_decl_node), - refer_offset_source(refer), + refer_offset(refer), refer_size_source( refer), file_descriptor, advance ? integer_one_node : integer_zero_node, @@ -4974,10 +5466,27 @@ parser_display_field(cbl_field_t *field) void parser_display( const struct cbl_special_name_t *upon, - struct cbl_refer_t refs[], - size_t n, - bool advance ) + const std::vector<cbl_refer_t> &refs, + bool advance, + const cbl_label_t *not_error, + const cbl_label_t *error ) { + const size_t n = refs.size(); + /* + * The first parameter to parser_display is the "device" upon which to display + * the data. Besides normal devices, these may include elements that define the + * Unix command line and environment: + * 1. ARG_NUM_e, the ARGUMENT-NUMBER + * 2. ARG_VALUE_e, the ARGUMENT-VALUE + * 3. ENV_NAME_e, the ENVIRONMENT-NAME + * 4. ENV_VALUE_e, the ENVIRONMENT-VALUE + * that need special care and feeding. + */ + + // At the present time, I am not sure what not_error and error are for + gcc_assert(!not_error); + gcc_assert(!error); + Analyze(); SHOW_PARSE { @@ -4986,7 +5495,7 @@ parser_display( const struct cbl_special_name_t *upon, for(size_t i=0; i<n; i++) { SHOW_PARSE_INDENT - SHOW_PARSE_REF("", refs[i]); + SHOW_PARSE_REF("", refs.at(i)); } if( advance ) { @@ -5018,38 +5527,106 @@ parser_display( const struct cbl_special_name_t *upon, { switch(upon->id) { + // See table 5 in the IBM Cobol For Linux x86 1.2 document. + + case STDIN_e: + case SYSIN_e: + case SYSIPT_e: + cbl_internal_error("Attempting to send to an input device."); + break; + + case C01_e: + case C02_e: + case C03_e: + case C04_e: + case C05_e: + case C06_e: + case C07_e: + case C08_e: + case C09_e: + case C10_e: + case C11_e: + case C12_e: + case CSP_e: + case S01_e: + case S02_e: + case S03_e: + case S04_e: + case S05_e: + case AFP_5A_e: + case ARG_VALUE_e: + cbl_internal_error("Not valid for DISPLAY statement."); + break; + case STDOUT_e: - case SYSOUT_e: - case SYSLIST_e: - case SYSLST_e: case CONSOLE_e: + // These are inarguably stdout gg_assign(file_descriptor, integer_one_node); break; case STDERR_e: + case SYSERR_e: + // These are inarguably stderr + gg_assign(file_descriptor, integer_two_node); + break; + + case SYSOUT_e: + case SYSLIST_e: + case SYSLST_e: case SYSPUNCH_e: case SYSPCH_e: - gg_assign(file_descriptor, integer_two_node); + // In the 21st century, when there are no longer valid assumptions to + // be made about the existence of line printers, and where things + // formerly-ubiquitous card punches no longer exist, there is a need + // for the possibility of assigning these "devices" to externally- + // determined Unix gadgetry in /dev: + gg_assign(file_descriptor, + gg_call_expr( INT, + "__gg__get_file_descriptor", + gg_string_literal(upon->os_filename), + NULL_TREE)); + needs_closing = true; break; - default: - if( upon->os_filename[0] ) - { - tree topen = gg_open( gg_string_literal(upon->os_filename), - build_int_cst_type(INT, O_APPEND|O_WRONLY)); - gg_assign(file_descriptor, topen); - needs_closing = true; - } - else - { - fprintf(stderr, "We don't know what to do in parser_display\n"); - gcc_unreachable(); - } + case ARG_NUM_e: + // Set the index number for a subsequent ACCEPT FROM ARG_VALUE_e + gg_call(VOID, + "__gg__set_arg_num", + gg_get_address_of(refs[0].field->var_decl_node), + refer_offset(refs[0]), + refer_size_source(refs[0]), + NULL_TREE); + return; + break; + + case ENV_NAME_e: + // Establish the name of an environment variable for later use with + // in either DISPLAY UPON or ACCEPT FROM + gg_call(VOID, + "__gg__set_env_name", + gg_get_address_of(refs[0].field->var_decl_node), + refer_offset(refs[0]), + refer_size_source(refs[0]), + NULL_TREE); + return; + break; + + case ENV_VALUE_e: + // Set the contents of the environment variable named with ENV_NAME_e + gg_call(VOID, + "__gg__set_env_value", + gg_get_address_of(refs[0].field->var_decl_node), + refer_offset(refs[0]), + refer_size_source(refs[0]), + NULL_TREE); + return; + break; } } else { - gg_assign(file_descriptor,integer_one_node); // stdout is file descriptor 1. + // stdout is file descriptor 1. + gg_assign(file_descriptor, integer_one_node); } for(size_t i=0; i<n-1; i++) @@ -5058,18 +5635,117 @@ parser_display( const struct cbl_special_name_t *upon, parser_display_internal(file_descriptor, refs[i], DISPLAY_NO_ADVANCE); } CHECK_FIELD(refs[n-1].field); - parser_display_internal(file_descriptor, refs[n-1], advance ? DISPLAY_ADVANCE : DISPLAY_NO_ADVANCE); - + parser_display_internal(file_descriptor, + refs[n-1], + advance ? DISPLAY_ADVANCE : DISPLAY_NO_ADVANCE); if( needs_closing ) { - tree tclose = gg_close(file_descriptor); - // We are ignoring the close() return value - gg_append_statement(tclose); + gg_close(file_descriptor); } cursor_at_sol = advance; } +static +bool // Returns false for literals; true for named variables +get_exhibit_name(tree file_descriptor, const cbl_refer_t &arg) + { + bool retval; + if( is_literal(arg.field) ) + { + // If something is a literal, we just display the literal value + parser_display_internal(file_descriptor, + arg, + DISPLAY_NO_ADVANCE); + retval = false; + } + else + { + // It's not a literal, so we show its name, and the names or literal + // values) of any qualifier subscripts or refmods + gg_write( file_descriptor, + gg_string_literal(arg.field->name), + build_int_cst_type(SIZE_T, strlen(arg.field->name)) ); + + if( arg.subscripts.size() ) + { + // This refer has subscripts: + gg_write( file_descriptor, + gg_string_literal("("), + integer_one_node ); + for(size_t i=0; i<arg.subscripts.size(); i++) + { + if( i > 0 ) + { + gg_write( file_descriptor, + gg_string_literal(","), + integer_one_node ); + } + get_exhibit_name(file_descriptor, arg.subscripts[i]); + } + gg_write( file_descriptor, + gg_string_literal(")"), + integer_one_node ); + } + if( arg.refmod.from || arg.refmod.len ) + { + gg_write( file_descriptor, + gg_string_literal("("), + integer_one_node ); + if( arg.refmod.from ) + { + get_exhibit_name(file_descriptor, *(arg.refmod.from)); + } + gg_write( file_descriptor, + gg_string_literal(":"), + integer_one_node ); + if( arg.refmod.len ) + { + get_exhibit_name(file_descriptor, *(arg.refmod.len)); + } + gg_write( file_descriptor, + gg_string_literal(")"), + integer_one_node ); + } + retval = true; + } + return retval; + } + +void +parser_exhibit( bool /*changed*/, bool /*named*/, + const std::vector<cbl_refer_t> &args ) + { + tree file_descriptor = gg_define_int(); + gg_assign(file_descriptor, integer_one_node); // stdout is file descriptor 1. + + for(size_t i=0; i<args.size(); i++) + { + CHECK_FIELD(args[i].field); + if(i > 0) + { + // When there more than one argument, the second through Nth get a space + // in front of them. + gg_write( file_descriptor, + gg_string_literal(" "), + integer_one_node); + } + if( get_exhibit_name(file_descriptor, args[i]) ) + { + gg_write( file_descriptor, + gg_string_literal("="), + integer_one_node); + parser_display_internal(file_descriptor, + args[i], + DISPLAY_NO_ADVANCE); + } + } + gg_write( file_descriptor, + gg_string_literal("\n"), + integer_one_node); + cursor_at_sol = true; + } + static tree get_literalN_value(cbl_field_t *var) { @@ -5104,7 +5780,8 @@ parser_assign( size_t nC, cbl_num_result_t *C, { TRACE1_HEADER char ach[32]; - sprintf(ach, "%ld target%s", nC, nC==1 ? "" : "s"); + sprintf(ach, HOST_SIZE_T_PRINT_DEC " target%s", + (fmt_size_t)nC, nC==1 ? "" : "s"); TRACE1_TEXT(ach); if( on_error ) { @@ -5123,7 +5800,8 @@ parser_assign( size_t nC, cbl_num_result_t *C, TRACE1 { char ach[48]; - sprintf(ach, "Processing target number %ld", i); + sprintf(ach, "Processing target number " HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)i); TRACE1_INDENT TRACE1_TEXT(ach); } @@ -5340,12 +6018,12 @@ parser_assign( size_t nC, cbl_num_result_t *C, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT(" Laying down on_error GOTO into") - SHOW_PARSE_LABEL(" ", on_error) + SHOW_PARSE_LABEL_OK(" ", on_error) } IF( gg_bitwise_or(error_flag, compute_error->structs.compute_error->compute_error_code), - ne_op, - integer_zero_node ) + ne_op, + integer_zero_node ) { gg_append_statement( on_error->structs.arith_error->into.go_to ); } @@ -5371,7 +6049,7 @@ parser_assign( size_t nC, cbl_num_result_t *C, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT(" Laying down not_error GOTO into") - SHOW_PARSE_LABEL(" ", not_error) + SHOW_PARSE_LABEL_OK(" ", not_error) } IF( compute_error->structs.compute_error->compute_error_code, eq_op, integer_zero_node ) { @@ -5387,7 +6065,7 @@ parser_assign( size_t nC, cbl_num_result_t *C, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT(" Laying down on_error LABEL BOTTOM:") - SHOW_PARSE_LABEL(" ", on_error) + SHOW_PARSE_LABEL_OK(" ", on_error) } gg_append_statement( on_error->structs.arith_error->bottom.label ); } @@ -5398,7 +6076,7 @@ parser_assign( size_t nC, cbl_num_result_t *C, { SHOW_PARSE_INDENT SHOW_PARSE_TEXT(" Laying down not_error LABEL BOTTOM:") - SHOW_PARSE_LABEL(" ", not_error) + SHOW_PARSE_LABEL_OK(" ", not_error) } gg_append_statement( not_error->structs.arith_error->bottom.label ); } @@ -5410,16 +6088,16 @@ parser_assign( size_t nC, cbl_num_result_t *C, } static cbl_figconst_t -is_figconst(cbl_field_t *field) +is_figconst_t(const cbl_field_t *field) { cbl_figconst_t figconst = (cbl_figconst_t)(field->attr & FIGCONST_MASK); return figconst; } static cbl_figconst_t -is_figconst(cbl_refer_t &sourceref) +is_figconst(const cbl_refer_t &sourceref) { - return is_figconst(sourceref.field); + return is_figconst_t(sourceref.field); } void @@ -5673,16 +6351,24 @@ parser_initialize_table(size_t nelem, } typedef size_t span_t[2]; static_assert(sizeof(spans[0]) == sizeof(span_t), "pair size wrong"); - static tree tspans = gg_define_variable(SIZE_T_P, "..pit_v1", vs_file_static); - static tree ttbls = gg_define_variable(SIZE_T_P, "..pit_v2", vs_file_static); - gg_assign(tspans, build_array_of_size_t(2*nspan, (const size_t *)spans)); - gg_assign(ttbls, build_array_of_size_t(2*ntbl, (const size_t *)tbls)); + static tree tspans = gg_define_variable(SIZE_T_P, + "..pit_v1", + vs_file_static); + static tree ttbls = gg_define_variable(SIZE_T_P, + "..pit_v2", +vs_file_static); + gg_assign(tspans, + build_array_of_size_t(2*nspan, + reinterpret_cast<const size_t *>(spans))); + gg_assign(ttbls, + build_array_of_size_t(2*ntbl, + reinterpret_cast<const size_t *>(tbls))); gg_call(VOID, "__gg__mirror_range", build_int_cst_type(SIZE_T, nelem), gg_get_address_of(src.field->var_decl_node), - refer_offset_source(src), + refer_offset(src), build_int_cst_type(SIZE_T, nspan), tspans, build_int_cst_type(SIZE_T, table), @@ -5720,7 +6406,7 @@ tree_type_from_field_type(cbl_field_t *field, size_t &nbytes) case FldNumericDisplay: case FldNumericBinary: case FldPacked: - if( field->data.digits > 18 ) + if( field->data.digits > 18 ) { retval = UINT128; nbytes = 16; @@ -5773,19 +6459,19 @@ tree_type_from_field_type(cbl_field_t *field, size_t &nbytes) break; default: - cbl_internal_error( "%s(): Invalid field type %s:", + cbl_internal_error( "%s: Invalid field type %s:", __func__, cbl_field_type_str(field->type)); break; } - } - if( retval == SIZE_T && field->attr & signable_e ) - { - retval = SSIZE_T; - } - if( retval == UINT128 && field->attr & signable_e ) - { - retval = INT128; + if( retval == SIZE_T && field->attr & signable_e ) + { + retval = SSIZE_T; + } + if( retval == UINT128 && field->attr & signable_e ) + { + retval = INT128; + } } return retval; } @@ -5801,24 +6487,25 @@ restore_local_variables() static inline bool is_valuable( cbl_field_type_t type ) { + /* The name of this routine is a play on words, in English. It doesn't + mean "Is worth a lot". It means "Can be converted to a value." */ switch ( type ) { case FldInvalid: case FldGroup: case FldAlphanumeric: case FldNumericEdited: - case FldAlphaEdited: case FldLiteralA: case FldClass: case FldConditional: case FldForward: case FldSwitch: case FldDisplay: - case FldBlob: return false; // These are variable types that have to be converted from their // COBOL form to a little-endian binary representation so that they // can be conveyed BY CONTENT/BY VALUE in a CALL or user-defined // function activation. + case FldAlphaEdited: case FldNumericDisplay: case FldNumericBinary: case FldFloat: @@ -5829,22 +6516,22 @@ is_valuable( cbl_field_type_t type ) { case FldPointer: return true; } - cbl_internal_error( "%s:%d: invalid symbol_type_t %d", __func__, __LINE__, type ); + cbl_internal_error( "%s:%d: invalid %<symbol_type_t%> %d", __func__, __LINE__, type ); return false; } -void parser_sleep(cbl_refer_t seconds) +void parser_sleep(const cbl_refer_t &seconds) { if( seconds.field ) { gg_get_address_of(seconds.field->var_decl_node); - //refer_offset_source(seconds); + //refer_offset(seconds); //refer_size_source(seconds); gg_call(VOID, "__gg__sleep", gg_get_address_of(seconds.field->var_decl_node), - refer_offset_source(seconds), + refer_offset(seconds), refer_size_source(seconds), NULL_TREE); } @@ -5853,7 +6540,7 @@ void parser_sleep(cbl_refer_t seconds) // This is a naked place-holding CONTINUE. Generate some do-nothing // code that will stick some .LOC information into the assembly language, // so that GDB-COBOL can display the CONTINUE statement. - gg_assign(var_decl_nop, build_int_cst_type(INT, 103)); + insert_nop(103); } } @@ -5873,7 +6560,7 @@ parser_exit_program(void) // exits back to COBOL only, else continue static void -pe_stuff(cbl_refer_t refer, ec_type_t ec) +program_end_stuff(cbl_refer_t refer, ec_type_t ec) { // This is the moral equivalent of a C "return xyz;". @@ -5896,9 +6583,6 @@ pe_stuff(cbl_refer_t refer, ec_type_t ec) gg_assign(retval, gg_cast(return_type, integer_zero_node)); - gg_modify_function_type(current_function->function_decl, - return_type); - if( is_valuable( field_type ) ) { // The field being returned is numeric. @@ -5939,12 +6623,12 @@ pe_stuff(cbl_refer_t refer, ec_type_t ec) tree array_type = build_array_type_nelts(UCHAR, returner->data.capacity); - tree retval = gg_define_variable(array_type, vs_static); - gg_memcpy(gg_get_address_of(retval), + tree array = gg_define_variable(array_type, vs_static); + gg_memcpy(gg_get_address_of(array), member(returner->var_decl_node, "data"), member(returner->var_decl_node, "capacity")); - tree actual = gg_cast(COBOL_FUNCTION_RETURN_TYPE, gg_get_address_of(retval)); + tree actual = gg_cast(COBOL_FUNCTION_RETURN_TYPE, gg_get_address_of(array)); restore_local_variables(); gg_return(actual); @@ -5964,7 +6648,7 @@ pe_stuff(cbl_refer_t refer, ec_type_t ec) } void -parser_exit( cbl_refer_t refer, ec_type_t ec ) +parser_exit( const cbl_refer_t& refer, ec_type_t ec ) { Analyze(); SHOW_PARSE @@ -6001,7 +6685,7 @@ parser_exit( cbl_refer_t refer, ec_type_t ec ) IF( current_function->called_by_main_counter, eq_op, integer_zero_node ) { // This function wasn't called by main, so we treat it like a GOBACK - pe_stuff(refer, ec); + program_end_stuff(refer, ec); } ELSE { @@ -6012,7 +6696,7 @@ parser_exit( cbl_refer_t refer, ec_type_t ec ) // This was a recursive call into the function originally called by // main. Because we are under the control of a calling program, we // treat this like a GOBACK - pe_stuff(refer, ec); + program_end_stuff(refer, ec); } ELSE { @@ -6037,7 +6721,7 @@ parser_exit( cbl_refer_t refer, ec_type_t ec ) { } ENDIF - pe_stuff(refer, ec); + program_end_stuff(refer, ec); } } @@ -6147,19 +6831,19 @@ parser_allocate(cbl_refer_t size_or_based, cbl_field_t *f_working = current_options().initial_working(); cbl_field_t *f_local = current_options().initial_local(); - int default_byte = wsclear() ? *wsclear() : -1; + unsigned int default_byte = wsclear() ? *wsclear() : (uint32_t)(-1); gg_call(VOID, "__gg__allocate", gg_get_address_of(size_or_based.field->var_decl_node), - refer_offset_source(size_or_based) , + refer_offset(size_or_based) , initialized ? integer_one_node : integer_zero_node, build_int_cst_type(INT, default_byte), f_working ? gg_get_address_of(f_working->var_decl_node) : null_pointer_node, f_local ? gg_get_address_of(f_local-> var_decl_node) : null_pointer_node, returning.field ? gg_get_address_of(returning.field->var_decl_node) : null_pointer_node, - returning.field ? refer_offset_source(returning) + returning.field ? refer_offset(returning) : size_t_zero_node, NULL_TREE); walk_initialization(size_or_based.field, initialized, false); @@ -6177,20 +6861,175 @@ parser_free( size_t n, cbl_refer_t refers[] ) gcc_assert( ! p->is_refmod_reference() ); if( !(p->field->type == FldPointer || p->addr_of || (p->field->attr & based_e)) ) { - dbgmsg("Deallocating %s means it has to be FldPointer or addr_of or based_e"); + dbgmsg("Deallocating %s means it has to be FldPointer or addr_of or based_e", + p->field->name); } gcc_assert( p->field->type == FldPointer || p->addr_of || (p->field->attr & based_e) ); gg_call(VOID, "__gg__deallocate", gg_get_address_of(p->field->var_decl_node), - refer_offset_source(*p), + refer_offset(*p), p->addr_of ? integer_one_node : integer_zero_node, NULL_TREE); walk_initialization(p->field, false, true); } } +static +cbl_label_addresses_t * +label_fetch(struct cbl_label_t *label) + { + if( !label->structs.goto_trees ) + { + label->structs.goto_trees + = static_cast<cbl_label_addresses_t *> + (xmalloc(sizeof(struct cbl_label_addresses_t))); + gcc_assert(label->structs.goto_trees); + + gg_create_goto_pair(&label->structs.goto_trees->go_to, + &label->structs.goto_trees->label); + } + return label->structs.goto_trees; + } + +void +parser_xml_parse( cbl_label_t *instance, + cbl_refer_t input, + cbl_field_t *encoding, + cbl_field_t *validating, + bool returns_national, + cbl_label_t *from_proc, + cbl_label_t *to_proc ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_LABEL_OK("", instance) + SHOW_PARSE_REF(" ", input) + SHOW_PARSE_END + } + + TRACE1 + { + TRACE1_HEADER + TRACE1_END + } + + // We know that this routine comes first in the sequence, so we can + // create the goto/label pairs here: + + instance->structs.xml_parse = static_cast<struct cbl_xml_parse_t *> + (xmalloc(sizeof(struct cbl_xml_parse_t))); + gcc_assert(instance->structs.xml_parse); + + gg_create_goto_pair(&instance->structs.xml_parse->over.go_to, + &instance->structs.xml_parse->over.label); + gg_create_goto_pair(&instance->structs.xml_parse->exception.go_to, + &instance->structs.xml_parse->exception.label); + gg_create_goto_pair(&instance->structs.xml_parse->no_exception.go_to, + &instance->structs.xml_parse->no_exception.label); + + // We need to create a COBOL ENTRY point into this function. That entry + // point will be used by __gg__xml_parse to perform from_proc through to_proc + // as part of processing the libxml2 callbacks. + + char ach[64]; + static int instance_counter = 1; + sprintf(ach, + "_%s_xml_callback_%d", + current_function->our_name, + instance_counter++); + + cbl_field_t for_entry = {}; + for_entry.type = FldAlphanumeric; + for_entry.data.capacity = strlen(ach); + for_entry.data.initial = ach; + for_entry.codeset.encoding = iconv_CP1252_e; + + // build an island for the callback: + tree island_goto; + tree island_label; + gg_create_goto_pair(&island_goto, + &island_label); + + gg_append_statement(island_goto); + // This creates the separate _xml_callback function + parser_entry(&for_entry, 0, nullptr); + // When invoked, the callback performs the processing procedures + parser_perform(from_proc, to_proc); + // And then returns back to the caller + gg_return(0); + gg_append_statement(island_label); + + // With the callback in place, we are ready to call the library: + tree pcallback = gg_get_function_address(VOID, ach); + + tree erc = gg_define_int(); + gg_assign(erc, gg_call_expr(INT, + "__gg__xml_parse", + gg_get_address_of(input.field->var_decl_node), + refer_offset(input), + refer_size_source(input), + encoding ? + gg_get_address_of(encoding->var_decl_node) + : null_pointer_node, + validating ? + gg_get_address_of(validating->var_decl_node) + : null_pointer_node, + build_int_cst_type(INT, returns_national), + pcallback, + NULL_TREE)); + IF( erc, ne_op, integer_zero_node ) + { + //gg_printf("__gg__xml_parse() failed with erc %d\n", erc, NULL_TREE); + gg_append_statement(instance->structs.xml_parse->exception.go_to); + } + ELSE + { + //gg_printf("__gg__xml_parse() apparently succeeded\n", NULL_TREE); + gg_append_statement(instance->structs.xml_parse->no_exception.go_to); + } + ENDIF + } + +void +parser_xml_on_exception( cbl_label_t *instance ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_LABEL_OK(" ", instance) + SHOW_PARSE_END + } + gg_append_statement(instance->structs.xml_parse->over.go_to); + gg_append_statement(instance->structs.xml_parse->exception.label); + } + +void +parser_xml_not_exception( cbl_label_t *instance ) +{ + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_LABEL_OK(" ", instance) + SHOW_PARSE_END + } + gg_append_statement(instance->structs.xml_parse->over.go_to); + gg_append_statement(instance->structs.xml_parse->no_exception.label); + } + +void parser_xml_end( cbl_label_t *instance ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_LABEL_OK(" ", instance) + SHOW_PARSE_END + } + gg_append_statement(instance->structs.xml_parse->over.label); + } + void parser_arith_error(cbl_label_t *arithmetic_label) { @@ -6199,7 +7038,6 @@ parser_arith_error(cbl_label_t *arithmetic_label) // We are entering either SIZE ERROR or NOT SIZE ERROR code RETURN_IF_PARSE_ONLY; - set_up_on_exception_label(arithmetic_label); SHOW_PARSE { @@ -6212,6 +7050,10 @@ parser_arith_error(cbl_label_t *arithmetic_label) SHOW_PARSE_END } + CHECK_LABEL(arithmetic_label); + + set_up_on_exception_label(arithmetic_label); + // Jump over the [NOT] ON EXCEPTION code that is about to be laid down gg_append_statement( arithmetic_label->structs.arith_error->over.go_to ); // Create the label that allows the following code to be executed at @@ -6238,6 +7080,8 @@ parser_arith_error_end(cbl_label_t *arithmetic_label) SHOW_PARSE_END } + CHECK_LABEL(arithmetic_label); + // Jump to the end of the arithmetic code: gg_append_statement( arithmetic_label->structs.arith_error->bottom.go_to ); // Lay down the label that allows the ERROR/NOT ERROR instructions @@ -6317,9 +7161,10 @@ initialize_the_data() // This is one-time initialization of the libgcobol program state stack gg_call(VOID, "__gg__init_program_state", + build_int_cst_type(INT, current_encoding(display_encoding_e)), + build_int_cst_type(INT, current_encoding(national_encoding_e)), NULL_TREE); - __gg__currency_signs = __gg__ct_currency_signs; // We initialize currency both at compile time and run time __gg__currency_sign_init(); gg_call(VOID, @@ -6368,6 +7213,280 @@ initialize_the_data() } } +static +void +establish_using(size_t nusing, + cbl_ffi_arg_t args[] ) + { + if( nusing ) + { + for(size_t i=0; i<nusing; i++) + { + // This code is relevant at compile time. It takes each + // expected formal parameter and tacks it onto the end of the + // function's arguments chain. + + char *ach = xasprintf("_p_%s", args[i].refer.field->name); + + size_t nbytes = 0; + tree par_type = tree_type_from_field_type(args[i].refer.field, nbytes); + if( par_type == FLOAT ) + { + par_type = SSIZE_T; + } + if( par_type == DOUBLE ) + { + par_type = SSIZE_T; + } + if( par_type == FLOAT128 ) + { + par_type = INT128; + } + chain_parameter_to_function(current_function->function_decl, par_type, ach); + free(ach); + } + + // During the call, we saved the parameter_count and an array of variable + // lengths. We need to look at those values if, and only if, one or more + // of our USING arguments has an OPTIONAL flag or if one of our targets is + // marked as VARYING. + bool check_for_parameter_count = false; + for(size_t i=0; i<nusing; i++) + { + if( args[i].optional ) + { + check_for_parameter_count = true; + break; + } + if( args[i].refer.field->attr & any_length_e ) + { + check_for_parameter_count = true; + break; + } + } + + if( check_for_parameter_count ) + { + IF( var_decl_call_parameter_signature, + eq_op, + gg_cast(CHAR_P, current_function->function_address) ) + { + // We know to use var_decl_call_parameter_count, so unflag this + // pointer to avoid problems in the ridiculous possibility of + // COBOL-A calls C_B calls COBOL_A + gg_assign(var_decl_call_parameter_signature, + gg_cast(CHAR_P, null_pointer_node)); + } + ELSE + { + // We were apparently called by a C routine, not a COBOL routine, so + // make sure we don't get shortchanged by a count left behind from an + // earlier COBOL call. + gg_assign(var_decl_call_parameter_count, + build_int_cst_type(INT, A_ZILLION)); + } + ENDIF + } + else + { + // None of our parameters require a count, so make sure we don't get + // bamboozled by a count left behind from an earlier COBOL call. + gg_assign(var_decl_call_parameter_count, + build_int_cst_type(INT, A_ZILLION)); + } + + // There are 'nusing' elements in the PROCEDURE DIVISION USING list. + + tree parameter = NULL_TREE; + tree rt_i = gg_define_int(); + for(size_t i=0; i<nusing; i++) + { + // And this compiler code generates run-time execution code. The + // generated code picks up, at run time, the variable we just + // established in the chain at compile time. + + // It makes more sense if you don't think about it too hard. + + // We need to be able to restore prior arguments when doing recursive + // calls: + IF( member(args[i].refer.field->var_decl_node, "data"), + ne_op, + gg_cast(UCHAR_P, null_pointer_node) ) + { + gg_call(VOID, + "__gg__push_local_variable", + gg_get_address_of(args[i].refer.field->var_decl_node), + NULL_TREE); + } + ELSE + ENDIF + + tree base = gg_define_variable(UCHAR_P); + gg_assign(rt_i, build_int_cst_type(INT, i)); + //gg_printf("The rt_i counter is %d\n", rt_i, NULL_TREE); + IF( rt_i, lt_op , var_decl_call_parameter_count ) + { + if( i == 0 ) + { + // This is the first parameter. + parameter = DECL_ARGUMENTS(current_function->function_decl); + } + else + { + // These are subsequent parameters + parameter = TREE_CHAIN(parameter); + } + gg_assign(base, gg_cast(UCHAR_P, parameter)); + + if( args[i].refer.field->attr & any_length_e ) + { + // gg_printf("side channel: Length of \"%s\" is %ld\n", + // member(args[i].refer.field->var_decl_node, "name"), + // gg_array_value(var_decl_call_parameter_lengths, rt_i), + // NULL_TREE); + + // Get the length from the global lengths[] side channel. Don't + // forget to use the length mask on the table value. + gg_assign(member(args[i].refer.field->var_decl_node, "capacity"), + gg_array_value(var_decl_call_parameter_lengths, rt_i)); + } + } + ELSE + { + gg_assign(base, gg_cast(UCHAR_P, null_pointer_node)); + } + ENDIF + + // Arriving here means that we are processing an instruction like + // this: + // PROCEDURE DIVISION USING using[0] using[1] ... using using[nusing-1] + + // When __gg__call_parameter_count is equal to A_ZILLION, then this is + // an OTHER-TO-COBOL call and the var_decl_call_parameter_lengths array + // is not valid + + cbl_ffi_crv_t crv = args[i].crv; + cbl_field_t *new_var = args[i].refer.field; + + if( crv == by_value_e ) + { + switch(new_var->type) + { + case FldGroup: + case FldAlphanumeric: + case FldAlphaEdited: + case FldNumericEdited: + crv = by_reference_e; + break; + default: + break; + } + } + + if( crv == by_value_e ) + { + // 'parameter' is the 64-bit or 128-bit value that was placed on the stack + + size_t nbytes; + tree_type_from_field_type(new_var, nbytes); + tree parm = gg_define_variable(INT128); + + if( nbytes <= 8 ) + { + // Our input is a 64-bit number + if( new_var->attr & signable_e ) + { + IF( gg_bitwise_and( gg_cast(SIZE_T, base), + build_int_cst_type(SIZE_T, 0x8000000000000000ULL)), + ne_op, + gg_cast(SIZE_T, integer_zero_node) ) + { + // Our input is a negative number + gg_assign(parm, gg_cast(INT128, integer_minus_one_node)); + } + ELSE + { + // Our input is a positive number + gg_assign(parm, gg_cast(INT128, integer_zero_node)); + } + ENDIF + } + else + { + // This is a 64-bit positive number: + gg_assign(parm, gg_cast(INT128, integer_zero_node)); + } + } + // At this point, parm has been set to 0 or -1 + + gg_memcpy(gg_get_address_of(parm), + gg_get_address_of(base), + build_int_cst_type(SIZE_T, nbytes)); + + tree array_type = build_array_type_nelts(UCHAR, new_var->data.capacity); + tree data_decl_node = gg_define_variable( array_type, + NULL, + vs_static); + gg_assign( member(new_var->var_decl_node, "data"), + gg_get_address_of(data_decl_node) ); + + // And then move it into place + gg_call(VOID, + "__gg__assign_value_from_stack", + gg_get_address_of(new_var->var_decl_node), + parm, + NULL_TREE); + // We now have to handle an oddball situation. It's possible we are + // dealing with + // + // linkage section. + // 01 var1 + // 01 var2 redefines var1 + // + // If so, we have to give var2::data_pointer the same value as + // var1::data_pointer + // + size_t our_index = symbol_index(symbol_elem_of(new_var)); + size_t next_index = our_index + 1; + // Look ahead in the symbol table for the next LEVEL01/77 + for(;;) + { + symbol_elem_t *e = symbol_at(next_index); + if( e->type != SymField ) + { + break; + } + cbl_field_t *next_var = cbl_field_of(e); + if( !next_var ) + { + break; + } + if( next_var->level == LEVEL01 || next_var->level == LEVEL77 ) + { + if( next_var->parent == our_index ) + { + gg_assign(member(next_var->var_decl_node, "data"), + member(new_var->var_decl_node, "data")); + } + break; + } + next_index += 1; + } + } + else + { + // 'parameter' is a reference, so it it becomes the data member of + // the cblc_field_t COBOL variable. + gg_assign(member(args[i].field()->var_decl_node, "data"), base); + + // We need to apply base + offset to the LINKAGE variable + // and all of its children + propogate_linkage_offsets( args[i].field(), base ); + } + } + } + } + void parser_division(cbl_division_t division, cbl_field_t *returning, @@ -6421,8 +7540,6 @@ parser_division(cbl_division_t division, SHOW_PARSE_END } - gg_set_current_line_number(CURRENT_LINE_NUMBER); - if( division == data_div_e ) { Analyze(); @@ -6451,8 +7568,8 @@ parser_division(cbl_division_t division, // We need a pointer to the array of program names char ach[2*sizeof(cbl_name_t)]; sprintf(ach, - "..accessible_program_list_%ld", - current_function->our_symbol_table_index); + "..accessible_program_list_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)current_function->our_symbol_table_index); tree prog_list = gg_define_variable(build_pointer_type(CHAR_P), ach, vs_file_static); @@ -6464,8 +7581,8 @@ parser_division(cbl_division_t division, tree pointer_type = build_pointer_type(function_type); tree constructed_array_type = build_array_type_nelts(pointer_type, 1); sprintf(ach, - "..accessible_program_pointers_%ld", - current_function->our_symbol_table_index); + "..accessible_program_pointers_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)current_function->our_symbol_table_index); tree prog_pointers = gg_define_variable( build_pointer_type(constructed_array_type), ach, @@ -6501,7 +7618,7 @@ parser_division(cbl_division_t division, // UPSI 10000110 means that bits 0, 5, and 6 are on, which means that // SW-0, SW-5, and SW-6 are on. gg_call(VOID, - "__gg__set_initial_switch_value", + "__gg__onetime_initialization", NULL_TREE); // And then flag one-time initialization as having been done. @@ -6527,7 +7644,7 @@ parser_division(cbl_division_t division, // gg_printf("Somebody wants to cancel %s\n", // gg_string_literal(current_function->our_unmangled_name), // NULL_TREE); - cbl_label_t *prog = cbl_label_of(symbol_at(current_program_index())); + const cbl_label_t *prog = cbl_label_of(symbol_at(current_program_index())); size_t initializer_index = prog->initial_section; cbl_label_t *initializer = cbl_label_of(symbol_at(initializer_index)); parser_perform(initializer, true); // true means suppress nexting @@ -6548,6 +7665,10 @@ parser_division(cbl_division_t division, { parser_local_add(returning); current_function->returning = returning; + + size_t nbytes = 0; + tree returning_type = tree_type_from_field_type(returning, nbytes); + gg_modify_function_type(current_function->function_decl, returning_type); } // Stash the returning variables for use during parser_return() @@ -6564,289 +7685,6 @@ parser_division(cbl_division_t division, // length. We establish those lengths based on the types of the target // for each USING. - for(size_t i=0; i<nusing; i++) - { - // This code is relevant at compile time. It takes each - // expected formal parameter and tacks it onto the end of the - // function's arguments chain. - - char ach[2*sizeof(cbl_name_t)]; - sprintf(ach, "_p_%s", args[i].refer.field->name); - - size_t nbytes = 0; - tree par_type = tree_type_from_field_type(args[i].refer.field, nbytes); - if( par_type == FLOAT ) - { - par_type = SSIZE_T; - } - if( par_type == DOUBLE ) - { - par_type = SSIZE_T; - } - if( par_type == FLOAT128 ) - { - par_type = INT128; - } - chain_parameter_to_function(current_function->function_decl, par_type, ach); - } - - bool check_for_parameter_count = false; - - if( nusing ) - { - // During the call, we saved the parameter_count and an array of variable - // lengths. We need to look at those values if, and only if, one or more - // of our USING arguments has an OPTIONAL flag or if one of our targets is - // marked as VARYING. - for(size_t i=0; i<nusing; i++) - { - if( args[i].optional ) - { - check_for_parameter_count = true; - break; - } - if( args[i].refer.field->attr & any_length_e ) - { - check_for_parameter_count = true; - break; - } - } - - if( check_for_parameter_count ) - { - IF( var_decl_call_parameter_signature, - eq_op, - gg_cast(CHAR_P, current_function->function_address) ) - { - // We know to use var_decl_call_parameter_count, so unflag this - // pointer to avoid problems in the ridiculous possibility of - // COBOL-A calls C_B calls COBOL_A - gg_assign(var_decl_call_parameter_signature, - gg_cast(CHAR_P, null_pointer_node)); - } - ELSE - { - // We were apparently called by a C routine, not a COBOL routine, so - // make sure we don't get shortchanged by a count left behind from an - // earlier COBOL call. - gg_assign(var_decl_call_parameter_count, - build_int_cst_type(INT, A_ZILLION)); - } - ENDIF - } - else - { - // None of our parameters require a count, so make sure we don't get - // bamboozled by a count left behind from an earlier COBOL call. - gg_assign(var_decl_call_parameter_count, - build_int_cst_type(INT, A_ZILLION)); - } - - // There are 'nusing' elements in the PROCEDURE DIVISION USING list. - - tree parameter; - tree rt_i = gg_define_int(); - for(size_t i=0; i<nusing; i++) - { - // And this compiler code generates run-time execution code. The - // generated code picks up, at run time, the variable we just - // established in the chain at compile time. - - // It makes more sense if you don't think about it too hard. - - // We need to be able to restore prior arguments when doing recursive - // calls: - IF( member(args[i].refer.field->var_decl_node, "data"), - ne_op, - gg_cast(UCHAR_P, null_pointer_node) ) - { - gg_call(VOID, - "__gg__push_local_variable", - gg_get_address_of(args[i].refer.field->var_decl_node), - NULL_TREE); - } - ELSE - ENDIF - - tree base = gg_define_variable(UCHAR_P); - gg_assign(rt_i, build_int_cst_type(INT, i)); - //gg_printf("The rt_i counter is %d\n", rt_i, NULL_TREE); - IF( rt_i, lt_op , var_decl_call_parameter_count ) - { - if( i == 0 ) - { - // This is the first parameter. - parameter = DECL_ARGUMENTS(current_function->function_decl); - } - else - { - // These are subsequent parameters - parameter = TREE_CHAIN(parameter); - } - gg_assign(base, gg_cast(UCHAR_P, parameter)); - - IF( gg_call_expr( CHAR_P, - "getenv", - gg_string_literal("PARAMETERS_ON_ENTRY"), - NULL_TREE), - ne_op, - gg_cast(CHAR_P, null_pointer_node)); - { - gg_printf("parameter_on_entry: %s(): %d %p\n", - gg_string_literal(current_function->our_unmangled_name), - build_int_cst_type(INT, i+1), - base, - NULL_TREE); - } - ELSE - ENDIF - - if( args[i].refer.field->attr & any_length_e ) - { - //gg_printf("side channel 0x%lx\n", gg_array_value(var_decl_call_parameter_lengths, rt_i), NULL_TREE); - - // Get the length from the global lengths[] side channel. Don't - // forget to use the length mask on the table value. - gg_assign(member(args[i].refer.field->var_decl_node, "capacity"), - gg_array_value(var_decl_call_parameter_lengths, rt_i)); - } - } - ELSE - { - gg_assign(base, gg_cast(UCHAR_P, null_pointer_node)); - } - ENDIF - - // Arriving here means that we are processing an instruction like - // this: - // PROCEDURE DIVISION USING using[0] using[1] ... using using[nusing-1] - - // When __gg__call_parameter_count is equal to A_ZILLION, then this is - // an OTHER-TO-COBOL call and the var_decl_call_parameter_lengths array - // is not valid - - cbl_ffi_crv_t crv = args[i].crv; - cbl_field_t *new_var = args[i].refer.field; - - if( crv == by_value_e ) - { - switch(new_var->type) - { - case FldGroup: - case FldAlphanumeric: - case FldAlphaEdited: - case FldNumericEdited: - crv = by_reference_e; - break; - default: - break; - } - } - - if( crv == by_value_e ) - { - // 'parameter' is the 64-bit or 128-bit value that was placed on the stack - - size_t nbytes; - tree_type_from_field_type(new_var, nbytes); - tree parm = gg_define_variable(INT128); - - if( nbytes <= 8 ) - { - // Our input is a 64-bit number - if( new_var->attr & signable_e ) - { - IF( gg_bitwise_and( gg_cast(SIZE_T, base), - build_int_cst_type(SIZE_T, 0x8000000000000000ULL)), - ne_op, - gg_cast(SIZE_T, integer_zero_node) ) - { - // Our input is a negative number - gg_assign(parm, gg_cast(INT128, integer_minus_one_node)); - } - ELSE - { - // Our input is a positive number - gg_assign(parm, gg_cast(INT128, integer_zero_node)); - } - ENDIF - } - else - { - // This is a 64-bit positive number: - gg_assign(parm, gg_cast(INT128, integer_zero_node)); - } - } - // At this point, parm has been set to 0 or -1 - - gg_memcpy(gg_get_address_of(parm), - gg_get_address_of(base), - build_int_cst_type(SIZE_T, nbytes)); - - tree array_type = build_array_type_nelts(UCHAR, new_var->data.capacity); - tree data_decl_node = gg_define_variable( array_type, - NULL, - vs_static); - gg_assign( member(new_var->var_decl_node, "data"), - gg_get_address_of(data_decl_node) ); - - // And then move it into place - gg_call(VOID, - "__gg__assign_value_from_stack", - gg_get_address_of(new_var->var_decl_node), - parm, - NULL_TREE); - // We now have to handle an oddball situation. It's possible we are - // dealing with - // - // linkage section. - // 01 var1 - // 01 var2 redefines var1 - // - // If so, we have to give var2::data_pointer the same value as - // var1::data_pointer - // - cbl_field_t *next_var; - size_t our_index = symbol_index(symbol_elem_of(new_var)); - size_t next_index = our_index + 1; - // Look ahead in the symbol table for the next LEVEL01/77 - for(;;) - { - symbol_elem_t *e = symbol_at(next_index); - if( e->type != SymField ) - { - break; - } - next_var = cbl_field_of(e); - if( !next_var ) - { - break; - } - if( next_var->level == LEVEL01 || next_var->level == LEVEL77 ) - { - if( next_var->parent == our_index ) - { - gg_assign(member(next_var->var_decl_node, "data"), - member(new_var->var_decl_node, "data")); - } - break; - } - next_index += 1; - } - } - else - { - // 'parameter' is a reference, so it it becomes the data member of - // the cblc_field_t COBOL variable. - gg_assign(member(args[i].field()->var_decl_node, "data"), base); - - // We need to apply base + offset to the LINKAGE variable - // and all of its children - propogate_linkage_offsets( args[i].field(), base ); - } - } - } - gg_call(VOID, "__gg__pseudo_return_bookmark", NULL_TREE); @@ -6892,6 +7730,30 @@ parser_division(cbl_division_t division, ENDIF } ENDIF + // The first token_location that the parser establishes is caused by the + // parser scanning all of the lines in the source code. This messes up the + // logic for backing up one line, which is needed to correctly step through + // COBOL code with GDB-COBOL. So, we clear it here. + current_location_minus_one_clear(); + + // It is at this point that we check to see if the call to this function + // is a re-entry because of an ENTRY statement: + + IF( var_decl_entry_label, ne_op, null_pointer_node ) + { + // This is an ENTRY re-entry. The processing of USING variables was + // done in parser_entry, so now we jump to the label + static tree loc = gg_define_variable(VOID_P, vs_static); + gg_assign(loc, var_decl_entry_label); + gg_assign(var_decl_entry_label, gg_cast(VOID_P, null_pointer_node)); + gg_goto(loc); + } + ELSE + { + } + ENDIF + + establish_using(nusing, args); } } @@ -6987,20 +7849,20 @@ parser_logop( struct cbl_field_t *tgt, if( tgt->type != FldConditional ) { - cbl_internal_error("parser_logop() was called with variable %s on line %d" - ", which is not a FldConditional\n", + cbl_internal_error("%<parser_logop()%> was called with variable %s on line %d" + ", which is not a FldConditional", tgt->name, cobol_location().first_line); } if( a && a->type != FldConditional ) { - cbl_internal_error("parser_logop() was called with variable %s on line %d" - ", which is not a FldConditional\n", + cbl_internal_error("%<parser_logop()%> was called with variable %s on line %d" + ", which is not a FldConditional", a->name, cobol_location().first_line); } if( b && b->type != FldConditional ) { - cbl_internal_error("parser_logop() was called with variable %s on line %d" - ", which is not a FldConditional\n", + cbl_internal_error("%<parser_logop()%> was called with variable %s on line %d" + ", which is not a FldConditional", b->name, cobol_location().first_line); } @@ -7106,9 +7968,9 @@ parser_relop( cbl_field_t *tgt, if( tgt->type != FldConditional ) { - cbl_internal_error("parser_relop() was called with variable %s, " - "which is not a FldConditional\n", - tgt->name); + cbl_internal_error("%<parser_relop%> was called with variable %qs, " + "which is not a FldConditional", + tgt->name); } static tree comp_res = gg_define_variable(INT, "..pr_comp_res", vs_file_static); @@ -7170,8 +8032,8 @@ parser_relop_long(cbl_field_t *tgt, if( tgt->type != FldConditional ) { - cbl_internal_error("parser_relop() was called with variable %s, " - "which is not a FldConditional\n", + cbl_internal_error("%<parser_relop()%> was called with variable %s, " + "which is not a FldConditional", tgt->name); } @@ -7180,8 +8042,7 @@ parser_relop_long(cbl_field_t *tgt, get_binary_value( tree_b, NULL, bref.field, - refer_offset_source(bref) ); - + refer_offset(bref) ); static tree comp_res = gg_define_variable(LONG, "..prl_comp_res", vs_file_static); gg_assign(comp_res, gg_subtract(tree_a, tree_b)); @@ -7216,8 +8077,8 @@ parser_if( struct cbl_field_t *conditional ) if( conditional->type != FldConditional ) { - cbl_internal_error("parser_if() was called with variable %s, " - "which is not a FldConditional\n", + cbl_internal_error("%<parser_if()%> was called with variable %s, " + "which is not a FldConditional", conditional->name); } @@ -7302,7 +8163,7 @@ parser_see_stop_run(struct cbl_refer_t exit_status, get_binary_value( returned_value, NULL, exit_status.field, - refer_offset_source(exit_status)); + refer_offset(exit_status)); TRACE1 { TRACE1_REFER(" exit_status ", exit_status, "") @@ -7329,21 +8190,6 @@ parser_see_stop_run(struct cbl_refer_t exit_status, gg_exit(returned_value); } -static -cbl_label_addresses_t * -label_fetch(struct cbl_label_t *label) - { - if( !label->structs.goto_trees ) - { - label->structs.goto_trees - = (cbl_label_addresses_t *)xmalloc(sizeof(struct cbl_label_addresses_t) ); - - gg_create_goto_pair(&label->structs.goto_trees->go_to, - &label->structs.goto_trees->label); - } - return label->structs.goto_trees; - } - void parser_label_label(struct cbl_label_t *label) { @@ -7354,15 +8200,18 @@ parser_label_label(struct cbl_label_t *label) SHOW_PARSE_HEADER SHOW_PARSE_LABEL("", label) char ach[32]; - sprintf(ach, " label is at %p", (void*)label); + sprintf(ach, " label is at %p", static_cast<void*>(label)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " label->proc is %p", (void*)label->structs.proc); + if( label ) + { + sprintf(ach, + " label->proc is %p", + static_cast<void*>(label->structs.proc)); + } SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } - CHECK_LABEL(label); - TRACE1 { TRACE1_HEADER @@ -7370,6 +8219,10 @@ parser_label_label(struct cbl_label_t *label) TRACE1_END } + CHECK_LABEL(label); + + label_verify.lay(label); + if(strcmp(label->name, "_end_declaratives") == 0 ) { suppress_cobol_entry_point = false; @@ -7381,21 +8234,25 @@ void parser_label_goto(struct cbl_label_t *label) { label->used = yylineno; + Analyze(); SHOW_PARSE { SHOW_PARSE_HEADER SHOW_PARSE_LABEL(" ", label) char ach[32]; - sprintf(ach, " label is at %p", (void*)label); + sprintf(ach, " label is at %p", static_cast<void*>(label)); SHOW_PARSE_TEXT(ach) - sprintf(ach, " label->proc is %p", (void*)label->structs.proc); + if( label ) + { + sprintf(ach, + " label->proc is %p", + static_cast<void*>(label->structs.proc)); + } SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } - CHECK_LABEL(label); - TRACE1 { TRACE1_HEADER @@ -7403,7 +8260,13 @@ parser_label_goto(struct cbl_label_t *label) TRACE1_END } - if(strcmp(label->name, "_end_declaratives") == 0 ) + CHECK_LABEL(label); + + label_verify.go_to(label); + + label_verify.go_to(label); + + if( strcmp(label->name, "_end_declaratives") == 0 ) { suppress_cobol_entry_point = true; } @@ -7462,25 +8325,26 @@ parser_setop( struct cbl_field_t *tgt, member(candidate, "data"), member(candidate, "capacity"), member(domain, "initial"), + build_int_cst_type(INT, + domain->codeset.encoding), NULL_TREE), ne_op, integer_zero_node)); break; default: - dbgmsg("###### %10s in %s:%d\n", __func__, __FILE__, __LINE__ ); - cbl_internal_error( - "###### candidate %s has unimplemented CVT_type %d(%s)\n", - candidate->name, - candidate->type, - cbl_field_type_str(candidate->type)); + dbgmsg("%10s in %s:%d", __func__, __FILE__, __LINE__ ); + cbl_internal_error("candidate %s has unimplemented %<CVT_type%> %d(%s)", + candidate->name, + candidate->type, + cbl_field_type_str(candidate->type)); gcc_unreachable(); break; } break; default: - dbgmsg("###### %10s in %s:%d\n", __func__, __FILE__, __LINE__ ); - cbl_internal_error("###### unknown setop_t code %d\n", op); + dbgmsg("%10s in %s:%d", __func__, __FILE__, __LINE__ ); + cbl_internal_error("unknown %<setop_t%> code %d", op); gcc_unreachable(); break; } @@ -7488,7 +8352,7 @@ parser_setop( struct cbl_field_t *tgt, void parser_classify( cbl_field_t *tgt, - cbl_refer_t candidate, + const cbl_refer_t &candidate, enum classify_t type ) { Analyze(); @@ -7517,7 +8381,7 @@ parser_classify( cbl_field_t *tgt, "__gg__classify", build_int_cst_type(INT, type), gg_get_address_of(candidate.field->var_decl_node), - refer_offset_dest(candidate), + refer_offset(candidate), refer_size_dest(candidate), NULL_TREE), ne_op, @@ -7533,9 +8397,9 @@ parser_classify( cbl_field_t *tgt, } void -parser_perform(struct cbl_perform_tgt_t *tgt, struct cbl_refer_t how_many) +parser_perform(const cbl_perform_tgt_t *tgt, cbl_refer_t how_many) { - cbl_field_t *N = how_many.field; + const cbl_field_t *N = how_many.field; // No SHOW_PARSE here; we want to fall through: if( !tgt->to() ) { @@ -7585,14 +8449,6 @@ create_iline_address_pairs(struct cbl_perform_tgt_t *tgt) gg_create_goto_pair(&tgt->addresses.setup.go_to, &tgt->addresses.setup.label); - - // Even in -O0 compilations, the compiler does some elementary optimizations - // around JMP instructions. We have the SETUP code for in-line performats - // in an island at the end of the loop code. With this intervention, NEXTing - // through the code shows you the final statement of the loop before the - // loop actually starts. - - tgt->addresses.line_number_of_setup_code = gg_get_current_line_number(); } void @@ -7606,7 +8462,7 @@ parser_perform_start( struct cbl_perform_tgt_t *tgt ) { SHOW_PARSE_TEXT(" cbl_perform_tgt_t is at") char ach[32]; - sprintf(ach, " %p", (void*)tgt); + sprintf(ach, " %p", static_cast<void*>(tgt)); SHOW_PARSE_TEXT(ach); SHOW_PARSE_LABEL(" ", tgt->from()) if( tgt->to() ) @@ -7655,7 +8511,7 @@ parser_perform_start( struct cbl_perform_tgt_t *tgt ) // Give GDB-COBOL something to chew on when NEXTing. This instruction will // get the line number of the PERFORM N TIMES code. gg_append_statement(tgt->addresses.top.label); - gg_assign(var_decl_nop, build_int_cst_type(INT, 104)); + insert_nop(104); } void @@ -7667,17 +8523,18 @@ parser_perform_conditional( struct cbl_perform_tgt_t *tgt ) SHOW_PARSE_HEADER SHOW_PARSE_TEXT(" cbl_perform_tgt_t is at") char ach[32]; - sprintf(ach, " %p", (void*)tgt); + sprintf(ach, " %p", static_cast<void*>(tgt)); SHOW_PARSE_TEXT(ach); SHOW_PARSE_END } - size_t i = tgt->addresses.number_of_conditionals; + unsigned int i = tgt->addresses.number_of_conditionals; if( !(i < MAXIMUM_UNTILS) ) { - cbl_internal_error("%s:%d: %zu exceeds MAXIMUM_UNTILS of %d, line %d", - __func__, __LINE__, i, MAXIMUM_UNTILS, CURRENT_LINE_NUMBER); + cbl_internal_error("%s:%d: %u exceeds %<MAXIMUM_UNTILS%> of %d, line %d", + __func__, __LINE__, + i, MAXIMUM_UNTILS, CURRENT_LINE_NUMBER); } gcc_assert(i < MAXIMUM_UNTILS); @@ -7716,12 +8573,12 @@ parser_perform_conditional_end( struct cbl_perform_tgt_t *tgt ) SHOW_PARSE_HEADER SHOW_PARSE_TEXT(" cbl_perform_tgt_t is at") char ach[32]; - sprintf(ach, " %p", (void*)tgt); + sprintf(ach, " %p", static_cast<void*>(tgt)); SHOW_PARSE_TEXT(ach); SHOW_PARSE_END } - size_t i = tgt->addresses.number_of_conditionals; + unsigned int i = tgt->addresses.number_of_conditionals; gcc_assert(i); // We need to cap off the prior conditional in this chain of conditionals @@ -7781,8 +8638,8 @@ perform_outofline_before_until(struct cbl_perform_tgt_t *tgt, char ach[256]; size_t our_pseudo_label = pseudo_label++; sprintf(ach, - "_proccallb.%ld:", - our_pseudo_label); + "_proccallb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); gg_insert_into_assembler( ach ); parser_if(varys[0].until); @@ -7804,8 +8661,9 @@ perform_outofline_before_until(struct cbl_perform_tgt_t *tgt, // Label the bottom of the PERFORM gg_append_statement( tgt->addresses.exit.label ); sprintf(ach, - "_procretb.%ld:", - our_pseudo_label); + "_procretb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); + token_location_override(current_location_minus_one()); gg_insert_into_assembler( ach ); } @@ -7836,8 +8694,8 @@ perform_outofline_after_until(struct cbl_perform_tgt_t *tgt, char ach[256]; size_t our_pseudo_label = pseudo_label++; sprintf(ach, - "_proccallb.%ld:", - our_pseudo_label); + "_proccallb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); gg_insert_into_assembler( ach ); create_iline_address_pairs(tgt); @@ -7867,8 +8725,9 @@ perform_outofline_after_until(struct cbl_perform_tgt_t *tgt, // Label the bottom of the PERFORM gg_append_statement( tgt->addresses.exit.label ); sprintf(ach, - "_procretb.%ld:", - our_pseudo_label); + "_procretb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); + token_location_override(current_location_minus_one()); gg_insert_into_assembler( ach ); } @@ -7932,8 +8791,8 @@ perform_outofline_testafter_varying(struct cbl_perform_tgt_t *tgt, char ach[256]; size_t our_pseudo_label = pseudo_label++; sprintf(ach, - "_proccallb.%ld:", - our_pseudo_label); + "_proccallb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); gg_insert_into_assembler( ach ); create_iline_address_pairs(tgt); @@ -7987,8 +8846,9 @@ perform_outofline_testafter_varying(struct cbl_perform_tgt_t *tgt, // Arriving here means that we all of the conditions were // true. So, we're done. sprintf(ach, - "_procretb.%ld:", - our_pseudo_label); + "_procretb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); + token_location_override(current_location_minus_one()); gg_insert_into_assembler( ach ); } @@ -8049,8 +8909,8 @@ perform_outofline_before_varying( struct cbl_perform_tgt_t *tgt, char ach[256]; size_t our_pseudo_label = pseudo_label++; sprintf(ach, - "_proccallb.%ld:", - our_pseudo_label); + "_proccallb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); gg_insert_into_assembler( ach ); // Initialize all varying: @@ -8130,8 +8990,9 @@ perform_outofline_before_varying( struct cbl_perform_tgt_t *tgt, // We have, you see, reached the egress: gg_append_statement( tgt->addresses.exit.label ); sprintf(ach, - "_procretb.%ld:", - our_pseudo_label); + "_procretb." HOST_SIZE_T_PRINT_DEC ":", + (fmt_size_t)our_pseudo_label); + token_location_override(current_location_minus_one()); gg_insert_into_assembler( ach ); } @@ -8211,8 +9072,6 @@ perform_inline_until( struct cbl_perform_tgt_t *tgt, GOTO TOP EXIT: */ - gg_set_current_line_number(cobol_location().last_line); - gg_append_statement(tgt->addresses.test.label); // Go to where the conditional is recalculated.... @@ -8327,8 +9186,6 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, parser_move(varys[i].varying, varys[i].from); } - gg_set_current_line_number(cobol_location().last_line); - // Lay down the testing cycle: for(size_t i=0; i<N; i++) { @@ -8338,7 +9195,7 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, { SHOW_PARSE_INDENT char ach[32]; - sprintf(ach, "LABEL [%ld]:", i); + sprintf(ach, "LABEL [" HOST_SIZE_T_PRINT_DEC "]:", (fmt_size_t)i); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -8349,7 +9206,8 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, { SHOW_PARSE_INDENT char ach[32]; - sprintf(ach, "LABEL CONDINTO[%ld]:", i); + sprintf(ach, "LABEL CONDINTO[" HOST_SIZE_T_PRINT_DEC "]:", + (fmt_size_t)i); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -8360,7 +9218,8 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, { SHOW_PARSE_INDENT char ach[32]; - sprintf(ach, "LABEL CONDBACK[%ld]:", i); + sprintf(ach, "LABEL CONDBACK[" HOST_SIZE_T_PRINT_DEC "]:", + (fmt_size_t)i); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -8395,7 +9254,8 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, { SHOW_PARSE_INDENT char ach[32]; - sprintf(ach, "GOTO [%ld]:", i-1); + sprintf(ach, "GOTO [" HOST_SIZE_T_PRINT_DEC "]:", + (fmt_size_t)(i-1)); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -8429,7 +9289,8 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, { SHOW_PARSE_INDENT char ach[32]; - sprintf(ach, "GOTO [%ld]:", N-1); + sprintf(ach, "GOTO [" HOST_SIZE_T_PRINT_DEC "]:", + (fmt_size_t)(N-1)); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -8446,7 +9307,8 @@ perform_inline_testbefore_varying( struct cbl_perform_tgt_t *tgt, { SHOW_PARSE_INDENT char ach[32]; - sprintf(ach, "GOTO [%ld]:", i-1); + sprintf(ach, "GOTO [" HOST_SIZE_T_PRINT_DEC "]:", + (fmt_size_t)(i-1)); SHOW_PARSE_TEXT(ach) SHOW_PARSE_END } @@ -8635,7 +9497,7 @@ parser_perform_until( struct cbl_perform_tgt_t *tgt, SHOW_PARSE_HEADER SHOW_PARSE_TEXT(" cbl_perform_tgt_t is at") char ach[32]; - sprintf(ach, " %p", (void*)tgt); + sprintf(ach, " %p", static_cast<void*>(tgt)); SHOW_PARSE_TEXT(ach); SHOW_PARSE_LABEL(" ", tgt->from()) if( tgt->to() ) @@ -8645,9 +9507,6 @@ parser_perform_until( struct cbl_perform_tgt_t *tgt, SHOW_PARSE_END } - gg_set_current_line_number(cobol_location().last_line); - gg_assign(var_decl_nop, build_int_cst_type(INT, 105)); - if( tgt->from()->type != LblLoop ) { perform_outofline( tgt, test_before, N, varys); @@ -8714,10 +9573,6 @@ parser_perform_inline_times(struct cbl_perform_tgt_t *tgt, gg_append_statement( tgt->addresses.testA.label ); gg_append_statement( tgt->addresses.test.label ); - // AT this point, we want to set the line_number to the location of the - // END-PERFORM statement. - gg_set_current_line_number(cobol_location().last_line); - gg_decrement(counter); // Do the test: IF( counter, gt_op, gg_cast(LONG, integer_zero_node) ) @@ -8748,8 +9603,6 @@ parser_perform_inline_times(struct cbl_perform_tgt_t *tgt, SHOW_PARSE_END } - int stash = gg_get_current_line_number(); - gg_set_current_line_number(tgt->addresses.line_number_of_setup_code); gg_append_statement( tgt->addresses.setup.label ); // Get the count: @@ -8780,8 +9633,6 @@ parser_perform_inline_times(struct cbl_perform_tgt_t *tgt, gg_append_statement( tgt->addresses.exit.go_to ); ENDIF - gg_set_current_line_number(stash); - SHOW_PARSE { SHOW_PARSE_INDENT @@ -8792,7 +9643,7 @@ parser_perform_inline_times(struct cbl_perform_tgt_t *tgt, } void -parser_set_conditional88( struct cbl_refer_t refer, bool which_way ) +parser_set_conditional88( const cbl_refer_t& refer, bool which_way ) { Analyze(); struct cbl_field_t *tgt = refer.field; @@ -8837,12 +9688,24 @@ parser_set_conditional88( struct cbl_refer_t refer, bool which_way ) if( !figconst ) { // We are dealing with an ordinary string. - static size_t buffer_size = 0; - static char *buffer = NULL; - size_t length = src->first.size(); - raw_to_internal(&buffer, &buffer_size, src->first.name(), length); + + // When Jim gets around to converting the domain to the target encoding, + // this code will have to be removed +#if 1 + char *fname = xstrdup(src->first.name()); + charmap_t *charmap = __gg__get_charmap(tgt->codeset.encoding); + for(size_t i=0; i<strlen(fname); i++) + { + fname[i] = charmap->mapped_character(fname[i]); + } + move_tree_to_field( parent, + build_string_literal(strlen(fname)+1, fname)); + free(fname); +#else move_tree_to_field( parent, - gg_string_literal(buffer)); + build_string_literal(src->first.size()+1, + src->first.name())); +#endif } else { @@ -8859,6 +9722,10 @@ static void set_user_status(struct cbl_file_t *file) { // This routine sets the user_status, if any, to the cblc_file_t::status + + // We have to do it this way, because in the case where the file->user_status + // is in linkage, the memory addresses can end up pointing to the wrong + // places if(file->user_status) { cbl_field_t *user_status = cbl_field_of(symbol_at(file->user_status)); @@ -8900,7 +9767,7 @@ parser_file_add(struct cbl_file_t *file) if( !file ) { - cbl_internal_error("%s(): called with NULL *file", __func__); + cbl_internal_error("%s: called with NULL *file", __func__); gcc_assert(file); } @@ -8966,8 +9833,8 @@ parser_file_add(struct cbl_file_t *file) gg_assign(array_of_keys, gg_cast(build_pointer_type(cblc_field_p_type_node), gg_malloc(build_int_cst_type(SIZE_T, - (number_of_key_fields+1) - *sizeof(void *))))); + (number_of_key_fields+1) + *int_size_in_bytes(VOID_P))))); strcpy(achName, "_"); strcat(achName, file->name); @@ -8978,8 +9845,8 @@ parser_file_add(struct cbl_file_t *file) gg_assign(key_numbers, gg_cast(build_pointer_type(INT), gg_malloc(build_int_cst_type(SIZE_T, - (number_of_key_fields+1) - *sizeof(int))))); + (number_of_key_fields+1) + *int_size_in_bytes(INT))))); strcpy(achName, "_"); strcat(achName, file->name); @@ -8991,7 +9858,7 @@ parser_file_add(struct cbl_file_t *file) gg_cast(build_pointer_type(INT), gg_malloc(build_int_cst_type(SIZE_T, (number_of_key_fields+1) - *sizeof(int))))); + *int_size_in_bytes(INT))))); size_t index = 0; for( size_t i=0; i<file->nkey; i++ ) @@ -9025,17 +9892,21 @@ parser_file_add(struct cbl_file_t *file) if(file->access == file_inaccessible_e) { cbl_internal_error( - "%s:%d file %s access mode is 'file_inaccessible_e' in %s", + "%s:%d file %s access mode is %<file_inaccessible_e%> in %s", current_filename.back().c_str(), CURRENT_LINE_NUMBER, file->name, __func__); } +#pragma message "Verify program-id is disambiguated" + size_t symbol_table_index = symbol_unique_index(symbol_elem_of(file)); + gg_call(VOID, "__gg__file_init", gg_get_address_of(new_var_decl), gg_string_literal(file->name), + build_int_cst_type(SIZE_T, symbol_table_index), array_of_keys, key_numbers, unique_flags, @@ -9052,12 +9923,15 @@ parser_file_add(struct cbl_file_t *file) build_int_cst_type(INT, (int)file->optional), build_int_cst_type(SIZE_T, varies.min), build_int_cst_type(SIZE_T, varies.max), +/* Right now, file->codeset.encoding is not being set properly. Remove this + comment and fix the following code when that's repaired. */ +// build_int_cst_type(INT, (int)file->codeset.encoding), + build_int_cst_type(INT, current_encoding(display_encoding_e)), + build_int_cst_type(INT, (int)file->codeset.alphabet), NULL_TREE); file->var_decl_node = new_var_decl; } -static void store_location_stuff(const cbl_name_t statement_name); - void parser_file_open( size_t nfiles, struct cbl_file_t *files[], int mode_char ) { @@ -9068,6 +9942,44 @@ parser_file_open( size_t nfiles, struct cbl_file_t *files[], int mode_char ) } } +static +tree get_the_filename(bool "ed_name, const cbl_file_t *file) + { + // The cbl_file_t has a cbl_field_t *filename. This can be a FldAlphanumeric. + // The runtime has a (char *)filename, so we need to + // do a runtime conversion. + + tree psz; // This is going to be either the name of the file, or the + // possible run-time environment variable that will contain + // the name of the file. + + cbl_field_t *field_of_name = symbol_field_forward(file->filename); + quoted_name = false; + if( field_of_name->type == FldForward ) + { + // The target of ASSIGN TO was unquoted, but didn't resolve to a + // cbl_field_t. This means that the name of the field is an + // environment variable that will hold the file name + psz = gg_define_char_star(); + gg_assign(psz, gg_strdup(gg_string_literal(field_of_name->name))); + } + else + { + // The name is coming from a presumably FldAlphaNumeric variable + psz = get_string_from(field_of_name); + gg_call( CHAR_P, + "__gg__convert_encoding", + psz, + build_int_cst_type(INT, + field_of_name->codeset.encoding), + build_int_cst_type(INT, + DEFAULT_SOURCE_ENCODING), + NULL_TREE); + quoted_name = true; + } + return psz; + } + void parser_file_open( struct cbl_file_t *file, int mode_char ) { @@ -9098,12 +10010,13 @@ parser_file_open( struct cbl_file_t *file, int mode_char ) if( !file ) { - cbl_internal_error("parser_file_open called with NULL *file"); + cbl_internal_error("%<parser_file_open%> called with NULL *file"); } if( !file->var_decl_node ) { - cbl_internal_error("parser_file_open for %s called with NULL var_decl_node", file->name); + cbl_internal_error("%<parser_file_open%> for %s called with NULL " + "%<var_decl_node%>", file->name); } if( mode_char == 'a' && (file->access != file_access_seq_e) ) @@ -9119,36 +10032,15 @@ parser_file_open( struct cbl_file_t *file, int mode_char ) TRACE1_END } - // The cbl_file_t has a cbl_field_t *filename. This can be a FldAlphanumeric. - // The runtime has a (char *)filename, so we need to - // do a runtime conversion. - - tree psz; // This is going to be either the name of the file, or the - // possible run-time environment variable that will contain - // the name of the file. - - cbl_field_t *field_of_name = symbol_field_forward(file->filename); - bool quoted_name = false; - if( field_of_name->type == FldForward ) - { - // The target of ASSIGN TO was unquoted, but didn't resolve to a - // cbl_field_t. This means that the name of the field is an - // environment variable that will hold the file name - psz = gg_define_char_star(); - gg_assign(psz, gg_strdup(gg_string_literal(field_of_name->name))); - } - else - { - // The name is coming from a presumably FldAlphaNumeric variable - psz = get_string_from(field_of_name); - quoted_name = true; - } + bool quoted_name; + tree pszFilename = get_the_filename(quoted_name, file); + sv_is_i_o = true; store_location_stuff("OPEN"); gg_call(VOID, "__gg__file_open", gg_get_address_of(file->var_decl_node), - psz, + pszFilename, build_int_cst_type(INT, mode_char), quoted_name ? integer_one_node : integer_zero_node, NULL_TREE); @@ -9176,12 +10068,13 @@ parser_file_close( struct cbl_file_t *file, file_close_how_t how ) if( !file ) { - cbl_internal_error("parser_file_close called with NULL *file"); + cbl_internal_error("%<parser_file_close%> called with NULL *file"); } if( !file->var_decl_node ) { - cbl_internal_error("parser_file_close for %s called with NULL file->var_decl_node", file->name); + cbl_internal_error("%<parser_file_close%> for %s called with " + "NULL %<file->var_decl_node%>", file->name); } TRACE1 @@ -9195,6 +10088,7 @@ parser_file_close( struct cbl_file_t *file, file_close_how_t how ) // We are done with the filename. The library routine will free "filename" // memory and set it back to null + sv_is_i_o = true; store_location_stuff("CLOSE"); gg_call(VOID, "__gg__file_close", @@ -9244,27 +10138,29 @@ parser_file_read( struct cbl_file_t *file, if( !file ) { - cbl_internal_error("parser_file_read called with NULL *file"); + cbl_internal_error("%<parser_file_read%> called with NULL *file"); } if( !file->var_decl_node ) { - cbl_internal_error("parser_file_read for %s called with NULL file->var_decl_node", file->name); + cbl_internal_error("%<parser_file_read%> for %s called with " + "NULL %<file->var_decl_node%>", file->name); } if( !file ) { - cbl_internal_error("parser_file_read called with NULL *field"); + cbl_internal_error("%<parser_file_read%> called with NULL *field"); } if( !file->var_decl_node ) { - cbl_internal_error("parser_file_read for %s called with NULL field->var_decl_node", file->name); + cbl_internal_error("%<parser_file_read%> for %s called with " + "NULL %<field->var_decl_node%>", file->name); } if( file->access == file_access_seq_e && where >= 0) { - cbl_internal_error("%s:%d file %s is RELATIVE/SEQUENTIAL, but 'where' >= 0", + cbl_internal_error("%s:%d file %s is RELATIVE/SEQUENTIAL, but %<where >= 0%>", current_filename.back().c_str(), CURRENT_LINE_NUMBER, file->name); @@ -9273,13 +10169,14 @@ parser_file_read( struct cbl_file_t *file, if( file->access == file_access_rnd_e && where < 0) { - cbl_internal_error("%s:%d file %s is RELATIVE/RANDOM, but 'where' < 0", + cbl_internal_error("%s:%d file %s is RELATIVE/RANDOM, but %<where < 0%>", current_filename.back().c_str(), CURRENT_LINE_NUMBER, file->name); where = 1; } + sv_is_i_o = true; store_location_stuff("READ"); gg_call(VOID, "__gg__file_read", @@ -9360,23 +10257,23 @@ parser_file_write( cbl_file_t *file, if( !file ) { - cbl_internal_error("%s(): called with NULL *file", __func__); + cbl_internal_error("%s: called with NULL *file", __func__); } if( !file->var_decl_node ) { - cbl_internal_error("%s(): for %s called with NULL file->var_decl_node", + cbl_internal_error("%s: for %s called with NULL %<file->var_decl_node%>", __func__, file->name); } if( !file ) { - cbl_internal_error("%s(): called with NULL *field", __func__); + cbl_internal_error("%s: called with NULL *field", __func__); } if( !file->var_decl_node ) { - cbl_internal_error( "%s(): for %s called with NULL field->var_decl_node", + cbl_internal_error( "%s: for %s called with NULL %<field->var_decl_node%>", __func__, file->name); } @@ -9388,7 +10285,7 @@ parser_file_write( cbl_file_t *file, get_binary_value( value, NULL, advance.field, - refer_offset_source(advance)); + refer_offset(advance)); gg_assign(t_advance, gg_cast(INT, value)); } else @@ -9414,6 +10311,7 @@ parser_file_write( cbl_file_t *file, record_area = cbl_field_of(symbol_at(file->default_record)); } + sv_is_i_o = true; store_location_stuff("WRITE"); gg_call(VOID, "__gg__file_write", @@ -9457,13 +10355,19 @@ void parser_file_delete( struct cbl_file_t *file, bool /*sequentially*/ ) { Analyze(); + + if( !file ) + { + cbl_internal_error("The file pointer should not be null"); + abort(); // Because cppcheck doesn't recognize [[noerror]] + } + bool sequentially = file->access == file_access_seq_e || file->org == file_sequential_e || file->org == file_line_sequential_e; SHOW_PARSE { SHOW_PARSE_HEADER - if(file) { SHOW_PARSE_TEXT(" "); SHOW_PARSE_TEXT(file->name); @@ -9476,13 +10380,10 @@ parser_file_delete( struct cbl_file_t *file, bool /*sequentially*/ ) SHOW_PARSE_TEXT(" sequentially") } } - else - { - SHOW_PARSE_TEXT(" *file is NULL") - } SHOW_PARSE_END } + sv_is_i_o = true; store_location_stuff("DELETE"); gg_call(VOID, "__gg__file_delete", @@ -9500,6 +10401,121 @@ parser_file_delete( struct cbl_file_t *file, bool /*sequentially*/ ) } } +static void +set_up_delete_file_label(cbl_label_t *delete_file_label) + { + if( delete_file_label ) + { + if( !delete_file_label->structs.delete_file ) + { + delete_file_label->structs.delete_file + = static_cast<cbl_delete_file_t *> + (xmalloc(sizeof(struct cbl_delete_file_t))); + // Set up the address pairs for this clause + gg_create_goto_pair( + &delete_file_label->structs.delete_file->over.go_to, + &delete_file_label->structs.delete_file->over.label); + gg_create_goto_pair( + &delete_file_label->structs.delete_file->exception.go_to, + &delete_file_label->structs.delete_file->exception.label); + gg_create_goto_pair( + &delete_file_label->structs.delete_file->no_exception.go_to, + &delete_file_label->structs.delete_file->no_exception.label); + gg_create_goto_pair( + &delete_file_label->structs.delete_file->bottom.go_to, + &delete_file_label->structs.delete_file->bottom.label); + } + } + } + +void +parser_file_delete_file( cbl_label_t *name, + std::vector<cbl_file_t*> filenames ) + { + // This removes a file from the file system. It is distinct from the + // FILE DELETE statement, which deletes a record from a file. + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT(" "); + SHOW_PARSE_TEXT(name->name); + for(size_t i=0; i<filenames.size(); i++) + { + SHOW_PARSE_INDENT + SHOW_PARSE_TEXT(filenames[i]->name) + } + SHOW_PARSE_END + } + set_up_delete_file_label(name); + tree there_was_an_error = gg_define_int(0); + for(size_t i=0; i<filenames.size(); i++) + { + bool quoted_name; + tree pszFilename = get_the_filename(quoted_name, filenames[i]); + gg_assign(there_was_an_error, + gg_bitwise_or(there_was_an_error, + gg_call_expr( + INT, + "__gg__file_remove", + gg_get_address_of(filenames[i]->var_decl_node), + pszFilename, + quoted_name ? integer_one_node : integer_zero_node, + NULL_TREE))); + set_user_status(filenames[i]); + } + IF( there_was_an_error, eq_op, integer_zero_node ) + { + // There was no error detected. + gg_append_statement(name->structs.delete_file->no_exception.go_to); + } + ELSE + { + // There was an error detected. + gg_append_statement(name->structs.delete_file->exception.go_to); + } + } + +void +parser_file_delete_on_exception( cbl_label_t *name ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT(" "); + SHOW_PARSE_TEXT(name->name); + SHOW_PARSE_END + } + gg_append_statement(name->structs.delete_file->bottom.go_to); + gg_append_statement(name->structs.delete_file->exception.label); + } + +void +parser_file_delete_not_exception( cbl_label_t *name ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT(" "); + SHOW_PARSE_TEXT(name->name); + SHOW_PARSE_END + } + gg_append_statement(name->structs.delete_file->bottom.go_to); + gg_append_statement(name->structs.delete_file->no_exception.label); + } + +void +parser_file_delete_end( cbl_label_t *name ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT(" "); + SHOW_PARSE_TEXT(name->name); + SHOW_PARSE_END + } + gg_append_statement(name->structs.delete_file->bottom.label); + } + void parser_file_rewrite(cbl_file_t *file, cbl_field_t *record_area, @@ -9539,6 +10555,7 @@ parser_file_rewrite(cbl_file_t *file, record_area = cbl_field_of(symbol_at(file->default_record)); } + sv_is_i_o = true; store_location_stuff("REWRITE"); gg_call(VOID, "__gg__file_rewrite", @@ -9634,8 +10651,8 @@ parser_file_start(struct cbl_file_t *file, // A key has a number of fields for(size_t ifield=0; ifield<file->keys[key_number].nfield; ifield++) { - size_t field_index = file->keys[key_number].fields[ifield]; - cbl_field_t *field = cbl_field_of(symbol_at(field_index)); + size_t nfield = file->keys[key_number].fields[ifield]; + cbl_field_t *field = cbl_field_of(symbol_at(nfield)); combined_length += field->data.capacity; } gg_assign(length, build_int_cst_type(SIZE_T, combined_length)); @@ -9645,9 +10662,10 @@ parser_file_start(struct cbl_file_t *file, get_binary_value( length, NULL, length_ref.field, - refer_offset_dest(length_ref)); + refer_offset(length_ref)); } + sv_is_i_o = true; store_location_stuff("START"); gg_call(VOID, "__gg__file_start", @@ -9661,15 +10679,53 @@ parser_file_start(struct cbl_file_t *file, static void inspect_tally(bool backward, - cbl_refer_t identifier_1, - unsigned long n_identifier_2, - cbx_inspect_t<cbl_refer_t>* identifier_2) + const cbl_refer_t &identifier_1, + cbl_inspect_opers_t& identifier_2) { Analyze(); // This is an INSPECT FORMAT 1 SHOW_PARSE { SHOW_PARSE_HEADER + char ach[128]; + sprintf(ach, "There are %lu identifier_2", gb4(identifier_2.size())); + SHOW_PARSE_TEXT(ach); + for(size_t i=0; i<identifier_2.size(); i++) + { + SHOW_PARSE_INDENT + sprintf(ach, "%lu: bounds: %lu", gb4(i), gb4(identifier_2[i].nbound())); + SHOW_PARSE_TEXT(ach); + for(size_t j=0; j<identifier_2[i].nbound(); j++) + { + SHOW_PARSE_INDENT + sprintf(ach, " %lu: matches: %lu", + gb4(j), gb4(identifier_2[i][j].matches.size())); + SHOW_PARSE_TEXT(ach); + + SHOW_PARSE_INDENT + if( identifier_2[i][j].bound == bound_characters_e ) + { + SHOW_PARSE_TEXT(" bound_characters"); + } + else + { + SHOW_PARSE_TEXT(" bound_leading/all"); + } + + if( identifier_2[i][j].matches.size() ) + { + SHOW_PARSE_INDENT + sprintf(ach, " before %p", + as_voidp(identifier_2.at(i).at(j).matches.at(0).before.identifier_4.field)); + SHOW_PARSE_TEXT(ach); + SHOW_PARSE_INDENT + sprintf(ach, " after %p", + as_voidp(identifier_2.at(i).at(j).matches.at(0).after.identifier_4.field)); + SHOW_PARSE_TEXT(ach); + } + } + } + SHOW_PARSE_END } @@ -9679,6 +10735,7 @@ inspect_tally(bool backward, size_t int_index = 0; size_t pcbl_index = 0; + unsigned long n_identifier_2 = identifier_2.size(); // The first integer is the all-important controlling count: int_index++; @@ -9692,12 +10749,11 @@ inspect_tally(bool backward, pcbl_index++; // For each FOR there is a count of the loops after the FOR int_index++; - for(size_t j=0; j<identifier_2[i].nbound; j++) + for(size_t j=0; j<identifier_2[i].nbound(); j++) { - // After each identifier-2, there is a cbl_inspect_bound_t value: int_index++; - if( identifier_2[i].opers[j].bound == bound_characters_e) + if( identifier_2[i][j].bound == bound_characters_e) { // This is a FOR CHARACTERS PHRASE1, so we will need before/after // for each: @@ -9708,7 +10764,7 @@ inspect_tally(bool backward, { // This is ALL or LEADING. Each has some number of identifier-3 int_index++; - for(size_t k=0; k<identifier_2[i].opers[j].n_identifier_3; k++) + for(size_t k=0; k<identifier_2[i][j].n_identifier_3(); k++) { // Put identifier-3 into the array: pcbl_index++; @@ -9735,15 +10791,17 @@ inspect_tally(bool backward, gg_assign(int_size, build_int_cst_type(INT, n_integers)); gg_assign(integers, gg_cast(SIZE_T_P, - gg_realloc(integers, n_integers * sizeof(void *)))); + gg_realloc(integers, + n_integers + * int_size_in_bytes(VOID_P)))); } ELSE { } ENDIF - size_t n_resolveds = pcbl_index; - cbl_refer_t *pcbl_refers = (cbl_refer_t *)xmalloc(n_resolveds * sizeof(cbl_refer_t)); + const size_t n_resolveds = pcbl_index; + std::vector<cbl_refer_t> pcbl_refers(n_resolveds); // Now we make a second pass, populating those arrays: int_index = 0; @@ -9762,34 +10820,42 @@ inspect_tally(bool backward, pcbl_refers[pcbl_index++] = identifier_2[i].tally; // For each FOR there is a count of the loops after the FOR gg_assign( gg_array_value(integers, int_index++), - build_int_cst_type(SIZE_T, identifier_2[i].nbound) ); - for(size_t j=0; j<identifier_2[i].nbound; j++) + build_int_cst_type(SIZE_T, identifier_2[i].nbound()) ); + for(size_t j=0; j<identifier_2[i].nbound(); j++) { // After each identifier-2, there is a cbl_inspect_bound_t value: gg_assign( gg_array_value(integers, int_index++), - build_int_cst_type(SIZE_T, identifier_2[i].opers[j].bound)); - if( identifier_2[i].opers[j].bound == bound_characters_e) + build_int_cst_type(SIZE_T, identifier_2[i][j].bound)); + if( identifier_2[i][j].bound == bound_characters_e) { // This is a FOR CHARACTERS PHRASE1, so we will need before/after // for each: - pcbl_refers[pcbl_index++] = identifier_2[i].opers[j].matches[0].before.identifier_4; - pcbl_refers[pcbl_index++] = identifier_2[i].opers[j].matches[0].after.identifier_4; + const auto& m( identifier_2[i][j].matches ); + if( m.empty() ) + { + pcbl_index += 2; + } + else + { + pcbl_refers[pcbl_index++] = m[0].before.identifier_4; + pcbl_refers[pcbl_index++] = m[0].after.identifier_4; + } } else { // This is ALL or LEADING. Each has some number of identifier-3 gg_assign( gg_array_value(integers, int_index++), - build_int_cst_type(SIZE_T, identifier_2[i].opers[j].n_identifier_3)); - for(size_t k=0; k<identifier_2[i].opers[j].n_identifier_3; k++) + build_int_cst_type(SIZE_T, identifier_2[i][j].n_identifier_3())); + for(size_t k=0; k<identifier_2[i][j].n_identifier_3(); k++) { // Put identifier-3 into the array: - pcbl_refers[pcbl_index++] = identifier_2[i].opers[j].matches[k].matching; + pcbl_refers[pcbl_index++] = identifier_2[i][j].matches[k].matching(); // We need the PHRASE1 for that identifier-3 - pcbl_refers[pcbl_index++] = identifier_2[i].opers[j].matches[k].before.identifier_4; + pcbl_refers[pcbl_index++] = identifier_2[i][j].matches[k].before.identifier_4; - pcbl_refers[pcbl_index++] = identifier_2[i].opers[j].matches[k].after.identifier_4; + pcbl_refers[pcbl_index++] = identifier_2[i][j].matches[k].after.identifier_4; } } } @@ -9801,7 +10867,7 @@ inspect_tally(bool backward, gcc_assert(pcbl_index == n_resolveds); // We have built up an array of integers, and an array of cbl_refer_t. - build_array_of_treeplets(1, pcbl_index, pcbl_refers); + build_array_of_treeplets(1, pcbl_index, pcbl_refers.data()); // Do the actual call: gg_call(VOID, @@ -9809,16 +10875,12 @@ inspect_tally(bool backward, backward ? integer_one_node : integer_zero_node, integers, NULL_TREE); - - // And free up the memory we allocated - free(pcbl_refers); } static void inspect_replacing(int backward, - cbl_refer_t identifier_1, - unsigned long n_ops, - cbx_inspect_t<cbl_refer_t>* operations) + const cbl_refer_t &identifier_1, + cbl_inspect_opers_t &operations) { Analyze(); // This is an INSPECT FORMAT 2 @@ -9829,6 +10891,7 @@ inspect_replacing(int backward, } // For REPLACING, unlike TALLY, there can be but one operation + unsigned long n_ops = operations.size(); gcc_assert(n_ops == 1); size_t n_id_3 = 0; @@ -9839,9 +10902,9 @@ inspect_replacing(int backward, // Make one pass through the inputs to count up the sizes of the arrays // we will be passing to the library routines: - for( size_t j=0; j<operations[0].nbound; j++) + for( size_t j=0; j<operations[0].nbound(); j++) { - if( operations[0].opers[j].bound == bound_characters_e) + if( operations[0][j].bound == bound_characters_e) { // This is a FOR CHARACTERS phrase @@ -9860,13 +10923,13 @@ inspect_replacing(int backward, // The n_identifier-3 values will go into the resolved values; we have to // leave room for them - n_id_3 += operations[0].opers[j].n_identifier_3; + n_id_3 += operations[0][j].n_identifier_3(); // Likewise identifier-5 values: - n_id_5 += operations[0].opers[j].n_identifier_3; + n_id_5 += operations[0][j].n_identifier_3(); // And each identifier-3 / identifier-5 pair has BEFORE and AFTER phrases: - n_id_4 += 2 * operations[0].opers[j].n_identifier_3; + n_id_4 += 2 * operations[0][j].n_identifier_3(); } } @@ -9874,8 +10937,8 @@ inspect_replacing(int backward, // all the integers and cbl_inspect_bound_t values, in a strict sequence so // that the library routine can peel them off. - size_t n_integers = 1 // Room for operations[0].nbound - + operations[0].nbound // Room for all the cbl_inspect_bound_t values + size_t n_integers = 1 // Room for operations[0].nbound() + + operations[0].nbound() // Room for all the cbl_inspect_bound_t values + n_all_leading_first; // Room for all of the n_identifier_3 counts static tree int_size = gg_define_variable(INT, "..pir_size", vs_file_static, 0); @@ -9886,19 +10949,21 @@ inspect_replacing(int backward, gg_assign(int_size, build_int_cst_type(INT, n_integers)); gg_assign(integers, gg_cast(SIZE_T_P, - gg_realloc(integers, n_integers * sizeof(void *)))); + gg_realloc(integers, + n_integers + * int_size_in_bytes(VOID_P)))); } ELSE { } ENDIF - size_t n_resolveds = 1 // Room for identifier-1 + const size_t n_resolveds = 1 // Room for identifier-1 + n_id_3 // Room for the identifier-3 variables + n_id_4 // Room for the identifier-4 variables + n_id_5; // Room for the identifier-5 variables - cbl_refer_t *pcbl_refers = (cbl_refer_t *)xmalloc(n_resolveds * sizeof(cbl_refer_t)); + std::vector<cbl_refer_t> pcbl_refers(n_resolveds); // Now we make a second pass, populating those arrays: size_t int_index = 0; @@ -9906,28 +10971,28 @@ inspect_replacing(int backward, // The first integer is the all-important controlling count: gg_assign( gg_array_value(integers, int_index++), - build_int_cst_type(SIZE_T, operations[0].nbound) ); + build_int_cst_type(SIZE_T, operations[0].nbound()) ); // The first refer is for identifier-1 pcbl_refers[pcbl_index++] = identifier_1; - for( size_t j=0; j<operations[0].nbound; j++) + for( size_t j=0; j<operations[0].nbound(); j++) { // For each FOR there is a count of the loops after the FOR // For each operation, there is a cbl_inspect_bound_t value: gg_assign( gg_array_value(integers, int_index++), - build_int_cst_type(SIZE_T, operations[0].opers[j].bound)); - if( operations[0].opers[j].bound == bound_characters_e) + build_int_cst_type(SIZE_T, operations[0][j].bound)); + if( operations[0][j].bound == bound_characters_e) { // This is a FOR CHARACTERS PHRASE1 // Put in the identifier-5 replacement value: - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[0].replacement; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[0].replacement; // Each identifier-5 gets a PHRASE1: - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[0].before.identifier_4; - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[0].after.identifier_4; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[0].before.identifier_4; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[0].after.identifier_4; SHOW_PARSE { @@ -9935,14 +11000,14 @@ inspect_replacing(int backward, { SHOW_PARSE_INDENT } - SHOW_PARSE_FIELD("ID-5 ", operations[0].opers[j].replaces[0].replacement.field) - if(operations[0].opers[j].replaces[0].before.identifier_4.field) + SHOW_PARSE_FIELD("ID-5 ", operations[0][j].replaces[0].replacement.field) + if(operations[0][j].replaces[0].before.identifier_4.field) { - SHOW_PARSE_FIELD(" before ", operations[0].opers[j].replaces[0].before.identifier_4.field) + SHOW_PARSE_FIELD(" before ", operations[0][j].replaces[0].before.identifier_4.field) } - if(operations[0].opers[j].replaces[0].after.identifier_4.field) + if(operations[0][j].replaces[0].after.identifier_4.field) { - SHOW_PARSE_FIELD(" after ", operations[0].opers[j].replaces[0].after.identifier_4.field) + SHOW_PARSE_FIELD(" after ", operations[0][j].replaces[0].after.identifier_4.field) } SHOW_PARSE_END } @@ -9951,19 +11016,19 @@ inspect_replacing(int backward, { // This is ALL or LEADING. Each has some number of identifier-3/identifier-5 pairs gg_assign( gg_array_value(integers, int_index++), - build_int_cst_type(SIZE_T, operations[0].opers[j].n_identifier_3)); - for(size_t k=0; k<operations[0].opers[j].n_identifier_3; k++) + build_int_cst_type(SIZE_T, operations[0][j].n_identifier_3())); + for(size_t k=0; k<operations[0][j].n_identifier_3(); k++) { // Put identifier-3 into the array: - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[k].matching; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[k].matching(); // Put in the identifier-5 replacement value: - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[k].replacement; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[k].replacement; // We need the PHRASE1 for that identifier-3/identifier-5 pair: - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[k].before.identifier_4; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[k].before.identifier_4; - pcbl_refers[pcbl_index++] = operations[0].opers[j].replaces[k].after.identifier_4; + pcbl_refers[pcbl_index++] = operations[0][j].replaces[k].after.identifier_4; SHOW_PARSE { @@ -9971,15 +11036,15 @@ inspect_replacing(int backward, { SHOW_PARSE_INDENT } - SHOW_PARSE_FIELD("ID-3 ", operations[0].opers[j].replaces[k].matching.field) - SHOW_PARSE_FIELD(" ID-5 ", operations[0].opers[j].replaces[k].replacement.field) - if( operations[0].opers[j].replaces[k].before.identifier_4.field ) + SHOW_PARSE_FIELD("ID-3 ", operations[0][j].replaces[k].matching().field) + SHOW_PARSE_FIELD(" ID-5 ", operations[0][j].replaces[k].replacement.field) + if( operations[0][j].replaces[k].before.identifier_4.field ) { - SHOW_PARSE_FIELD("before ", operations[0].opers[j].replaces[k].before.identifier_4.field) + SHOW_PARSE_FIELD("before ", operations[0][j].replaces[k].before.identifier_4.field) } - if(operations[0].opers[j].replaces[k].after.identifier_4.field) + if(operations[0][j].replaces[k].after.identifier_4.field) { - SHOW_PARSE_FIELD("after ", operations[0].opers[j].replaces[k].after.identifier_4.field) + SHOW_PARSE_FIELD("after ", operations[0][j].replaces[k].after.identifier_4.field) } SHOW_PARSE_END } @@ -9987,9 +11052,9 @@ inspect_replacing(int backward, } } - //fprintf(stderr, "%s(): %ld %ld\n", __func__, int_index, n_integers); + //fprintf(stderr, "%s: %ld %ld\n", __func__, int_index, n_integers); gcc_assert(int_index == n_integers); - //fprintf(stderr, "%s(): %ld %ld\n", __func__, pcbl_index, n_resolveds); + //fprintf(stderr, "%s: %ld %ld\n", __func__, pcbl_index, n_resolveds); gcc_assert(pcbl_index == n_resolveds); // We have built up an array of integers, and an array of cbl_refer_t. @@ -10004,7 +11069,7 @@ inspect_replacing(int backward, } } - build_array_of_treeplets(1, pcbl_index, pcbl_refers); + build_array_of_treeplets(1, pcbl_index, pcbl_refers.data()); // Do the actual call: gg_call(VOID, @@ -10015,13 +11080,12 @@ inspect_replacing(int backward, } void -parser_inspect(cbl_refer_t identifier_1, +parser_inspect(const cbl_refer_t& identifier_1, bool backward, - unsigned long n_operations, - cbx_inspect_t<cbl_refer_t>* operations) + cbl_inspect_opers_t& operations) { Analyze(); - gcc_assert(n_operations); + gcc_assert(! operations.empty()); /* Operating philosophy: We are going to minimize the amount of GENERIC tag creation here at compile time, mainly by eliminating @@ -10031,12 +11095,12 @@ parser_inspect(cbl_refer_t identifier_1, if( operations[0].tally.field ) { // This is a FORMAT 1 "TALLYING" - inspect_tally(backward, identifier_1, n_operations, operations); + inspect_tally(backward, identifier_1, operations); } else { // This is a FORMAT 2 "REPLACING" - inspect_replacing(backward, identifier_1, n_operations, operations); + inspect_replacing(backward, identifier_1, operations); } } @@ -10060,27 +11124,27 @@ parser_inspect_conv(cbl_refer_t input, backward ? integer_one_node : integer_zero_node, input.field ? gg_get_address_of(input.field->var_decl_node) : null_pointer_node, - refer_offset_source(input), + refer_offset(input), refer_size_source(input), original.field ? gg_get_address_of(original.field->var_decl_node) : null_pointer_node, - refer_offset_dest(original), + refer_offset(original), refer_size_dest(original), replacement.field ? gg_get_address_of( replacement.field->var_decl_node) : null_pointer_node, - refer_offset_source(replacement), + refer_offset(replacement), replacement.all ? build_int_cst_type(SIZE_T, -1LL) : refer_size_source(replacement), after.identifier_4.field ? gg_get_address_of( after.identifier_4.field->var_decl_node) : null_pointer_node, - refer_offset_source(after.identifier_4), + refer_offset(after.identifier_4), refer_size_source(after.identifier_4), before.identifier_4.field ? gg_get_address_of( before.identifier_4.field->var_decl_node) : null_pointer_node, - refer_offset_source(before.identifier_4), + refer_offset(before.identifier_4), refer_size_source(before.identifier_4), NULL_TREE ); @@ -10130,10 +11194,10 @@ parser_intrinsic_numval_c( cbl_field_t *f, "__gg__test_numval_c", gg_get_address_of(f->var_decl_node), gg_get_address_of(input.field->var_decl_node), - refer_offset_source(input), + refer_offset(input), refer_size_source(input), currency.field ? gg_get_address_of(currency.field->var_decl_node) : null_pointer_node, - refer_offset_source(currency), + refer_offset(currency), refer_size_source(currency), NULL_TREE ); @@ -10144,10 +11208,10 @@ parser_intrinsic_numval_c( cbl_field_t *f, "__gg__numval_c", gg_get_address_of(f->var_decl_node), gg_get_address_of(input.field->var_decl_node), - refer_offset_source(input), + refer_offset(input), refer_size_source(input), currency.field ? gg_get_address_of(currency.field->var_decl_node) : null_pointer_node, - refer_offset_source(currency), + refer_offset(currency), refer_size_source(currency), NULL_TREE ); @@ -10156,7 +11220,7 @@ parser_intrinsic_numval_c( cbl_field_t *f, void parser_intrinsic_subst( cbl_field_t *f, - cbl_refer_t& ref1, + const cbl_refer_t& ref1, size_t argc, cbl_substitute_t * argv ) { @@ -10164,6 +11228,13 @@ parser_intrinsic_subst( cbl_field_t *f, SHOW_PARSE { SHOW_PARSE_HEADER + SHOW_PARSE_FIELD(" TO ", f) + for(size_t i=0; i<argc; i++) + { + SHOW_PARSE_INDENT + SHOW_PARSE_FIELD(" ", argv[i].orig.field) + SHOW_PARSE_FIELD(" ", argv[i].replacement.field) + } SHOW_PARSE_END } TRACE1 @@ -10172,10 +11243,13 @@ parser_intrinsic_subst( cbl_field_t *f, TRACE1_END } + sv_is_i_o = true; store_location_stuff("SUBSTITUTE"); - unsigned char *control_bytes = (unsigned char *)xmalloc(argc * sizeof(unsigned char)); - cbl_refer_t *arg1 = (cbl_refer_t *)xmalloc(argc * sizeof(cbl_refer_t)); - cbl_refer_t *arg2 = (cbl_refer_t *)xmalloc(argc * sizeof(cbl_refer_t)); + unsigned char *control_bytes = + static_cast<unsigned char *>(xmalloc(argc * sizeof(unsigned char))); + gcc_assert(control_bytes); + std::vector<cbl_refer_t> arg1(argc); + std::vector<cbl_refer_t> arg2(argc); for(size_t i=0; i<argc; i++) { @@ -10191,14 +11265,14 @@ parser_intrinsic_subst( cbl_field_t *f, tree control = gg_array_of_bytes(argc, control_bytes); - build_array_of_treeplets(1, argc, arg1); - build_array_of_treeplets(2, argc, arg2); + build_array_of_treeplets(1, argc, arg1.data()); + build_array_of_treeplets(2, argc, arg2.data()); gg_call(VOID, "__gg__substitute", gg_get_address_of(f->var_decl_node), gg_get_address_of(ref1.field->var_decl_node), - refer_offset_source(ref1), + refer_offset(ref1), refer_size_source(ref1), build_int_cst_type(SIZE_T, argc), control, @@ -10206,8 +11280,6 @@ parser_intrinsic_subst( cbl_field_t *f, gg_free(control); - free(arg2); - free(arg1); free(control_bytes); } @@ -10231,7 +11303,8 @@ parser_intrinsic_callv( cbl_field_t *tgt, SHOW_PARSE_HEADER SHOW_PARSE_TEXT(" of ") SHOW_PARSE_TEXT(function_name) - fprintf(stderr, " with %zd parameters", nrefs); + fprintf(stderr, " with " HOST_SIZE_T_PRINT_DEC " parameters", + (fmt_size_t)nrefs); SHOW_PARSE_END } @@ -10300,7 +11373,9 @@ parser_intrinsic_call_0(cbl_field_t *tgt, { // Pass __gg__when_compiled() the time from right now. struct timespec tp; - clock_gettime(CLOCK_REALTIME, &tp); // time_t tv_sec; long tv_nsec + uint64_t now = get_time_nanoseconds(); + tp.tv_sec = now / 1000000000; + tp.tv_nsec = now % 1000000000; store_location_stuff(function_name); gg_call(VOID, @@ -10355,15 +11430,15 @@ parser_intrinsic_call_1( cbl_field_t *tgt, } size_t upper = ref1.field->occurs.bounds.upper ? ref1.field->occurs.bounds.upper : 1; - if( ref1.nsubscript ) + if( ref1.nsubscript() ) { upper = 1; } - if( is_table(ref1.field) && !ref1.nsubscript ) + if( is_table(ref1.field) && !ref1.nsubscript() ) { static tree depending_on = gg_define_variable(LONG, "..pic1_dep"); - gg_get_depending_on_value(depending_on, ref1.field); + depending_on_value(depending_on, ref1.field); gg_call(VOID, "__gg__int128_to_field", gg_get_address_of(tgt->var_decl_node), @@ -10404,6 +11479,16 @@ parser_intrinsic_call_1( cbl_field_t *tgt, } } } + else if( strcmp(function_name, "__gg__char") == 0 ) + { + gg_call(VOID, + function_name, + gg_get_address_of(tgt->var_decl_node), + gg_get_address_of(ref1.field->var_decl_node), + refer_offset(ref1), + refer_size_source(ref1), + NULL_TREE); + } else { TRACE1 @@ -10419,7 +11504,7 @@ parser_intrinsic_call_1( cbl_field_t *tgt, function_name, gg_get_address_of(tgt->var_decl_node), gg_get_address_of(ref1.field->var_decl_node), - refer_offset_source(ref1), + refer_offset(ref1), refer_size_source(ref1), NULL_TREE); } @@ -10458,14 +11543,16 @@ parser_intrinsic_call_2( cbl_field_t *tgt, TRACE1_REFER("parameter 2: ", ref2, "") } store_location_stuff(function_name); + gg_call(VOID, function_name, gg_get_address_of(tgt->var_decl_node), gg_get_address_of(ref1.field->var_decl_node), - refer_offset_source(ref1), + refer_offset(ref1), refer_size_source(ref1), - ref2.field ? gg_get_address_of(ref2.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref2), + ref2.field ? gg_get_address_of(ref2.field->var_decl_node) + : null_pointer_node, + refer_offset(ref2), refer_size_source(ref2), NULL_TREE); TRACE1 @@ -10512,13 +11599,13 @@ parser_intrinsic_call_3( cbl_field_t *tgt, function_name, gg_get_address_of(tgt->var_decl_node), ref1.field ? gg_get_address_of(ref1.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref1), + refer_offset(ref1), refer_size_source(ref1), ref2.field ? gg_get_address_of(ref2.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref2), + refer_offset(ref2), refer_size_source(ref2), ref3.field ? gg_get_address_of(ref3.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref3), + refer_offset(ref3), refer_size_source(ref3), NULL_TREE); TRACE1 @@ -10567,16 +11654,16 @@ parser_intrinsic_call_4( cbl_field_t *tgt, function_name, gg_get_address_of(tgt->var_decl_node), ref1.field ? gg_get_address_of(ref1.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref1), + refer_offset(ref1), refer_size_source(ref1), ref2.field ? gg_get_address_of(ref2.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref2), + refer_offset(ref2), refer_size_source(ref2), ref3.field ? gg_get_address_of(ref3.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref3), + refer_offset(ref3), refer_size_source(ref3), ref4.field ? gg_get_address_of(ref4.field->var_decl_node) : null_pointer_node, - refer_offset_source(ref4), + refer_offset(ref4), refer_size_source(ref4), NULL_TREE); TRACE1 @@ -10609,7 +11696,9 @@ static void create_lsearch_address_pairs(struct cbl_label_t *name) { // Create the lsearch structure - name->structs.lsearch = (cbl_lsearch_t *)xmalloc(sizeof(cbl_lsearch_t)); + name->structs.lsearch = + static_cast<cbl_lsearch_t *>(xmalloc(sizeof(cbl_lsearch_t))); + gcc_assert(name->structs.lsearch); cbl_lsearch_t *lsearch = name->structs.lsearch; gg_create_goto_pair(&lsearch->addresses.at_exit.go_to, @@ -10673,7 +11762,7 @@ parser_lsearch_start( cbl_label_t *name, { // Extract the number of elements in that rightmost dimension. lsearch->limit = gg_define_variable(LONG); - gg_get_depending_on_value(lsearch->limit, current); + depending_on_value(lsearch->limit, current); break; } current = parent_of(current); @@ -10859,7 +11948,9 @@ parser_bsearch_start( cbl_label_t* name, } // We need a cbl_bsearch_t structure: - name->structs.bsearch = (cbl_bsearch_t *)xmalloc(sizeof(cbl_bsearch_t)); + name->structs.bsearch = + static_cast<cbl_bsearch_t *>(xmalloc(sizeof(cbl_bsearch_t))); + gcc_assert(name->structs.bsearch); cbl_bsearch_t *bsearch = name->structs.bsearch; // Create the address/label pairs we need @@ -10892,6 +11983,8 @@ parser_bsearch_start( cbl_label_t* name, current = parent_of(current); } + CHECK_FIELD(current); + // There are a number of things we learn from the field "current" // We get the index: @@ -10910,7 +12003,7 @@ parser_bsearch_start( cbl_label_t* name, // Assign the left and right values: gg_assign(bsearch->left, build_int_cst_type(LONG, 1)); - gg_get_depending_on_value(bsearch->right, current); + depending_on_value(bsearch->right, current); // Create the variable that will take the compare result. bsearch->compare_result = gg_define_int(); @@ -10999,12 +12092,11 @@ parser_bsearch_conditional( cbl_label_t* name ) } bool -is_ascending_key(cbl_refer_t key) +is_ascending_key(const cbl_refer_t& key) { bool retval = true; cbl_field_t *family_tree = key.field; - gcc_assert(family_tree); while( family_tree ) { if( family_tree->occurs.nkey ) @@ -11013,14 +12105,17 @@ is_ascending_key(cbl_refer_t key) } family_tree = parent_of(family_tree); } + + CHECK_FIELD(family_tree); gcc_assert(family_tree->occurs.nkey); + for(size_t i=0; i<family_tree->occurs.nkey; i++) { for(size_t j=0; j<family_tree->occurs.keys[i].field_list.nfield; j++) { size_t index_of_field = family_tree->occurs.keys[i].field_list.fields[j]; - cbl_field_t *key_field = cbl_field_of(symbol_at(index_of_field)); + const cbl_field_t *key_field = cbl_field_of(symbol_at(index_of_field)); if( strcmp( key_field->name, key.field->name ) == 0 ) @@ -11116,7 +12211,9 @@ gg_array_of_field_pointers( size_t N, cbl_field_t **fields ) { tree retval = gg_define_variable(build_pointer_type(cblc_field_p_type_node)); - gg_assign(retval, gg_cast(build_pointer_type(cblc_field_p_type_node), gg_malloc( build_int_cst_type(SIZE_T, N * sizeof(void *))))); + gg_assign(retval, gg_cast(build_pointer_type(cblc_field_p_type_node), + gg_malloc(build_int_cst_type(SIZE_T, + N * int_size_in_bytes(VOID_P))))); for(size_t i=0; i<N; i++) { gg_assign(gg_array_value(retval, i), gg_get_address_of(fields[i]->var_decl_node)); @@ -11144,8 +12241,7 @@ void parser_sort(cbl_refer_t tableref, bool duplicates, cbl_alphabet_t *alphabet, - size_t nkeys, - cbl_key_t *keys ) + const std::vector<cbl_key_t>& keys ) { Analyze(); SHOW_PARSE @@ -11163,22 +12259,26 @@ parser_sort(cbl_refer_t tableref, gcc_assert(table->var_decl_node); if( !is_table(table) ) { - cbl_internal_error( "%s(): asked to sort %s, but it's not a table", + cbl_internal_error( "%s: asked to sort %s, which is not a table", __func__, tableref.field->name); } - size_t total_keys = 0; - for( size_t i=0; i<nkeys; i++ ) - { - total_keys += keys[i].nfield; - } - cbl_field_t **flattened_fields = (cbl_field_t **)xmalloc(total_keys * sizeof(cbl_field_t *)); - size_t *flattened_ascending = (size_t *)xmalloc(total_keys * sizeof(size_t)); + size_t total_keys = std::accumulate( keys.begin(), keys.end(), 0, + [](size_t n, const cbl_key_t& key ) { + return n + key.fields.size(); + } ); + typedef const cbl_field_t * const_field_t; + const_field_t *flattened_fields = + static_cast<const_field_t *>(xmalloc(total_keys * sizeof(cbl_field_t *))); + gcc_assert(flattened_fields); + size_t *flattened_ascending = + static_cast<size_t *>(xmalloc(total_keys * sizeof(size_t))); + gcc_assert(flattened_ascending); size_t key_index = 0; - for( size_t i=0; i<nkeys; i++ ) + for( size_t i=0; i<keys.size(); i++ ) { - for( size_t j=0; j<keys[i].nfield; j++ ) + for( size_t j=0; j<keys[i].fields.size(); j++ ) { flattened_fields[key_index] = keys[i].fields[j]; flattened_ascending[key_index] = keys[i].ascending ? 1 : 0; @@ -11187,13 +12287,14 @@ parser_sort(cbl_refer_t tableref, } // Create the array of cbl_field_t pointers for the keys - tree all_keys = gg_array_of_field_pointers( total_keys, flattened_fields); + tree all_keys = gg_array_of_field_pointers( total_keys, + const_cast<cbl_field_t**>(flattened_fields)); // Create the array of integers that are the flags for ASCENDING: tree ascending = gg_array_of_size_t( total_keys, flattened_ascending ); tree depending_on = gg_define_variable(LONG, "_sort_size"); - gg_get_depending_on_value(depending_on, table); + depending_on_value(depending_on, table); if( alphabet ) { @@ -11203,7 +12304,7 @@ parser_sort(cbl_refer_t tableref, gg_call(VOID, "__gg__sort_table", gg_get_address_of(tableref.field->var_decl_node), - refer_offset_source(tableref), + refer_offset(tableref), gg_cast(SIZE_T, depending_on), build_int_cst_type(SIZE_T, key_index), all_keys, @@ -11226,8 +12327,7 @@ void parser_file_sort( cbl_file_t *workfile, bool duplicates, cbl_alphabet_t *alphabet, - size_t nkeys, - cbl_key_t *keys, + const std::vector<cbl_key_t>& keys, size_t ninput, cbl_file_t **inputs, size_t noutput, @@ -11291,7 +12391,7 @@ parser_file_sort( cbl_file_t *workfile, else { // Having both or neither violates SORT syntax - cbl_internal_error("%s(): syntax error -- both (or neither) USING " + cbl_internal_error("%s: syntax error: both (or neither) USING " "and input-proc are specified", __func__); } @@ -11304,18 +12404,22 @@ parser_file_sort( cbl_file_t *workfile, // clone of the code for handling multiple keys, each of which can have // multiple fields. - size_t total_keys = 0; - for( size_t i=0; i<nkeys; i++ ) - { - total_keys += keys[i].nfield; - } - cbl_field_t **flattened_fields = (cbl_field_t **)xmalloc(total_keys * sizeof(cbl_field_t *)); - size_t *flattened_ascending = (size_t *) xmalloc(total_keys * sizeof(size_t)); + size_t total_keys = std::accumulate( keys.begin(), keys.end(), 0, + []( size_t n, const cbl_key_t& key ) { + return n + key.fields.size(); + } ); + typedef const cbl_field_t * const_field_t; + auto flattened_fields + = static_cast<const_field_t *>(xmalloc(total_keys * sizeof(cbl_field_t *))); + gcc_assert(flattened_fields); + size_t *flattened_ascending = + static_cast<size_t *>(xmalloc(total_keys * sizeof(size_t))); + gcc_assert(flattened_ascending); size_t key_index = 0; - for( size_t i=0; i<nkeys; i++ ) + for( size_t i=0; i<keys.size(); i++ ) { - for( size_t j=0; j<keys[i].nfield; j++ ) + for( size_t j=0; j<keys[i].fields.size(); j++ ) { flattened_fields[key_index] = keys[i].fields[j]; flattened_ascending[key_index] = keys[i].ascending ? 1 : 0; @@ -11324,7 +12428,8 @@ parser_file_sort( cbl_file_t *workfile, } // Create the array of cbl_field_t pointers for the keys - tree all_keys = gg_array_of_field_pointers( total_keys, flattened_fields); + tree all_keys = gg_array_of_field_pointers( total_keys, + const_cast<cbl_field_t**>(flattened_fields)); // Create the array of integers that are the flags for ASCENDING: tree ascending = gg_array_of_size_t( total_keys, flattened_ascending ); @@ -11420,7 +12525,7 @@ parser_file_sort( cbl_file_t *workfile, } else { - cbl_internal_error("%s(): syntax error -- both (or neither) GIVING " + cbl_internal_error("%s: syntax error: both (or neither) GIVING " "and output-proc are specified", __func__); } } @@ -11468,7 +12573,9 @@ parser_return_start( cbl_file_t *workfile, cbl_refer_t into ) // We assume that workfile is open. - workfile->addresses = (cbl_sortreturn_t *)xmalloc(sizeof(cbl_sortreturn_t)); + workfile->addresses = static_cast<cbl_sortreturn_t *> + (xmalloc(sizeof(cbl_sortreturn_t))); + gcc_assert(workfile->addresses); gg_create_goto_pair(&workfile->addresses->at_end.go_to, &workfile->addresses->at_end.label); gg_create_goto_pair(&workfile->addresses->not_at_end.go_to, @@ -11499,7 +12606,13 @@ parser_return_start( cbl_file_t *workfile, cbl_refer_t into ) IF( member(workfile, "io_status"), lt_op, build_int_cst(INT, FsKeySeq) ) { - // The read didn't succeed because of an end-of-file condition + // The read didn't succeed because of an end-of-file condition. + + // Because there is an AT END clause, we suppress the error condition that + // was raised. + gg_assign(var_decl_exception_code, integer_zero_node); + + // And then we jump to the at_end code: gg_append_statement(workfile->addresses->at_end.go_to); } ELSE @@ -11608,7 +12721,8 @@ gg_array_of_file_pointers( size_t N, { tree retval = gg_define_variable(build_pointer_type(cblc_file_p_type_node)); gg_assign(retval, gg_cast( build_pointer_type(cblc_file_p_type_node), - gg_malloc( build_int_cst_type(SIZE_T, N * sizeof(void *))))); + gg_malloc( build_int_cst_type(SIZE_T, + N * int_size_in_bytes(VOID_P))))); for(size_t i=0; i<N; i++) { gg_assign(gg_array_value(retval, i), gg_get_address_of(files[i]->var_decl_node)); @@ -11619,8 +12733,7 @@ gg_array_of_file_pointers( size_t N, void parser_file_merge( cbl_file_t *workfile, cbl_alphabet_t *alphabet, - size_t nkeys, - cbl_key_t *keys, + const std::vector<cbl_key_t>& keys, size_t ninputs, cbl_file_t **inputs, size_t noutputs, @@ -11643,20 +12756,23 @@ parser_file_merge( cbl_file_t *workfile, build_int_cst_type(INT, file_sequential_e)); } - size_t total_keys = 0; - for( size_t i=0; i<nkeys; i++ ) - { - total_keys += keys[i].nfield; - } - cbl_field_t **flattened_fields - = (cbl_field_t **)xmalloc(total_keys * sizeof(cbl_field_t *)); + size_t total_keys = std::accumulate( keys.begin(), keys.end(), 0, + []( size_t i, const cbl_key_t& key ) { + return i + key.fields.size(); + } ); + typedef const cbl_field_t * const_field_t; + const_field_t *flattened_fields + = static_cast<const_field_t *> + (xmalloc(total_keys * sizeof(cbl_field_t *))); + gcc_assert(flattened_fields); size_t *flattened_ascending - = (size_t *)xmalloc(total_keys * sizeof(size_t)); + = static_cast<size_t *>(xmalloc(total_keys * sizeof(size_t))); + gcc_assert(flattened_ascending); size_t key_index = 0; - for( size_t i=0; i<nkeys; i++ ) + for( size_t i=0; i<keys.size(); i++ ) { - for( size_t j=0; j<keys[i].nfield; j++ ) + for( size_t j=0; j<keys[i].fields.size(); j++ ) { flattened_fields[key_index] = keys[i].fields[j]; flattened_ascending[key_index] = keys[i].ascending ? 1 : 0; @@ -11665,7 +12781,9 @@ parser_file_merge( cbl_file_t *workfile, } // Create the array of cbl_field_t pointers for the keys - tree all_keys = gg_array_of_field_pointers(total_keys, flattened_fields); + tree all_keys = gg_array_of_field_pointers( + total_keys, + const_cast<cbl_field_t**>(flattened_fields)); // Create the array of integers that are the flags for ASCENDING: tree ascending = gg_array_of_size_t(total_keys, flattened_ascending); @@ -11689,6 +12807,9 @@ parser_file_merge( cbl_file_t *workfile, ELSE ENDIF + const cbl_enabled_exceptions_t& + enabled_exceptions( cdf_enabled_exceptions() ); + for(size_t i=0; i<ninputs; i++) { if( process_this_exception(ec_sort_merge_file_open_e) ) @@ -11732,7 +12853,7 @@ parser_file_merge( cbl_file_t *workfile, gg_call(VOID, "__gg__merge_files", gg_get_address_of(workfile->var_decl_node), - build_int_cst_type(SIZE_T, nkeys), + build_int_cst_type(SIZE_T, keys.size()), all_keys, ascending, build_int_cst_type(SIZE_T, ninputs), @@ -11826,7 +12947,7 @@ parser_file_merge( cbl_file_t *workfile, } else { - cbl_internal_error("%s(): syntax error -- both (or neither) " + cbl_internal_error("%s: syntax error: both (or neither) " "files and output-proc are specified", __func__); } } @@ -11846,7 +12967,8 @@ parser_string_overflow( cbl_label_t *name ) */ name->structs.unstring - = (cbl_unstring_t *)xmalloc(sizeof(struct cbl_unstring_t) ); + = static_cast<cbl_unstring_t *>(xmalloc(sizeof(struct cbl_unstring_t))); + gcc_assert(name->structs.unstring); // Set up the address pairs for this clause gg_create_goto_pair(&name->structs.unstring->over.go_to, @@ -11904,9 +13026,9 @@ parser_unstring(cbl_refer_t src, gg_append_statement(not_overflow->structs.unstring->over.label); } - cbl_refer_t *delims = (cbl_refer_t *)xmalloc(ndelimited * sizeof(cbl_refer_t)); - char *alls = (char *)xmalloc(ndelimited+1); - + std::vector<cbl_refer_t> delims(ndelimited); + char *alls = static_cast<char *>(xmalloc(ndelimited+1)); + gcc_assert(alls); for(size_t i=0; i<ndelimited; i++) { delims[i] = delimiteds[i]; @@ -11916,7 +13038,7 @@ parser_unstring(cbl_refer_t src, tree t_alls = build_string_literal(ndelimited+1, alls); - build_array_of_treeplets(1, ndelimited, delims); + build_array_of_treeplets(1, ndelimited, delims.data()); build_array_of_treeplets(2, noutputs, outputs); build_array_of_treeplets(3, noutputs, delimiters); build_array_of_treeplets(4, noutputs, counts); @@ -11926,21 +13048,20 @@ parser_unstring(cbl_refer_t src, gg_call_expr( INT, "__gg__unstring", gg_get_address_of(src.field->var_decl_node), - refer_offset_source(src), + refer_offset(src), refer_size_source(src), build_int_cst_type(SIZE_T, ndelimited), t_alls, build_int_cst_type(SIZE_T, noutputs), pointer.field ? gg_get_address_of(pointer.field->var_decl_node) : null_pointer_node, - refer_offset_dest(pointer), + refer_offset(pointer), refer_size_dest(pointer), tally.field ? gg_get_address_of(tally.field->var_decl_node) : null_pointer_node, - refer_offset_dest(tally), + refer_offset(tally), refer_size_dest(tally), NULL_TREE) ); free(alls); - free(delims); if( overflow ) { @@ -11976,12 +13097,12 @@ parser_unstring(cbl_refer_t src, } void -parser_string( cbl_refer_t tgt, - cbl_refer_t pointer, - size_t nsource, - cbl_string_src_t *sources, - cbl_label_t *overflow, - cbl_label_t *not_overflow ) +parser_string(const cbl_refer_t& tgt, + const cbl_refer_t& pointer, + size_t nsource, + cbl_string_src_t *sources, + cbl_label_t *overflow, + cbl_label_t *not_overflow ) { SHOW_PARSE { @@ -11998,7 +13119,8 @@ parser_string( cbl_refer_t tgt, } // We need an array of nsource+1 integers: - size_t *integers = (size_t *)xmalloc((nsource+1)*sizeof(size_t)); + size_t *integers = static_cast<size_t *>(xmalloc((nsource+1)*sizeof(size_t))); + gcc_assert(integers); // Count up how many treeplets we are going to need: size_t cblc_count = 2; // tgt and pointer @@ -12007,7 +13129,7 @@ parser_string( cbl_refer_t tgt, cblc_count += 1 + sources[i].ninput; // 1 for identifier_2 + ninput identifier_1 values; } - cbl_refer_t *refers = (cbl_refer_t *)xmalloc(cblc_count * sizeof(cbl_refer_t)); + std::vector<cbl_refer_t> refers(cblc_count); size_t index_int = 0; size_t index_cblc = 0; @@ -12032,7 +13154,7 @@ parser_string( cbl_refer_t tgt, tree pintegers = build_array_of_size_t( index_int, integers); - build_array_of_treeplets(1, index_cblc, refers); + build_array_of_treeplets(1, index_cblc, refers.data()); tree t_overflow = gg_define_int(); gg_assign(t_overflow, gg_call_expr( INT, @@ -12042,7 +13164,6 @@ parser_string( cbl_refer_t tgt, gg_free(pintegers); free(integers); - free(refers); if( overflow ) { @@ -12090,8 +13211,9 @@ parser_call_exception( cbl_label_t *name ) } name->structs.call_exception - = (cbl_call_exception_t *)xmalloc(sizeof(struct cbl_call_exception_t) ); - + = static_cast<cbl_call_exception_t *> + (xmalloc(sizeof(struct cbl_call_exception_t))); + gcc_assert(name->structs.call_exception); // Set up the address pairs for this clause gg_create_goto_pair(&name->structs.call_exception->over.go_to, &name->structs.call_exception->over.label); @@ -12139,11 +13261,11 @@ static void create_and_call(size_t narg, cbl_ffi_arg_t args[], - tree function_handle, + tree function_pointer, + const char *funcname, tree returned_value_type, cbl_refer_t returned, - cbl_label_t *not_except - ) + cbl_label_t *not_except) { // We have a good function handle, so we are going to create a call tree *arguments = NULL; @@ -12151,8 +13273,10 @@ create_and_call(size_t narg, if(narg) { - arguments = (tree *)xmalloc(2*narg * sizeof(tree)); - allocated = (int * )xmalloc(narg * sizeof(int)); + arguments = static_cast<tree *>(xmalloc(2*narg * sizeof(tree))); + gcc_assert(arguments); + allocated = static_cast<int *>(xmalloc(narg * sizeof(int))); + gcc_assert(allocated); } // Put the arguments onto the "stack" of calling parameters: @@ -12202,7 +13326,7 @@ create_and_call(size_t narg, else { gg_assign(location, - qualified_data_source(args[i].refer)), + qualified_data_location(args[i].refer)), gg_assign(length, refer_size_source(args[i].refer)); } @@ -12331,7 +13455,7 @@ create_and_call(size_t narg, INT128, "__gg__fetch_call_by_value_value", gg_get_address_of(args[i].refer.field->var_decl_node), - refer_offset_source(args[i].refer), + refer_offset(args[i].refer), refer_size_source(args[i].refer), NULL_TREE))); } @@ -12344,7 +13468,7 @@ create_and_call(size_t narg, INT128, "__gg__fetch_call_by_value_value", gg_get_address_of(args[i].refer.field->var_decl_node), - refer_offset_source(args[i].refer), + refer_offset(args[i].refer), refer_size_source(args[i].refer), NULL_TREE))); } @@ -12364,28 +13488,67 @@ create_and_call(size_t narg, gg_assign(var_decl_call_parameter_count, build_int_cst_type(INT, narg)); - gg_assign(var_decl_call_parameter_signature, - gg_cast(CHAR_P, function_handle)); + tree call_expr = NULL_TREE; + if( function_pointer ) + { + gg_assign(var_decl_call_parameter_signature, + gg_cast(CHAR_P, function_pointer)); - tree call_expr = gg_call_expr_list( returned_value_type, - function_handle, + call_expr = gg_call_expr_list(returned_value_type, + function_pointer, + narg, + arguments ); + } + else + { + tree fndecl_type = build_varargs_function_type_array( returned_value_type, + 0, // No parameters yet + NULL); // And, hence, no types + + // Fetch the FUNCTION_DECL for that FUNCTION_TYPE + tree function_decl = gg_build_fn_decl(funcname, fndecl_type); + set_call_convention(function_decl, current_call_convention()); + + // Take the address of the function decl: + tree address_of_function = gg_get_address_of(function_decl); + + // Stash that address as the called program's signature: + tree address_as_char_p = gg_cast(CHAR_P, address_of_function); + tree assigment = gg_assign( var_decl_call_parameter_signature, + address_as_char_p); + // The source of the assigment is the second element of a MODIFY_EXPR + parser_call_target( funcname, assigment ); + + // Create the call_expr from that address + call_expr = build_call_array_loc( gg_token_location(), + returned_value_type, + address_of_function, narg, - arguments ); + arguments); + // Among other possibilities, this might be a forward reference to a + // contained function. The name here is "prog2", and ultimately will need + // to be replaced with a call to "prog2.62". So, this call expr goes into + // a list of call expressions whose function_decl targets will be replaced. + parser_call_target( funcname, call_expr ); + } + tree returned_value; + if( returned.field ) { - returned_value = gg_define_variable(returned_value_type); + // Because the CALL had a RETURNING clause, RETURN-CODE doesn't return a + // value. So, we make sure it is zero + //// gg_assign(var_decl_return_code, build_int_cst_type(SHORT, 0)); + + // We expect the return value to be a 64-bit or 128-bit integer. How + // we treat that returned value depends on the target. - // We are expecting a return value of type CHAR_P, SSIZE_T, SIZE_T, - // UINT128 or INT128 + // Pick up that value: + returned_value = gg_define_variable(returned_value_type); push_program_state(); gg_assign(returned_value, gg_cast(returned_value_type, call_expr)); pop_program_state(); - // Because the CALL had a RETURNING clause, RETURN-CODE doesn't return a - // value. So, we make sure it is zero - gg_assign(var_decl_return_code, build_int_cst_type(SHORT, 0)); - if( returned_value_type == CHAR_P ) { tree returned_location = gg_define_uchar_star(); @@ -12393,9 +13556,9 @@ create_and_call(size_t narg, // we were given a returned::field, so find its location and length: gg_assign(returned_location, gg_add( member(returned.field->var_decl_node, "data"), - refer_offset_dest(returned))); + refer_offset(returned))); gg_assign(returned_length, - refer_size_dest(returned)); + gg_cast(TREE_TYPE(returned_length), refer_size_dest(returned))); // The returned value is a string of nbytes, which by specification // has to be at least as long as the returned_length of the target: @@ -12405,15 +13568,17 @@ create_and_call(size_t narg, { // Somebody was discourteous enough to return a NULL pointer // We'll jam in spaces: + charmap_t *charmap = __gg__get_charmap(returned.field->codeset.encoding); + int dest_space = charmap->mapped_character(ascii_space); gg_memset( returned_location, - char_nodes[(unsigned char)internal_space], + char_nodes[(unsigned char)dest_space], returned_length ); } ELSE { // There is a valid pointer. Do the assignment. move_tree(returned.field, - refer_offset_dest(returned), + refer_offset(returned), returned_value, integer_one_node); } @@ -12433,13 +13598,14 @@ create_and_call(size_t narg, // We got back a 64-bit or 128-bit integer. The called and calling // programs have to agree on size, but other than that, integer numeric // types are converted one to the other. + gg_call(VOID, "__gg__int128_to_qualified_field", gg_get_address_of(returned.field->var_decl_node), - refer_offset_dest(returned), + refer_offset(returned), refer_size_dest(returned), gg_cast(INT128, returned_value), - member(returned.field->var_decl_node, "rdigits"), + gg_cast(INT, member(returned.field->var_decl_node, "rdigits")), build_int_cst_type(INT, truncation_e), null_pointer_node, NULL_TREE ); @@ -12458,7 +13624,7 @@ create_and_call(size_t narg, tree returned_length = gg_define_size_t(); // we were given a returned::field, so find its location and length: gg_assign(returned_location, - qualified_data_source(returned)); + qualified_data_location(returned)); gg_assign(returned_length, refer_size_source(returned)); @@ -12478,34 +13644,17 @@ create_and_call(size_t narg, else { cbl_internal_error( - "%s(): What in the name of Nero's fiddle are we doing here?", + "%s: What in the name of Nero are we doing here?", __func__); } } else { - // Because no explicit returning value is expected, we switch to - // the IBM default behavior, where the returned INT value is assigned - // to our RETURN-CODE: - returned_value = gg_define_variable(SHORT); - - // Before doing the call, we save the COBOL program_state: + // Because no explicit returning value is expected, we just call it. We + // expect COBOL routines to set RETURN-CODE when they think it necessary. push_program_state(); - gg_assign(returned_value, gg_cast(SHORT, call_expr)); - // And after the call, we restore it: + gg_append_statement(call_expr); pop_program_state(); - - // We know that the returned value is a 2-byte little-endian INT: - gg_assign( var_decl_return_code, - returned_value); - TRACE1 - { - TRACE1_HEADER - gg_printf("returned value: %d", - gg_cast(INT, var_decl_return_code), - NULL_TREE); - TRACE1_END - } } for( size_t i=0; i<narg; i++ ) @@ -12552,7 +13701,7 @@ parser_call( cbl_refer_t name, SHOW_PARSE_TEXT(" (") for(size_t i=0; i<narg; i++) { - cbl_field_t *p = args[i].refer.field; + const cbl_field_t *p = args[i].refer.field; SHOW_PARSE_FIELD( " ", p) } SHOW_PARSE_TEXT(" )") @@ -12613,39 +13762,49 @@ parser_call( cbl_refer_t name, // We are getting close to establishing the function_type. To do that, // we want to establish the function's return type. -// gg_push_context(); size_t nbytes; tree returned_value_type = tree_type_from_field_type(returned.field, nbytes); - tree function_handle = function_handle_from_name( name, - returned_value_type); - if( (use_static_call() && is_literal(name.field)) - || (name.field && name.field->type == FldPointer) ) + if( use_static_call() && is_literal(name.field) ) + { + // name is a literal + create_and_call(narg, + args, + NULL_TREE, + name.field->data.original(), + returned_value_type, + returned, + not_except); + } + else if( name.field && name.field->type == FldPointer ) { - // If these conditions are true, then we know we have a good - // function_handle, and we don't need to check + tree function_pointer = function_pointer_from_name( name, + returned_value_type); + // This is call-by-pointer; we know function_pointer is good: create_and_call(narg, args, - function_handle, + function_pointer, + nullptr, returned_value_type, returned, - not_except - ); + not_except); } else { + tree function_pointer = function_pointer_from_name( name, + returned_value_type); // We might not have a good handle, so we have to check: - IF( function_handle, + IF( function_pointer, ne_op, - gg_cast(TREE_TYPE(function_handle), null_pointer_node) ) + gg_cast(TREE_TYPE(function_pointer), null_pointer_node) ) { create_and_call(narg, args, - function_handle, + function_pointer, + nullptr, returned_value_type, returned, - not_except - ); + not_except); } ELSE { @@ -12672,7 +13831,8 @@ parser_call( cbl_refer_t name, NULL_TREE); gg_printf("WARNING: %s:%d \"CALL %s\" not found" - " with no \"CALL ON EXCEPTION\" phrase\n", + " with no \"CALL ON EXCEPTION\" phrase.\n" + "(You might need -rdynamic or --export-dynamic for symbols in the executable.)\n", gg_string_literal(current_filename.back().c_str()), build_int_cst_type(INT, CURRENT_LINE_NUMBER), mangled_name, @@ -12693,8 +13853,6 @@ parser_call( cbl_refer_t name, gg_append_statement( not_except->structs.call_exception->bottom.label ); free( not_except->structs.call_exception ); } -// gg_pop_context(); - } // Set global variable to use alternative ENTRY point. @@ -12704,10 +13862,80 @@ parser_entry_activate( size_t iprog, const cbl_label_t *declarative ) assert(iprog == symbol_elem_of(declarative)->program); } -// Define ENTRY point with alternative LINKAGE +static tree entry_goto; +static tree entry_label; +static tree entry_addr; + void -parser_entry( cbl_field_t */*name*/, size_t /*narg*/, cbl_ffi_arg_t */*args*/ ) +parser_entry( const cbl_field_t *name, size_t nusing, cbl_ffi_arg_t *args ) { + // We are implementing the ENTRY statement, which creates an alternative + // entry point into the current program-id. There is no actual way to do + // that literally. So, we are going to create a separate routine that sets + // things up and then calls the current routine with the information it needs + // to transfer processing to the ENTRY point. + + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_TEXT(" ") + SHOW_PARSE_TEXT(name->data.original()) + SHOW_PARSE_END + } + + // Get the name of the program that contains the ENTRY statement. + char *name_of_parent = xstrdup(current_function->our_name); + + // Get the name of the ENTRY point. + // cppcheck-suppress nullPointerRedundantCheck + char *psz = cobol_name_mangler(name->data.original()); + + // Create a goto/label pair. The label will be set up here; the goto will + // be used when we re-enter the containing function: + + gg_create_goto_pair(&entry_goto, + &entry_label, + &entry_addr); + + // Start creating the ENTRY function. + tree function_decl = gg_define_function( VOID, + psz, + psz, + NULL_TREE); + free(psz); + + // Modify the default settings for this entry point + TREE_ADDRESSABLE(function_decl) = 0; + TREE_USED(function_decl) = 0; + TREE_NOTHROW(function_decl) = 0; + TREE_STATIC(function_decl) = 1; + DECL_EXTERNAL (function_decl) = 0; + TREE_PUBLIC (function_decl) = 1; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1; + + // When the ENTRY function point is called, we process its "using" + // parameters: + establish_using(nusing, args); + + // Put the entry_label into the global variable that will be picked up + // when the containing program-id is re-entered: + gg_assign(var_decl_entry_label, entry_addr); + + // Get the function address of the containing function. + tree gfa = gg_get_function_address(VOID, name_of_parent); + free(name_of_parent); + + // Call the containing function + gg_append_statement(gg_call_expr_list(VOID, + gfa, + 0, + NULL)); + // We are done with the ENTRY function: + gg_finalize_function(); + + // Lay down the address of the label that matches var_decl_entry_label; + // the containing program-id will jump to this point. + gg_append_statement(entry_label); } void @@ -12728,7 +13956,7 @@ parser_bitop( struct cbl_field_t *tgt, // tgt has to be a FldConditional { SHOW_PARSE_HEADER SHOW_PARSE_FIELD( " switch: ", a) - fprintf(stderr, " mask: %lx", bitmask); + fprintf(stderr, " mask: " HOST_SIZE_T_PRINT_HEX_PURE, (fmt_size_t)bitmask); fprintf(stderr, " op: %s", ops[op]); SHOW_PARSE_FIELD( " target ", tgt) SHOW_PARSE_END @@ -12737,7 +13965,7 @@ parser_bitop( struct cbl_field_t *tgt, // tgt has to be a FldConditional if(tgt && tgt->type != FldConditional) { fprintf(stderr, - "%s(): The target %s has to be a FldConditional, not %s\n", + "%s: The target %s has to be a FldConditional, not %s\n", __func__, tgt->name, cbl_field_type_str(tgt->type)); @@ -12774,7 +14002,7 @@ parser_bitop( struct cbl_field_t *tgt, // tgt has to be a FldConditional case bit_or_op: case bit_xor_op: fprintf(stderr, - "%s(): The %s operation is not valid\n", + "%s: The %s operation is not valid\n", __func__, ops[op]); gcc_unreachable(); @@ -12812,7 +14040,7 @@ parser_bitwise_op(struct cbl_field_t *tgt, { SHOW_PARSE_HEADER SHOW_PARSE_FIELD( " switch: ", a) - fprintf(stderr, " mask: %lx", bitmask); + fprintf(stderr, " mask: " HOST_SIZE_T_PRINT_HEX_PURE, (fmt_size_t)bitmask); fprintf(stderr, " op: %s", ops[op]); SHOW_PARSE_FIELD( " target ", tgt) SHOW_PARSE_END @@ -12821,7 +14049,7 @@ parser_bitwise_op(struct cbl_field_t *tgt, if( tgt && !is_valuable(tgt->type) && tgt->type != FldLiteralN) { fprintf(stderr, - "%s(): The target %s has to be is_valuable, not %s\n", + "%s: The target %s has to be is_valuable, not %s\n", __func__, tgt->name, cbl_field_type_str(tgt->type)); @@ -12835,7 +14063,7 @@ parser_bitwise_op(struct cbl_field_t *tgt, case bit_on_op: case bit_off_op: fprintf(stderr, - "%s(): The %s operation is not valid\n", + "%s: The %s operation is not valid\n", __func__, ops[op]); gcc_unreachable(); @@ -12890,11 +14118,11 @@ parser_set_pointers( size_t ntgt, cbl_refer_t *tgts, cbl_refer_t source ) || source.field->type == FldLiteralA)) { // This is something like SET varp TO ENTRY "ref". - tree function_handle = function_handle_from_name(source, + tree function_pointer = function_pointer_from_name(source, COBOL_FUNCTION_RETURN_TYPE); - gg_memcpy(qualified_data_dest(tgts[i]), - gg_get_address_of(function_handle), - build_int_cst_type(SIZE_T, sizeof(void *))); + gg_memcpy(qualified_data_location(tgts[i]), + gg_get_address_of(function_pointer), + sizeof_pointer); } else { @@ -12912,10 +14140,10 @@ parser_set_pointers( size_t ntgt, cbl_refer_t *tgts, cbl_refer_t source ) gg_call( VOID, "__gg__set_pointer", gg_get_address_of(tgts[i].field->var_decl_node), - refer_offset_dest(tgts[i]), + refer_offset(tgts[i]), build_int_cst_type(INT, tgts[i].addr_of ? REFER_T_ADDRESS_OF : 0), source.field ? gg_get_address_of(source.field->var_decl_node) : null_pointer_node, - refer_offset_source(source), + refer_offset(source), build_int_cst_type(INT, source.addr_of ? REFER_T_ADDRESS_OF : 0), NULL_TREE ); @@ -12942,7 +14170,8 @@ typedef struct hier_node hier_node() : our_index(0), common(false), - parent_node(NULL) + parent_node(nullptr), + name(nullptr) {} } hier_node; @@ -12986,14 +14215,14 @@ find_uncles(const hier_node *node, std::vector<const hier_node *> &uncles) } void -parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) +parser_program_hierarchy( const cbl_prog_hier_t& hier ) { Analyze(); - /* The complication in this routine is that it gets called near the end - of every program-id. And it keeps growing. The reason is because the - parser doesn't know when it is working on the last program of a list of - nested programs. So, we just do what we need to do, and we keep track - of what we've already built so that we don't build it more than once. + /* This routine gets called near the end of every program-id. It keeps + growing because the parser doesn't know when it is working on the last + program of a list of nested programs. So, we just do what we need to do, + and we keep track of what we've already built so that we don't build it + more than once. */ SHOW_PARSE { @@ -13004,7 +14233,7 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) } else { - for( size_t i=0; i<hier.nlabel; i++ ) + for( size_t i=0; i<hier.labels.size(); i++ ) { if( i ) { @@ -13016,11 +14245,11 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) } char ach[128]; sprintf(ach, - "%ld %s%s parent:%ld", - hier.labels[i].ordinal, + HOST_SIZE_T_PRINT_DEC " %s%s parent:" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)hier.labels[i].ordinal, hier.labels[i].label.name, hier.labels[i].label.common ? " COMMON" : "", - hier.labels[i].label.parent); + (fmt_size_t)hier.labels[i].label.parent); SHOW_PARSE_TEXT(ach); } } @@ -13059,9 +14288,9 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) node_map[0] = nodes.back(); // Pass 1: Create a node for every program: - for( size_t i=0; i<hier.nlabel; i++ ) + for( size_t i=0; i<hier.labels.size(); i++ ) { - hier_node *existing_node = find_hier_node(node_map, hier.labels[i].ordinal); + const hier_node *existing_node = find_hier_node(node_map, hier.labels[i].ordinal); gcc_assert( existing_node == NULL ); hier_node *new_node = new hier_node; @@ -13073,7 +14302,7 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) } // Pass 2: populate each node with their parent and children: - for( size_t i=0; i<hier.nlabel; i++ ) + for( size_t i=0; i<hier.labels.size(); i++ ) { hier_node *child_node = find_hier_node(node_map, hier.labels[i].ordinal); gcc_assert(child_node); @@ -13105,9 +14334,9 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) // are also accessible by us. Go find them. std::vector<const hier_node *>uncles; find_uncles(nodes[i], uncles); - for( size_t i=0; i<uncles.size(); i++ ) + for( size_t j=0; j<uncles.size(); j++ ) { - const hier_node *uncle = uncles[i]; + const hier_node *uncle = uncles[j]; if( map_of_sets[caller].find(uncle->name) == map_of_sets[caller].end() ) { // We have a COMMON uncle or sibling we haven't seen before. @@ -13145,16 +14374,17 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) if( callers.find(caller) == callers.end() ) { // We haven't seen this caller before - callers.insert(caller); - char ach[2*sizeof(cbl_name_t)]; + char ach[3*sizeof(cbl_name_t)]; tree names_table_type = build_array_type_nelts(CHAR_P, mol->second.size()+1); - sprintf(ach, "..our_accessible_functions_%ld", caller); + sprintf(ach, "..our_accessible_functions_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)caller); tree the_names_table = gg_define_variable(names_table_type, ach, vs_file_static); // Here is where we build a table out of constructors: tree constructed_array_type = build_array_type_nelts(pointer_type, mol->second.size()); - sprintf(ach, "..our_constructed_table_%ld", caller); + sprintf(ach, "..our_constructed_table_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)caller); tree the_constructed_table = gg_define_variable(constructed_array_type, ach, vs_file_static); tree constr_names = make_node(CONSTRUCTOR); @@ -13172,7 +14402,10 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) callee != mol->second.end(); callee++ ) { - sprintf(ach, "%s.%ld", (*callee)->name, (*callee)->parent_node->our_index); + sprintf(ach, + "%s." HOST_SIZE_T_PRINT_DEC, + (*callee)->name, + (fmt_size_t)(*callee)->parent_node->our_index); CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(constr_names), build_int_cst_type(SIZE_T, i), @@ -13198,13 +14431,17 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) // And put a pointer to that table into the file-static variable set aside // for it: - sprintf(ach, "..accessible_program_list_%ld", caller); + sprintf(ach, "..accessible_program_list_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)caller); tree accessible_list_var_decl = gg_trans_unit_var_decl(ach); gg_assign( accessible_list_var_decl, gg_get_address_of(the_names_table) ); - sprintf(ach, "..accessible_program_pointers_%ld", caller); + sprintf(ach, "..accessible_program_pointers_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)caller); tree accessible_programs_decl = gg_trans_unit_var_decl(ach); gg_assign( accessible_programs_decl, gg_get_address_of(the_constructed_table) ); + + callers.insert(caller); } } } @@ -13213,72 +14450,6 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ) } void -parser_set_handled(ec_type_t ec_handled) - { - if( mode_syntax_only() ) return; - SHOW_PARSE - { - SHOW_PARSE_HEADER - char ach[64]; - sprintf(ach, "ec_type_t: 0x%lx", size_t(ec_handled)); - SHOW_PARSE_TEXT(ach); - SHOW_PARSE_END - } - - TRACE1 - { - TRACE1_HEADER - TRACE1_END - } - - if( gg_trans_unit.function_stack.size() ) - { - if( ec_handled ) - { - // We assume that exception_handled is zero, always. We only make it - // non-zero when something needs to be done. __gg__match_exception is - // in charge of setting it back to zero. - gg_assign(var_decl_exception_handled, - build_int_cst_type(INT, (int)ec_handled)); - } - } - else - { - yywarn("parser_set_handled() called between programs"); - } - } - -void -parser_set_file_number(int file_number) - { - if( mode_syntax_only() ) return; - SHOW_PARSE - { - SHOW_PARSE_HEADER - char ach[32]; - sprintf(ach, "file number: %d", file_number); - SHOW_PARSE_TEXT(ach); - SHOW_PARSE_END - } - - TRACE1 - { - TRACE1_HEADER - TRACE1_END - } - - if( gg_trans_unit.function_stack.size() ) - { - gg_assign(var_decl_exception_file_number, - build_int_cst_type(INT, file_number)); - } - else - { - yywarn("parser_set_file_number() called between programs"); - } - } - -void parser_set_numeric(struct cbl_field_t *tgt, ssize_t value) { Analyze(); @@ -13289,7 +14460,7 @@ parser_set_numeric(struct cbl_field_t *tgt, ssize_t value) SHOW_PARSE_TEXT(tgt->name) SHOW_PARSE_TEXT(" to ") char ach[32]; - sprintf(ach, "%ld", value); + sprintf(ach, HOST_SIZE_T_PRINT_DEC, (fmt_size_t)value); SHOW_PARSE_TEXT(ach); SHOW_PARSE_END } @@ -13304,159 +14475,6 @@ parser_set_numeric(struct cbl_field_t *tgt, ssize_t value) NULL_TREE ); } -static void -stash_exceptions( const cbl_enabled_exceptions_array_t *enabled ) - { - // We need to create a static array of bytes - size_t narg = enabled->nbytes(); - unsigned char *p = (unsigned char *)(enabled->ecs); - - static size_t prior_narg = 0; - static size_t max_narg = 128; - static unsigned char *prior_p = (unsigned char *)xmalloc(max_narg); - - bool we_got_new_data = false; - if( prior_narg != narg ) - { - we_got_new_data = true; - } - else - { - // The narg counts are the same. - for(size_t i=0; i<narg; i++) - { - if( p[i] != prior_p[i] ) - { - we_got_new_data = true; - break; - } - } - } - - if( !we_got_new_data ) - { - return; - } - - if( narg > max_narg ) - { - max_narg = narg; - prior_p = (unsigned char *)xrealloc(prior_p, max_narg); - } - - memcpy(prior_p, p, narg); - - static int count = 1; - - tree array_of_chars_type; - tree array_of_chars; - - if( narg ) - { - char ach[32]; - sprintf(ach, "_ec_array_%d", count++); - array_of_chars_type = build_array_type_nelts(UCHAR, narg); - - // We have the array. Now we need to build the constructor for it - tree constr = make_node(CONSTRUCTOR); - TREE_TYPE(constr) = array_of_chars_type; - TREE_STATIC(constr) = 1; - TREE_CONSTANT(constr) = 1; - - for(size_t i=0; i<narg; i++) - { - CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(constr), - build_int_cst_type(SIZE_T, i), - build_int_cst_type(UCHAR, p[i])); - } - array_of_chars = gg_define_variable(array_of_chars_type, ach, vs_static); - DECL_INITIAL(array_of_chars) = constr; - - gg_call(VOID, - "__gg__stash_exceptions", - build_int_cst_type(SIZE_T, enabled->nec), - narg ? gg_get_address_of(array_of_chars) : null_pointer_node, - NULL_TREE); - } - } - -static void -store_location_stuff(const cbl_name_t statement_name) - { - if( exception_location_active && !current_declarative_section_name() ) - { - // We need to establish some stuff for EXCEPTION- function processing - gg_assign(var_decl_exception_source_file, - gg_string_literal(current_filename.back().c_str())); - - gg_assign(var_decl_exception_program_id, - gg_string_literal(current_function->our_unmangled_name)); - - if( strstr(current_function->current_section->label->name, "_implicit") - != current_function->current_section->label->name ) - { - gg_assign(var_decl_exception_section, - gg_string_literal(current_function->current_section->label->name)); - } - else - { - gg_assign(var_decl_exception_section, - gg_cast(build_pointer_type(CHAR_P),null_pointer_node)); - } - - if( strstr(current_function->current_paragraph->label->name, "_implicit") - != current_function->current_paragraph->label->name ) - { - gg_assign(var_decl_exception_paragraph, - gg_string_literal(current_function->current_paragraph->label->name)); - } - else - { - gg_assign(var_decl_exception_paragraph, - gg_cast(build_pointer_type(CHAR_P), null_pointer_node)); - } - - gg_assign(var_decl_exception_source_file, - gg_string_literal(current_filename.back().c_str())); - gg_assign(var_decl_exception_line_number, build_int_cst_type(INT, - CURRENT_LINE_NUMBER)); - gg_assign(var_decl_exception_statement, gg_string_literal(statement_name)); - } - } - -void -parser_exception_prepare( const cbl_name_t statement_name, - const cbl_enabled_exceptions_array_t *enabled ) - { - Analyze(); - SHOW_PARSE - { - SHOW_PARSE_HEADER - SHOW_PARSE_TEXT(enabled->nec? " stashing " : " skipping ") - SHOW_PARSE_TEXT(statement_name) - SHOW_PARSE_END - } - - TRACE1 - { - TRACE1_HEADER - TRACE1_END - } - - if( enabled->nec ) - { - if( gg_trans_unit.function_stack.size() ) - { - stash_exceptions(enabled); - store_location_stuff(statement_name); - } - else - { - yywarn("parser_exception_prepare() called between programs"); - } - } - } - void parser_exception_clear() { @@ -13485,8 +14503,7 @@ parser_exception_raise(ec_type_t ec) } void -parser_match_exception(cbl_field_t *index, - cbl_field_t *blob ) +parser_match_exception(cbl_field_t *index) { Analyze(); SHOW_PARSE @@ -13494,14 +14511,6 @@ parser_match_exception(cbl_field_t *index, SHOW_PARSE_HEADER SHOW_PARSE_FIELD(" index ", index) SHOW_PARSE_INDENT - if( blob ) - { - SHOW_PARSE_FIELD("blob ", blob) - } - else - { - SHOW_PARSE_TEXT("blob is NULL") - } SHOW_PARSE_END } @@ -13510,22 +14519,12 @@ parser_match_exception(cbl_field_t *index, TRACE1_HEADER TRACE1_FIELD("index ", index, "") TRACE1_INDENT - TRACE1_TEXT("blob ") - if( blob ) - { - TRACE1_TEXT(blob->name) - } - else - { - TRACE1_TEXT("is NULL") - } TRACE1_END } gg_call(VOID, "__gg__match_exception", gg_get_address_of(index->var_decl_node), - blob ? blob->var_decl_node : null_pointer_node, NULL_TREE); TRACE1 @@ -13548,9 +14547,36 @@ parser_check_fatal_exception() SHOW_PARSE_TEXT(" Check for fatal EC...") SHOW_PARSE_END } - gg_call(VOID, - "__gg__check_fatal_exception", - NULL_TREE); + TRACE1 + { + TRACE1_HEADER + TRACE1_TEXT(" Check for fatal EC...") + TRACE1_END + } + + // Performance note: + // A simple program that does two billion additions of 32-bit binary numbers + // in its innermost loop had an execution time of 19.5 seconds. By putting in + // the if() statement, that was reduced to 3.8 seconds. + + if( cdf_enabled_exceptions().size() || sv_is_i_o ) + { + gg_call(VOID, + "__gg__check_fatal_exception", + NULL_TREE); + } + } + +void +parser_push_exception() + { + gg_call(VOID, "__gg__exception_push", NULL_TREE); + } + +void +parser_pop_exception() + { + gg_call(VOID, "__gg__exception_pop", NULL_TREE); } void @@ -13570,6 +14596,7 @@ void parser_exception_file( cbl_field_t *tgt, cbl_file_t *file) { Analyze(); + RETURN_IF_PARSE_ONLY; gg_call(VOID, "__gg__func_exception_file", gg_get_address_of(tgt->var_decl_node), @@ -13653,7 +14680,7 @@ hijack_for_development(const char *funcname) // Assume that funcname is lowercase with no hyphens enter_program_common(funcname, funcname); parser_display_literal("You have been hijacked by a program named \"dubner\""); - gg_insert_into_assembler("# HIJACKED DUBNER CODE START"); + gg_insert_into_assemblerf("%s HIJACKED DUBNER CODE START", ASM_COMMENT_START); for(int i=0; i<10; i++) { @@ -13666,12 +14693,12 @@ hijack_for_development(const char *funcname) NULL_TREE); } - gg_insert_into_assembler("# HIJACKED DUBNER CODE END"); + gg_insert_into_assemblerf("%s HIJACKED DUBNER CODE END", ASM_COMMENT_START); gg_return(0); } static void -conditional_abs(tree source, cbl_field_t *field) +conditional_abs(tree source, const cbl_field_t *field) { Analyze(); if( !(field->attr & signable_e) ) @@ -13681,9 +14708,9 @@ conditional_abs(tree source, cbl_field_t *field) } static bool -mh_identical(cbl_refer_t &destref, - cbl_refer_t &sourceref, - TREEPLET &tsource) +mh_identical(const cbl_refer_t &destref, + const cbl_refer_t &sourceref, + const TREEPLET &tsource) { // Check to see if the two variables are identical types, thus allowing // for a simple byte-for-byte copy of the data areas: @@ -13694,6 +14721,7 @@ mh_identical(cbl_refer_t &destref, && destref.field->data.rdigits == sourceref.field->data.rdigits && (destref.field->attr & (signable_e|separate_e|leading_e)) == (sourceref.field->attr & (signable_e|separate_e|leading_e)) + && destref.field->codeset.encoding == sourceref.field->codeset.encoding && !destref.field->occurs.depending_on && !sourceref.field->occurs.depending_on && !destref.refmod.from @@ -13705,7 +14733,7 @@ mh_identical(cbl_refer_t &destref, ) { // The source and destination are identical in type - if( (sourceref.field->attr & intermediate_e) || !symbol_find_odo(sourceref.field) ) + if( !symbol_find_odo(sourceref.field) ) { Analyze(); // Source doesn't have a depending_on clause @@ -13715,7 +14743,7 @@ mh_identical(cbl_refer_t &destref, SHOW_PARSE_TEXT("mh_identical()"); } gg_memcpy(gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)), + refer_offset(destref)), gg_add(member(sourceref.field->var_decl_node, "data"), tsource.offset), build_int_cst_type(SIZE_T, sourceref.field->data.capacity)); @@ -13725,8 +14753,6 @@ mh_identical(cbl_refer_t &destref, return moved; } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" static bool mh_source_is_literalN(cbl_refer_t &destref, cbl_refer_t &sourceref, @@ -13749,19 +14775,23 @@ mh_source_is_literalN(cbl_refer_t &destref, SHOW_PARSE_TEXT("mh_source_is_literalN: __gg__psz_to_alpha_move") } - static char *buffer = NULL; - static size_t buffer_size = 0; - raw_to_internal(&buffer, - &buffer_size, - sourceref.field->data.initial, - strlen(sourceref.field->data.initial)); + // We need the data sent to __gg__psz_to_alpha_move to be in the + // encoding of the destination + + size_t charsout; + const char *converted = __gg__iconverter( + sourceref.field->codeset.encoding, + destref.field->codeset.encoding, + sourceref.field->data.initial, + strlen(sourceref.field->data.initial), + &charsout); gg_call(VOID, "__gg__psz_to_alpha_move", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), refer_size_dest(destref), - gg_string_literal(buffer), - build_int_cst_type(SIZE_T, strlen(sourceref.field->data.initial)), + gg_string_literal(converted), + build_int_cst_type(SIZE_T, charsout), NULL_TREE); moved = true; break; @@ -13796,13 +14826,13 @@ mh_source_is_literalN(cbl_refer_t &destref, { // We are dealing with a negative number gg_memset(gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)), + refer_offset(destref)), build_int_cst_type(UCHAR, 0xFF), build_int_cst_type(SIZE_T, 8)); } ELSE gg_memset(gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)), + refer_offset(destref)), build_int_cst_type(UCHAR, 0x00), build_int_cst_type(SIZE_T, 8)); ENDIF @@ -13811,7 +14841,7 @@ mh_source_is_literalN(cbl_refer_t &destref, { // The too-short source is positive. gg_memset(gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)), + refer_offset(destref)), build_int_cst_type(UCHAR, 0x00), build_int_cst_type(SIZE_T, 8)); } @@ -13820,7 +14850,7 @@ mh_source_is_literalN(cbl_refer_t &destref, tree literalN_value = get_literalN_value(sourceref.field); scale_by_power_of_ten_N(literalN_value, -sourceref.field->data.rdigits); gg_memcpy(gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)), + refer_offset(destref)), gg_get_address_of(literalN_value), build_int_cst_type(SIZE_T, sourceref.field->data.capacity)); moved = true; @@ -13866,9 +14896,9 @@ mh_source_is_literalN(cbl_refer_t &destref, Analyzer.Message("Check to see if result fits"); if( destref.field->data.digits ) { - __int128 power_of_ten = get_power_of_ten(destref.field->data.digits); - IF( gg_abs(source), ge_op, build_int_cst_type(calc_type, - power_of_ten) ) + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(destref.field->data.digits); + IF( gg_abs(source), ge_op, wide_int_to_tree(calc_type, + power_of_ten) ) { gg_assign(size_error, gg_bitwise_or(size_error, integer_one_node)); } @@ -13881,7 +14911,7 @@ mh_source_is_literalN(cbl_refer_t &destref, tree dest_location = gg_indirect( gg_cast(build_pointer_type(dest_type), gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)))); + refer_offset(destref)))); gg_assign(dest_location, gg_cast(dest_type, source)); moved = true; break; @@ -13910,7 +14940,7 @@ mh_source_is_literalN(cbl_refer_t &destref, gg_call(INT, "__gg__int128_to_qualified_field", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), refer_size_dest(destref), gg_cast(INT128, literalN_value), build_int_cst_type(INT, sourceref.field->data.rdigits), @@ -13938,14 +14968,32 @@ mh_source_is_literalN(cbl_refer_t &destref, SHOW_PARSE_INDENT SHOW_PARSE_TEXT(" FldAlphaEdited") } + + // __gg__string_to_alpha_edited expects the source string to be in + // the same encoding as the target: + size_t len = strlen(sourceref.field->data.initial); + char *src = + static_cast<char *>(xmalloc(len+1)); + memcpy( src, + sourceref.field->data.initial, + strlen(sourceref.field->data.initial)); + size_t charsout; + const char *converted = __gg__iconverter( + sourceref.field->codeset.encoding, + destref.field->codeset.encoding, + src, + len, + &charsout); gg_call(VOID, - "__gg__string_to_alpha_edited_ascii", + "__gg__string_to_alpha_edited", gg_add( member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref) ), - gg_string_literal(sourceref.field->data.initial), - build_int_cst_type(INT, strlen(sourceref.field->data.initial)), + refer_offset(destref) ), + build_int_cst_type(INT, destref.field->codeset.encoding), + gg_string_literal(converted), + build_int_cst_type(INT, len), gg_string_literal(destref.field->data.picture), NULL_TREE); + free(src); moved = true; break; } @@ -13953,7 +15001,7 @@ mh_source_is_literalN(cbl_refer_t &destref, case FldFloat: { tree tdest = gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref) ); + refer_offset(destref) ); switch( destref.field->data.capacity ) { // For some reason, using FLOAT128 in the build_pointer_type causes @@ -13966,26 +15014,20 @@ mh_source_is_literalN(cbl_refer_t &destref, // The following generated code is the exact equivalent // of the C code: // *(float *)dest = (float)data.value - _Float32 src = (_Float32)sourceref.field->data.value_of(); - tree tsrc = build_string_literal(sizeof(src), (char *)&src); - gg_assign(gg_indirect(gg_cast(build_pointer_type(INT), tdest)), - gg_indirect(gg_cast(build_pointer_type(INT), tsrc ))); + gg_assign(gg_indirect(gg_cast(build_pointer_type(FLOAT), tdest)), + fold_convert (FLOAT, sourceref.field->data.value_of())); break; } case 8: { - _Float64 src = (_Float64)sourceref.field->data.value_of(); - tree tsrc = build_string_literal(sizeof(src), (char *)&src); - gg_assign(gg_indirect(gg_cast(build_pointer_type(LONG), tdest)), - gg_indirect(gg_cast(build_pointer_type(LONG), tsrc ))); + gg_assign(gg_indirect(gg_cast(build_pointer_type(DOUBLE), tdest)), + fold_convert (DOUBLE, sourceref.field->data.value_of())); break; } case 16: { - _Float128 src = (_Float128)sourceref.field->data.value_of(); - tree tsrc = build_string_literal(sizeof(src), (char *)&src); - gg_assign(gg_indirect(gg_cast(build_pointer_type(INT128), tdest)), - gg_indirect(gg_cast(build_pointer_type(INT128), tsrc ))); + gg_assign(gg_indirect(gg_cast(build_pointer_type(FLOAT128), tdest)), + sourceref.field->data.value_of()); break; } } @@ -13995,8 +15037,8 @@ mh_source_is_literalN(cbl_refer_t &destref, default: cbl_internal_error( - "In parser_move(%s to %s), the move of FldLiteralN to %s " - "hasn't been implemented", + "In %<parser_move(%s to %s)%>, the move of FldLiteralN to %s " + "is unimplemented", sourceref.field->name, destref.field->name, cbl_field_type_str(destref.field->type)); @@ -14005,7 +15047,6 @@ mh_source_is_literalN(cbl_refer_t &destref, } return moved; } -#pragma GCC diagnostic pop static tree float_type_of(int n) @@ -14025,14 +15066,14 @@ tree float_type_of(int n) } static tree -float_type_of(cbl_field_t *field) +float_type_of(const cbl_field_t *field) { gcc_assert(field->type == FldFloat); return float_type_of(field->data.capacity); } static tree -float_type_of(cbl_refer_t *refer) +float_type_of(const cbl_refer_t *refer) { return float_type_of(refer->field); } @@ -14064,7 +15105,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call(VOID, "__gg__float32_from_int128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, build_int_cst_type(INT, rounded), @@ -14075,7 +15116,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call(VOID, "__gg__float64_from_int128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, build_int_cst_type(INT, rounded), @@ -14086,7 +15127,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call(VOID, "__gg__float128_from_int128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, build_int_cst_type(INT, rounded), @@ -14128,9 +15169,9 @@ mh_dest_is_float( cbl_refer_t &destref, tree stype = float_type_of(&sourceref); tree tdest = gg_add(member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)); + refer_offset(destref)); tree source = gg_add(member(sourceref.field->var_decl_node, "data"), - refer_offset_source(sourceref)); + refer_offset(sourceref)); gg_assign(gg_indirect(gg_cast(build_pointer_type(dtype), tdest)), gg_cast(dtype, gg_indirect(gg_cast(build_pointer_type(stype), @@ -14147,7 +15188,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call_expr( INT, "__gg__float64_from_128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, NULL_TREE)); @@ -14157,7 +15198,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call( INT, "__gg__float64_from_128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, NULL_TREE); @@ -14174,7 +15215,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call_expr( INT, "__gg__float32_from_64", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, NULL_TREE)); @@ -14184,7 +15225,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call( INT, "__gg__float32_from_64", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, NULL_TREE); @@ -14199,7 +15240,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call_expr( INT, "__gg__float32_from_128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, NULL_TREE)); @@ -14209,7 +15250,7 @@ mh_dest_is_float( cbl_refer_t &destref, gg_call( INT, "__gg__float32_from_128", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), tsource.pfield, tsource.offset, NULL_TREE); @@ -14232,8 +15273,8 @@ mh_dest_is_float( cbl_refer_t &destref, } default: - cbl_internal_error("In mh_dest_is_float(%s to %s), the " - "move of %s to %s hasn't been implemented", + cbl_internal_error("In %<mh_dest_is_float%>(%s to %s), the " + "move of %s to %s is unimplemented", sourceref.field->name, destref.field->name, cbl_field_type_str(sourceref.field->type), @@ -14264,8 +15305,11 @@ picky_memset(tree &dest_p, unsigned char value, size_t length) } static void -picky_memcpy(tree &dest_p, tree &source_p, size_t length) +picky_memcpy(tree &dest_p, const tree &source_p, size_t length, tree zero) { + // This is the routine that copies digits for NumericDisplay. In addition + // to just moving digits from source to destination, it has to handle + // clearing up embedded sign information. if( length ) { tree dest_ep = gg_define_variable(TREE_TYPE(dest_p)); @@ -14274,7 +15318,10 @@ picky_memcpy(tree &dest_p, tree &source_p, size_t length) build_int_cst_type(SIZE_T, length))); WHILE( dest_p, lt_op, dest_ep ) { - gg_assign(gg_indirect(dest_p), gg_indirect(source_p)); + gg_assign(gg_indirect(dest_p), + gg_bitwise_or(zero, + gg_bitwise_and(gg_indirect(source_p), + build_int_cst_type(UCHAR, 0x0F)))); gg_increment(dest_p); gg_increment(source_p); } @@ -14283,10 +15330,10 @@ picky_memcpy(tree &dest_p, tree &source_p, size_t length) } static bool -mh_numeric_display( cbl_refer_t &destref, - cbl_refer_t &sourceref, - TREEPLET &tsource, - tree size_error) +mh_numeric_display( const cbl_refer_t &destref, + const cbl_refer_t &sourceref, + const TREEPLET &tsource, + tree size_error) { bool moved = false; @@ -14296,189 +15343,216 @@ mh_numeric_display( cbl_refer_t &destref, && !(sourceref.field->attr & scaled_e) ) { Analyze(); - // I believe that there are 225 pathways through the following code. That's - // because there are five different valid combination of signable_e, + // I believe that there are 450 pathways through the following code. + // That's because there are five different valid combination of signable_e, // separate_e, and leading_e. There are three possibilities for - // sender/receiver rdigits (too many, too few, and just right), and the same - // for ldigits. 5 * 5 * 3 * 3 = 225. + // sender/receiver rdigits (too many, too few, and just right), and the + // same for ldigits. 5 * 5 * 3 * 3 * 2 = 450. // Fasten your seat belts. - // In order to simplify processing of a signable internal sender, we are - // going to pick up the sign byte and temporarily turn off the sign bit in - // the source data. At the end, we will restore that value. This - // reflexively makes me a bit nervous (it isn't, for example, thread-safe), - // but it makes life easier. + // This routine is complicated by the fact that although I had several + // false starts of putting this into libgcobol, I keep coming back to the + // fact that assignment of zoned values is common. And, so, there are all + // kinds of things that are known at compile time that would turn into + // execution-time decisions if I moved them to the library. So, complex + // or not, I am doing all this code here at compile time because it will + // minimize the code at execution time. + + // One thing to keep in mind is the problem caused by a source value being + // internally signed. That turns an ASCII "123" into "12t", and we + // very probably don't want that "t" to find its way into the destination + // value. The internal sign characteristic of ASCII is that the high + // nybble of the sign location is 0x30 or 0x70. For EBCDIC, the high + // nybble is 0xC0 for positive values, and 0xD0 for negative; all other + // digits are 0x70. + + charmap_t *charmap_source = + __gg__get_charmap(sourceref.field->codeset.encoding); + charmap_t *charmap_dest = + __gg__get_charmap( destref.field->codeset.encoding); + + static tree source_sign_loc = gg_define_variable(UCHAR_P, + "..mhnd_sign_loc", + vs_file_static); + static tree dest_sign_loc = gg_define_variable(UCHAR_P, + "..mhnd_dest_sign_loc", + vs_file_static); + static tree source_sign = gg_define_variable(INT, + "..mhnd_sign", + vs_file_static); + // The destination data pointer + static tree dest_p = gg_define_variable( UCHAR_P, + "..mhnd_dest", + vs_file_static); + // The source data pointer + static tree source_p = gg_define_variable( UCHAR_P, + "..mhnd_source", + vs_file_static); + // When we need an end pointer + static tree source_ep = gg_define_variable( UCHAR_P, + "..mhnd_source_e", + vs_file_static); + + bool source_is_signable = sourceref.field->attr & signable_e; + bool source_is_leading = sourceref.field->attr & leading_e; + bool source_is_separate = sourceref.field->attr & separate_e; + + bool dest_is_signable = destref.field->attr & signable_e; + bool dest_is_leading = destref.field->attr & leading_e; + bool dest_is_separate = destref.field->attr & separate_e; + + int switch_source = (source_is_signable ? 4 : 0 ) + + (source_is_leading ? 2 : 0 ) + + (source_is_separate ? 1 : 0 ) ; - static tree source_sign_loc = gg_define_variable(UCHAR_P, "..mhnd_sign_loc", vs_file_static); - static tree source_sign_byte = gg_define_variable(UCHAR, "..mhnd_sign_byte", vs_file_static); - static tree dest_p = gg_define_variable(UCHAR_P, "..mhnd_dest", vs_file_static); // The destination data pointer - static tree source_p = gg_define_variable(UCHAR_P, "..mhnd_source", vs_file_static); // The source data pointer - static tree source_ep = gg_define_variable(UCHAR_P, "..mhnd_source_e", vs_file_static); // When we need an end pointer + int switch_dest = (dest_is_signable ? 4 : 0 ) + + (dest_is_leading ? 2 : 0 ) + + (dest_is_separate ? 1 : 0 ) ; - gg_assign(dest_p, qualified_data_dest(destref)); + // Calculate the start of the source data: gg_assign(source_p, gg_add(member(sourceref.field, "data"), tsource.offset)); - if( sourceref.field->attr & signable_e ) + // Calculate the start of the destination data + gg_assign(dest_p, qualified_data_location(destref)); + + // Figure out exactly where the sign is, if any, and where the input + // digits are. + + switch( switch_source ) { - // The source is signable + case 0: + case 1: + case 2: + case 3: + // not signable + gg_assign(source_sign, integer_zero_node); + break; + case 4: + // signable, not leading, not separate + // Calculate location of the sign byte; it's the last byte of the data + gg_assign(source_sign_loc, + gg_add(source_p, + build_int_cst_type(SIZE_T, + sourceref.field->data.capacity-1))); + break; + case 5: + // signable, not leading, separate + // Calculate location of the sign byte; it's the last byte of the data + gg_assign(source_sign_loc, + gg_add(source_p, + build_int_cst_type(SIZE_T, + sourceref.field->data.capacity-1))); + break; + case 6: + // signable, leading, not separate + // Calculate location of the sign byte; it's the first byte of the data + gg_assign(source_sign_loc, source_p); + break; + case 7: + // signable, leading, separate + // Calculate location of the sign byte; it's the first byte of the data + gg_assign(source_sign_loc, source_p); + gg_increment(source_p); + break; + } + // At this point, the source sign is at source_sign_loc, and the digits + // start at source_p - if( !(sourceref.field->attr & leading_e) ) + // Let's learn what the source sign is + if( source_is_signable && source_is_separate ) + { + IF( gg_indirect(source_sign_loc), + eq_op, + build_int_cst_type(UCHAR, + charmap_source->mapped_character(ascii_minus)) ) { - // The sign location is trailing. Whether separate or not, the location - // is the final byte of the data: - gg_assign(source_sign_loc, gg_add(member( sourceref.field->var_decl_node, "data"), - tsource.offset)), - gg_assign(source_sign_loc, - gg_add(source_sign_loc, - build_int_cst_type(SIZE_T, - sourceref.field->data.capacity-1))); - if( (sourceref.field->attr & separate_e) ) - { - // We have trailing separate - } - else - { - // We have trailing internal - } + // Flag the source as negative + gg_assign(source_sign, integer_one_node); } - else + ELSE { - // The source sign location is in the leading position. - gg_assign(source_sign_loc, - gg_add(member(sourceref.field->var_decl_node, "data"), - tsource.offset)); - if( (sourceref.field->attr & separate_e) ) - { - // We have leading separate, so the first actual digit is at - // source_p+1. - gg_increment(source_p); - } - else - { - // We have leading internal - } + // Flag the source as positive + gg_assign(source_sign, integer_zero_node); } - // Pick up the byte that contains the sign data, whether internal or - // external: - gg_assign(source_sign_byte, gg_indirect(source_sign_loc)); - - if( !(sourceref.field->attr & separate_e) ) + ENDIF + } + if( source_is_signable && !source_is_separate ) + { + // We need to look for an indication that we are internally signed. We + // can tell that by checking to see if the digit is between '0' and '9' + IF( gg_indirect(source_sign_loc), + lt_op, + build_int_cst_type(UCHAR, + charmap_source->mapped_character(ascii_0)) ) + { + // The sign byte is less than '0', so we are negative + gg_assign(source_sign, integer_one_node); + } + ELSE { - // This is signable and internal, so we want to turn off the sign bit - // in the original source data - if( internal_codeset_is_ebcdic() ) + IF( gg_indirect(source_sign_loc), + gt_op, + build_int_cst_type(UCHAR, + charmap_source->mapped_character(ascii_9)) ) { - gg_assign(gg_indirect(source_sign_loc), - gg_bitwise_or(source_sign_byte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT))); + // The sign byte is greater than '9', so we are negative + gg_assign(source_sign, integer_one_node); } - else + ELSE { - gg_assign(gg_indirect(source_sign_loc), - gg_bitwise_and( source_sign_byte, - build_int_cst_type( UCHAR, - ~NUMERIC_DISPLAY_SIGN_BIT))); + // The sign byte is betwixt '0' and '9', so we are positive + gg_assign(source_sign, integer_zero_node); } + ENDIF } - } - else - { - // The number is unsigned, so do nothing. + ENDIF } - // Let the shenanigans begin. + // We now know the source's sign, and where its digits are. - // We are now ready to output the very first byte. + // The first order of business is to move the digits into place. To do + // that, we need to know where things go in the destination: - // The first thing to do is see if we need to output a leading sign - // character - if( (destref.field->attr & signable_e) - && (destref.field->attr & leading_e) - && (destref.field->attr & separate_e) ) + switch( switch_dest ) { - // The output is signed, separate, and leading, so the first character - // needs to be either '+' or '-' - if( (sourceref.field->attr & separate_e) ) - { - // The source is signable/separate - // Oooh. Shiny. We already have that character. - gg_assign(gg_indirect(dest_p), source_sign_byte); - } - else - { - // The source is internal. Not that up above we set source_sign_byte - // even for source values that aren't signable - if( internal_codeset_is_ebcdic() ) - { - // We are working in EBCDIC - if( sourceref.field->attr & signable_e ) - { - IF( gg_bitwise_and( source_sign_byte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), - eq_op, - build_int_cst_type( UCHAR, 0) ) - { - // The source was negative - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, EBCDIC_MINUS)); - - } - ELSE - { - // The source was positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, EBCDIC_PLUS)); - } - ENDIF - } - else - { - // The source is not signable, so the result is positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, EBCDIC_PLUS)); - } - } - else - { - // We are working in ASCII - if( sourceref.field->attr & signable_e ) - { - IF( gg_bitwise_and( source_sign_byte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), - ne_op, - build_int_cst_type( UCHAR, 0) ) - { - // The source was negative - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, '-')); - - } - ELSE - { - // The source was positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, '+')); - } - ENDIF - } - else - { - // The source is not signable, so the result is positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, '+')); - } - } - } - gg_increment(dest_p); + case 0: + case 1: + case 2: + case 3: + // not signable + break; + case 4: + // signable, not leading, not separate + // Calculate location of the sign byte; it's the last byte of the data + gg_assign(dest_sign_loc, + gg_add(dest_p, + build_int_cst_type(SIZE_T, + destref.field->data.capacity-1))); + break; + case 5: + // signable, not leading, separate + // Calculate location of the sign byte; it's the last byte of the data + gg_assign(dest_sign_loc, + gg_add(dest_p, + build_int_cst_type(SIZE_T, + destref.field->data.capacity-1))); + break; + case 6: + // signable, leading, not separate + // Calculate location of the sign byte; it's the first byte of the data + gg_assign(dest_sign_loc, dest_p); + break; + case 7: + // signable, leading, separate + // Calculate location of the sign byte; it's the first byte of the data + gg_assign(dest_sign_loc, dest_p); + gg_increment(dest_p); + break; } - // We have the leading '+' or '-', assuming one is needed. We can - // now start outputting the digits to the left of the decimal place + // We can now start copying the digits to the left of the decimal place int dest_ldigits = (int)destref.field->data.digits - destref.field->data.rdigits; @@ -14492,10 +15566,9 @@ mh_numeric_display( cbl_refer_t &destref, // The destination has more ldigits than the source, and needs some // leading zeroes: picky_memset( dest_p, - internal_codeset_is_ebcdic() ? - EBCDIC_ZERO : '0' , + charmap_dest->mapped_character(ascii_0) , dest_ldigits - source_ldigits); - // With the leading zeros set, copy over the ldigits: + // With the leading zeros set, set the number of ldigits to copy: digit_count = source_ldigits; } else if( dest_ldigits == source_ldigits ) @@ -14503,7 +15576,7 @@ mh_numeric_display( cbl_refer_t &destref, // This is the Goldilocks zone. Everything is *just* right. digit_count = dest_ldigits; } - else + else // dest_ldigits < source_ldigits { // The destination is smaller than the source. We have to throw away the // the high-order digits of the source. If any of them are non-zero, then @@ -14519,8 +15592,7 @@ mh_numeric_display( cbl_refer_t &destref, IF( gg_indirect(source_p), ne_op, build_int_cst_type( UCHAR, - internal_codeset_is_ebcdic() ? - EBCDIC_ZERO : '0') ) + charmap_source->mapped_character(ascii_0)) ) { set_exception_code(ec_size_truncation_e); gg_assign(size_error, integer_one_node); @@ -14536,9 +15608,8 @@ mh_numeric_display( cbl_refer_t &destref, // remaining digits digit_count = dest_ldigits; } - - // The ldigits are in place. We now go the very similar exercise for the - // rdigits: + // We now have digit_count, which will cover the ldigits. Augment it by + // the number of rdigits: int dest_rdigits = destref.field->data.rdigits; int source_rdigits = sourceref.field->data.rdigits; @@ -14566,217 +15637,91 @@ mh_numeric_display( cbl_refer_t &destref, // over only the necessary rdigits, discarding the ones to the right. digit_count += dest_rdigits; } - - picky_memcpy(dest_p, source_p, digit_count); + picky_memcpy(dest_p, + source_p, + digit_count, + build_int_cst_type(UCHAR, + charmap_dest->mapped_character(ascii_0))); picky_memset( dest_p, - internal_codeset_is_ebcdic() ? - EBCDIC_ZERO : '0' , + charmap_dest->mapped_character(ascii_0), trailing_zeros); - // With the digits in place, we need to sort out what to do if the target - // is signable: - if( destref.field->attr & signable_e ) + // With the digits in place, the only thing left is to establish the sign + + switch( switch_dest ) { - if( (destref.field->attr & separate_e) - && !(destref.field->attr & leading_e) ) - { - // The target is separate/trailing, so we need to tack a '+' - // or '-' character - if( sourceref.field->attr & separate_e ) - { - // The source was separate, so we already have what we need in t - // source_sign_byte: - gg_assign(gg_indirect(dest_p), source_sign_byte); - gg_increment(dest_p); - } - else + case 0: + case 1: + case 2: + case 3: + // not signable, so there is nothing to do. + break; + case 4: + case 6: + // signable, not leading, not separate + if( charmap_dest->is_like_ebcdic() ) { - // The source is either internal, or unsigned - if( sourceref.field->attr & signable_e ) + IF( source_sign, ne_op, integer_zero_node ) { - // The source is signable/internal, so we need to extract the - // sign bit from source_sign_byte - if( internal_codeset_is_ebcdic() ) - { - IF( gg_bitwise_and( source_sign_byte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), - eq_op, - build_int_cst_type( UCHAR, 0) ) - { - // The source was negative - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, EBCDIC_MINUS)); - - } - ELSE - { - // The source was positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, EBCDIC_PLUS)); - } - ENDIF - } - else - { - IF( gg_bitwise_and( source_sign_byte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), - ne_op, - build_int_cst_type( UCHAR, 0) ) - { - // The source was negative - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, '-')); - - } - ELSE - { - // The source was positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, '+')); - } - ENDIF - } + // It's negative ebcdic, so we have to turn the bit off. + gg_assign(gg_indirect(dest_sign_loc), + gg_bitwise_and(gg_indirect(dest_sign_loc), + build_int_cst_type(UCHAR, + ~NUMERIC_DISPLAY_SIGN_BIT_EBCDIC))); } - else + ELSE { - // The source is unsigned, so dest is positive - gg_assign(gg_indirect(dest_p), - build_int_cst_type( UCHAR, - internal_codeset_is_ebcdic() ? - EBCDIC_PLUS : '+' )); } - } - gg_increment(dest_p); - } - else if( !(destref.field->attr & separate_e) ) - { - // The destination is signed/internal - if( destref.field->attr & leading_e ) - { - // The sign bit goes into the first byte: - gg_assign(dest_p, qualified_data_dest(destref)); + ENDIF } else { - // The sign bit goes into the last byte: - gg_decrement(dest_p); - } - if( sourceref.field->attr & signable_e ) - { - if( sourceref.field->attr & separate_e ) + IF( source_sign, ne_op, integer_zero_node ) { - // The source is separate, so source_sign_byte is '+' or '-' - IF( source_sign_byte, - eq_op, - build_int_cst_type(UCHAR, - internal_codeset_is_ebcdic() ? - EBCDIC_MINUS : '-') ) - { - // The source is negative, so turn the ASCII bit on - if( !internal_codeset_is_ebcdic() ) - { - gg_assign(gg_indirect(dest_p), - gg_bitwise_or(gg_indirect(dest_p), - build_int_cst_type( - UCHAR, - NUMERIC_DISPLAY_SIGN_BIT))); - - } - else - { - // It's ebcdic, so turn the sign bit OFF - gg_assign(gg_indirect(dest_p), - gg_bitwise_and(gg_indirect(dest_p), - build_int_cst_type( - UCHAR, - ~NUMERIC_DISPLAY_SIGN_BIT))); - } - } - ELSE - { - // The source is positive, so turn the EBCDIC bit ON: - if( internal_codeset_is_ebcdic() ) - { - gg_assign(gg_indirect(dest_p), - gg_bitwise_or(gg_indirect(dest_p), - build_int_cst_type( - UCHAR, - NUMERIC_DISPLAY_SIGN_BIT))); - } - } - ENDIF + // It's negative ascii, so we have to turn the bit on. + gg_assign(gg_indirect(dest_sign_loc), + gg_bitwise_or(gg_indirect(dest_sign_loc), + build_int_cst_type(UCHAR, + NUMERIC_DISPLAY_SIGN_BIT_ASCII))); } - else + ELSE { - // The source is signable/internal, so the sign bit is in - // source_sign_byte. Whatever it is, it has to go into dest_p: - if( internal_codeset_is_ebcdic() ) - { - // This is EBCDIC, so if the source_sign_byte bit is LOW, we - // clear that bit in dest_p high. - IF( gg_bitwise_and( source_sign_byte, - build_int_cst_type( - UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), - eq_op, - build_int_cst_type(UCHAR, 0) ) - { - // The source was negative, so make the dest negative - gg_assign(gg_indirect(dest_p), - gg_bitwise_and(gg_indirect(dest_p), - build_int_cst_type( - UCHAR, - ~NUMERIC_DISPLAY_SIGN_BIT))); - } - ELSE - ENDIF - } - else - { - // This is ASCII, so if the source_sign_byte bit is high, we - // set that bit in dest_p high. - IF( gg_bitwise_and( source_sign_byte, - build_int_cst_type( - UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), - ne_op, - build_int_cst_type(UCHAR, 0) ) - { - // The source was negative, so make the dest negative - gg_assign(gg_indirect(dest_p), - gg_bitwise_or(gg_indirect(dest_p), - build_int_cst_type( - UCHAR, - NUMERIC_DISPLAY_SIGN_BIT))); - } - ELSE - ENDIF - } } + ENDIF } - } - } + break; + case 5: + case 7: + // signable, not leading, separate + // signable, leading, separate + // Calculate location of the sign byte; it's the last byte of the data - if( (sourceref.field->attr & signable_e) - && !(sourceref.field->attr & separate_e)) - { - // The source is signable internal, so we need to restore the original - // sign byte in the original source data: - gg_assign(gg_indirect(source_sign_loc), source_sign_byte); + IF( source_sign, eq_op, integer_zero_node ) + { + gg_assign(gg_indirect(dest_sign_loc), + build_int_cst_type(UCHAR, + charmap_dest->mapped_character(ascii_plus))); + } + ELSE + { + gg_assign(gg_indirect(dest_sign_loc), + build_int_cst_type(UCHAR, + charmap_dest->mapped_character(ascii_minus))); + } + ENDIF + break; } moved = true; } return moved; - } + } //NUMERIC_DISPLAY_SIGN static bool -mh_little_endian( cbl_refer_t &destref, - cbl_refer_t &sourceref, - TREEPLET &tsource, - bool check_for_error, - tree size_error) +mh_little_endian( const cbl_refer_t &destref, + const cbl_refer_t &sourceref, + const TREEPLET &tsource, + bool check_for_error, + tree size_error) { bool moved = false; @@ -14818,7 +15763,7 @@ mh_little_endian( cbl_refer_t &destref, // Get binary value from float actually scales the source value to the // dest:: rdigits copy_little_endian_into_place(destref.field, - refer_offset_dest(destref), + refer_offset(destref), source, destref.field->data.rdigits, check_for_error, @@ -14832,7 +15777,7 @@ mh_little_endian( cbl_refer_t &destref, sourceref.field, tsource.offset); copy_little_endian_into_place(destref.field, - refer_offset_dest(destref), + refer_offset(destref), source, sourceref.field->data.rdigits, check_for_error, @@ -14844,9 +15789,9 @@ mh_little_endian( cbl_refer_t &destref, } static bool -mh_source_is_group( cbl_refer_t &destref, - cbl_refer_t &sourceref, - TREEPLET &tsrc) +mh_source_is_group( const cbl_refer_t &destref, + const cbl_refer_t &sourceref, + const TREEPLET &tsrc) { bool retval = false; if( sourceref.field->type == FldGroup && !(destref.field->attr & rjust_e) ) @@ -14855,13 +15800,13 @@ mh_source_is_group( cbl_refer_t &destref, // We are moving a group to a something. The rule here is just move as // many bytes as you can, and, if necessary, fill with spaces tree tdest = gg_add( member(destref.field->var_decl_node, "data"), - refer_offset_dest(destref)); + refer_offset(destref)); tree tsource = gg_add( member(sourceref.field->var_decl_node, "data"), tsrc.offset); tree dbytes = refer_size_dest(destref); tree sbytes = tsrc.length; - IF( sbytes, ge_op, dbytes ) + IF( sbytes, ge_op, gg_cast(TREE_TYPE(sbytes), dbytes) ) { // There are too many source bytes gg_memcpy(tdest, tsource, dbytes); @@ -14869,7 +15814,9 @@ mh_source_is_group( cbl_refer_t &destref, ELSE { // There are too-few source bytes: - gg_memset(tdest, build_int_cst_type(INT, internal_space), dbytes); + charmap_t *charmap = __gg__get_charmap(destref.field->codeset.encoding); + int dest_space = charmap->mapped_character(ascii_space); + gg_memset(tdest, build_int_cst_type(INT, dest_space), dbytes); gg_memcpy(tdest, tsource, sbytes); } ENDIF @@ -14878,6 +15825,141 @@ mh_source_is_group( cbl_refer_t &destref, return retval; } +static bool +mh_source_is_literalA(const cbl_refer_t &destref, + const cbl_refer_t &sourceref, + cbl_round_t rounded, + tree size_error) + { + bool moved = false; + if( sourceref.field->type == FldLiteralA ) + { + // We are moving a literal somewhere. Because a program-id can take + // variables of ANY LENGTH, we don't know the length of the target + // variable. We do, however, know its encoding. So, we are going to + // construct a string with the same number of characters as the source, but + // in the target variable's encoding. + + // We will then call a library routine that will be in charge of trimming + // and space filling. + + cbl_encoding_t encoding_dest = destref.field->codeset.encoding; + charmap_t *charmap_dest = __gg__get_charmap(encoding_dest); + + if( destref.refmod.from + || destref.refmod.len ) + { + // Let the move routine know to treat the destination as alphanumeric + gg_attribute_bit_set(destref.field, refmod_e); + } + + static char *buffer = NULL; + static size_t buffer_size = 0; + size_t source_length = sourceref.field->data.capacity; + + if( buffer_size < source_length ) + { + buffer_size = source_length; + buffer = static_cast<char *>(xrealloc(buffer, source_length)); + } + gcc_assert(buffer); + + cbl_figconst_t figconst = cbl_figconst_of( sourceref.field->data.initial); + if( figconst ) + { + // We are going to fill 'buffer' with a solid run of the figurative + // constant in the destination codeset. + char const_char = 0x7F; // Head off a compiler warning about + // // uninitialized variables + switch(figconst) + { + case normal_value_e : + // This is not possible, it says here in the fine print. + abort(); + break; + case low_value_e : + const_char = charmap_dest->low_value_character(); + break; + case zero_value_e : + const_char = charmap_dest->mapped_character(ascii_zero); + break; + case space_value_e : + const_char = charmap_dest->mapped_character(ascii_space); + break; + case quote_value_e : + const_char = charmap_dest->quote_character(); + break; + case high_value_e : + const_char = charmap_dest->high_value_character(); + break; + case null_value_e: + const_char = 0x00; + break; + } + memset(buffer, const_char, source_length); + } + else + { + // We are going to convert the source string to the destination codeset, + // and then copy it to 'buffer', trimming if necessary, and space-filling + // to the right if necessary: + cbl_encoding_t encoding_src = sourceref.field->codeset.encoding; + + size_t outlength; + const char *source_string = __gg__iconverter( encoding_src, + encoding_dest, + sourceref.field->data.initial, + source_length, + &outlength ); + // Copy over the converted string + memcpy( buffer, + source_string, + outlength ); + } + + // If the source is flagged ALL, or if we are setting the destination to + // a figurative constant, pass along the ALL bit: + int rounded_parameter = rounded + | ((sourceref.all || figconst ) ? REFER_ALL_BIT : 0); + + if( size_error ) + { + gg_assign(size_error, + gg_call_expr( INT, + "__gg__move_literala", + gg_get_address_of(destref.field->var_decl_node), + refer_offset(destref), + refer_size_dest(destref), + build_int_cst_type(INT, rounded_parameter), + build_string_literal(source_length, + buffer), + build_int_cst_type( SIZE_T, source_length), + NULL_TREE)); + } + else + { + gg_call ( INT, + "__gg__move_literala", + gg_get_address_of(destref.field->var_decl_node), + refer_offset(destref), + refer_size_dest(destref), + build_int_cst_type(INT, rounded_parameter), + build_string_literal(source_length, + buffer), + build_int_cst_type( SIZE_T, source_length), + NULL_TREE); + } + if( destref.refmod.from + || destref.refmod.len ) + { + // Return that value to its original form + gg_attribute_bit_clear(destref.field, refmod_e); + } + moved = true; + } + return moved; + } + static void move_helper(tree size_error, // This is an INT cbl_refer_t destref, @@ -14911,7 +15993,7 @@ move_helper(tree size_error, // This is an INT { // We are creating a copy of the original destination in case we clobber it // and have to restore it because of a computational error. - bool first_time = true; + static bool first_time = true; static size_t stash_size = 1024; if( first_time ) { @@ -14923,7 +16005,7 @@ move_helper(tree size_error, // This is an INT stash_size = destref.field->data.capacity; gg_assign(stash, gg_cast(UCHAR_P, gg_realloc(stash, stash_size))); } - st_data = qualified_data_dest(destref); + st_data = qualified_data_location(destref); st_size = refer_size_dest(destref); gg_memcpy(stash, st_data, @@ -14936,7 +16018,7 @@ move_helper(tree size_error, // This is an INT //goto dont_be_clever; this will go through to the default. } - if( !moved ) + // if( !moved ) // commented out to quiet cppcheck { moved = mh_source_is_group(destref, sourceref, tsource); } @@ -14983,111 +16065,10 @@ move_helper(tree size_error, // This is an INT if( !moved && sourceref.field->type == FldLiteralA) { - SHOW_PARSE1 - { - SHOW_PARSE_INDENT - SHOW_PARSE_TEXT("__gg__move_literala") - } - - cbl_figconst_t figconst = cbl_figconst_of( sourceref.field->data.initial); - - if( destref.refmod.from - || destref.refmod.len ) - { - // Let the move routine know to treat the destination as alphanumeric - gg_attribute_bit_set(destref.field, refmod_e); - } - - static char *buffer = NULL; - static size_t buffer_size = 0; - size_t source_length = sourceref.field->data.capacity; - - if( buffer_size < source_length ) - { - buffer_size = source_length; - buffer = (char *)xrealloc(buffer, buffer_size); - } - - if( figconst ) - { - char const_char = 0x7F; // Head off a compiler warning about - // // uninitialized variables - switch(figconst) - { - case normal_value_e : - // This is not possible, it says here in the fine print. - abort(); - break; - case low_value_e : - const_char = ascii_to_internal(__gg__low_value_character); - break; - case zero_value_e : - const_char = internal_zero; - break; - case space_value_e : - const_char = internal_space; - break; - case quote_value_e : - const_char = ascii_to_internal(__gg__quote_character); - break; - case high_value_e : - const_char = ascii_to_internal(__gg__high_value_character); - break; - case null_value_e: - const_char = 0x00; - break; - } - memset(buffer, const_char, source_length); - } - else - { - memset( buffer, ascii_space, source_length); - memcpy( buffer, - sourceref.field->data.initial, - std::min(source_length, (size_t)sourceref.field->data.capacity) ); - for( size_t i=0; i<source_length; i++) - { - buffer[i] = ascii_to_internal(buffer[i]); - } - } - - int rounded_parameter = rounded - | ((sourceref.all || figconst ) ? REFER_ALL_BIT : 0); - - if( size_error ) - { - gg_assign(size_error, - gg_call_expr( INT, - "__gg__move_literala", - gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), - refer_size_dest(destref), - build_int_cst_type(INT, rounded_parameter), - build_string_literal(source_length, - buffer), - build_int_cst_type( SIZE_T, source_length), - NULL_TREE)); - } - else - { - gg_call ( INT, - "__gg__move_literala", - gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), - refer_size_dest(destref), - build_int_cst_type(INT, rounded_parameter), - build_string_literal(source_length, - buffer), - build_int_cst_type( SIZE_T, source_length), - NULL_TREE); - } - if( destref.refmod.from - || destref.refmod.len ) - { - // Return that value to its original form - gg_attribute_bit_clear(destref.field, refmod_e); - } - moved = true; + moved = mh_source_is_literalA(destref, + sourceref, + rounded, + size_error); } if( !moved ) @@ -15116,7 +16097,7 @@ move_helper(tree size_error, // This is an INT gg_call_expr( INT, "__gg__move", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), refer_size_dest(destref), tsource.pfield, tsource.offset, @@ -15130,7 +16111,7 @@ move_helper(tree size_error, // This is an INT gg_call ( INT, "__gg__move", gg_get_address_of(destref.field->var_decl_node), - refer_offset_dest(destref), + refer_offset(destref), refer_size_dest(destref), tsource.pfield, tsource.offset, @@ -15149,7 +16130,7 @@ move_helper(tree size_error, // This is an INT gg_attribute_bit_clear(destref.field, refmod_e); } - moved = true; + // moved = true; // commented out to quiet cppcheck } if( restore_on_error ) @@ -15228,77 +16209,72 @@ parser_print_string(const char *fmt, const char *ach) gg_printf(fmt, gg_string_literal(ach), NULL_TREE); } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" +REAL_VALUE_TYPE +real_powi10 (uint32_t x) +{ + REAL_VALUE_TYPE ten, pow10; + real_from_integer (&ten, TYPE_MODE (FLOAT128), 10, SIGNED); + real_powi (&pow10, TYPE_MODE (FLOAT128), &ten, x); + return pow10; +} + +static char * -binary_initial_from_float128(cbl_field_t *field, int rdigits, _Float128 value) +binary_initial(cbl_field_t *field) { // This routine returns an xmalloced buffer designed to replace the // data.initial member of the incoming field char *retval = NULL; - char ach[128] = ""; - // We need to adjust value so that it has no decimal places - if( rdigits ) + uint32_t capacity; + uint32_t ddigits; + int32_t drdigits; + uint64_t attr; + FIXED_WIDE_INT(128) value128 = dirty_to_binary(field->data.original(), + capacity, + ddigits, + drdigits, + attr); + int scaled_rdigits = get_scaled_rdigits(field); + + int i = field->data.rdigits; + while( i<0 ) { - value *= get_power_of_ten(rdigits); + value128 = value128/10; + i += 1; } - // We need to make sure that the resulting string will fit into - // a number with 'digits' digits - // Keep in mind that pure binary types, like BINARY-CHAR, have no digits - if( field->data.digits ) + // We take the digits of value128, and put them into ach. We line up + // the rdigits, and we truncate the string after desired_digits + while(drdigits < scaled_rdigits) { - value = fmodf128(value, (_Float128)get_power_of_ten(field->data.digits)); + value128 *= 10; + drdigits += 1; } - - // We convert it to a integer string of digits: - strfromf128(ach, sizeof(ach), "%.0f", value); - if( strcmp(ach, "-0") == 0 ) + while(drdigits > scaled_rdigits) { - // Yes, negative zero can be a thing. Let's make it go away. - strcpy(ach, "0"); + value128 = value128 / 10; + drdigits -= 1; } - retval = (char *)xmalloc(field->data.capacity); + retval = static_cast<char *>(xmalloc(field->data.capacity)); + gcc_assert(retval); switch(field->data.capacity) { + tree type; case 1: - *(signed char *)retval = atoi(ach); - break; case 2: - *(signed short *)retval = atoi(ach); - break; case 4: - *(signed int *)retval = atoi(ach); - break; case 8: - *(signed long *)retval = atol(ach); - break; case 16: - { - __int128 val = 0; - bool negative = false; - for(size_t i=0; i<strlen(ach); i++) - { - if( ach[i] == '-' ) - { - negative = true; - continue; - } - val *= 10; - val += ach[i] & 0x0F; - } - if( negative ) - { - val = -val; - } - *(__int128 *)retval = val; - } + type = build_nonstandard_integer_type ( field->data.capacity + * BITS_PER_UNIT, 0); + native_encode_wide_int (type, value128, PTRCAST(unsigned char, retval), + field->data.capacity); break; default: fprintf(stderr, - "Trouble in initial_from_float128 at %s() %s:%d\n", + "Trouble in binary_initial at %s() %s:%d\n", __func__, __FILE__, __LINE__); @@ -15308,32 +16284,97 @@ binary_initial_from_float128(cbl_field_t *field, int rdigits, _Float128 value) return retval; } -#pragma GCC diagnostic pop static void -digits_from_float128(char *retval, cbl_field_t *field, size_t width, int rdigits, _Float128 value) +digits_from_int128( char *ach, + cbl_field_t *field, + uint32_t desired_digits, + FIXED_WIDE_INT(128) value128, // cppcheck-suppress unknownMacro + int32_t rdigits) + { + if( value128 < 0 ) + { + value128 = -value128; + } + + // 'rdigits' are the number of rdigits in value128. + + int scaled_rdigits = get_scaled_rdigits(field); + + int i = field->data.rdigits; + while( i<0 ) + { + value128 = value128/10; + i += 1; + } + + // We take the digits of value128, and put them into ach. We line up + // the rdigits, and we truncate the string after desired_digits + while(rdigits < scaled_rdigits) + { + value128 *= 10; + rdigits += 1; + } + while(rdigits > scaled_rdigits) + { + value128 = value128 / 10; + rdigits -= 1; + } + char conv[128]; + print_dec (value128, conv, SIGNED); + size_t len = strlen(conv); + + if( len<desired_digits ) + { + memset(ach, ascii_0, desired_digits - len); + strcpy(ach+desired_digits - len, conv); + } + else + { + strcpy(ach, conv + len-desired_digits); + } + } + +#if 0 +// This routine was replaced with digits_from_int1289. However, I am choosing +// to keep it around for a while, because it is a master class in manipulating +// REAL_VALUE_TYPE and FIXED_WIDE_INT + +static void +digits_from_float128(char *retval, cbl_field_t *field, size_t width, int rdigits, REAL_VALUE_TYPE value) { char ach[128]; // We need to adjust value so that it has no decimal places if( rdigits ) { - value *= get_power_of_ten(rdigits); + REAL_VALUE_TYPE pow10 = real_powi10 (rdigits); + real_arithmetic (&value, MULT_EXPR, &value, &pow10); } // We need to make sure that the resulting string will fit into // a number with 'digits' digits - - value = fmodf128(value, (_Float128)get_power_of_ten(field->data.digits)); + REAL_VALUE_TYPE pow10 = real_powi10 (field->data.digits); + mpfr_t m0, m1; + + mpfr_inits2 (FLOAT_MODE_FORMAT (TYPE_MODE (float128_type_node))->p, m0, m1, + NULL); + mpfr_from_real (m0, &value, MPFR_RNDN); + mpfr_from_real (m1, &pow10, MPFR_RNDN); + mpfr_clear_flags (); + mpfr_fmod (m0, m0, m1, MPFR_RNDN); + real_from_mpfr (&value, m0, + REAL_MODE_FORMAT (TYPE_MODE (float128_type_node)), + MPFR_RNDN); + real_convert (&value, TYPE_MODE (float128_type_node), &value); + mpfr_clears (m0, m1, NULL); + real_roundeven (&value, TYPE_MODE (float128_type_node), &value); + + bool fail = false; + FIXED_WIDE_INT(128) i + = FIXED_WIDE_INT(128)::from (real_to_integer (&value, &fail, 128), SIGNED); // We convert it to a integer string of digits: - strfromf128(ach, sizeof(ach), "%.0f", value); - if( strcmp(ach, "-0") == 0 ) - { - // Yes, negative zero can be a thing. Let's make it go away. - strcpy(ach, "0"); - } - - //fprintf(stderr, "digits_from_float128() %s %f %s ", field->name, (double)value, ach); + print_dec (i, ach, SIGNED); gcc_assert( strlen(ach) <= field->data.digits ); if( strlen(ach) < width ) @@ -15342,57 +16383,25 @@ digits_from_float128(char *retval, cbl_field_t *field, size_t width, int rdigits } strcpy(retval + (width-strlen(ach)), ach); } +#endif -char * -initial_from_float128(cbl_field_t *field, _Float128 value) +static char * +initial_from_initial(cbl_field_t *field) { Analyze(); // This routine returns an xmalloced buffer that is intended to replace the // data.initial member of the incoming field. - //fprintf(stderr, "initial_from_float128 %s\n", field->name); + //fprintf(stderr, " %s\n", field->name); char *retval = NULL; - int rdigits; // Let's handle the possibility of a figurative constant - cbl_figconst_t figconst = cbl_figconst_of( field->data.initial); - //cbl_figconst_t figconst = (cbl_figconst_t)(field->attr & FIGCONST_MASK); + cbl_figconst_t figconst = cbl_figconst_of(field->data.initial); if( figconst ) { - int const_char = 0xFF; // Head off a compiler warning about uninitialized - // // variables - switch(figconst) - { - case normal_value_e : - // This really should never happen because normal_value_e is zero - abort(); - break; - case low_value_e : - const_char = ascii_to_internal(__gg__low_value_character); - break; - case zero_value_e : - const_char = internal_zero; - break; - case space_value_e : - const_char = internal_space; - break; - case quote_value_e : - const_char = ascii_to_internal(__gg__quote_character); - break; - case high_value_e : - if( __gg__high_value_character == DEGENERATE_HIGH_VALUE ) - { - const_char = __gg__high_value_character; - } - else - { - const_char = ascii_to_internal(__gg__high_value_character); - } - break; - case null_value_e: - break; - } + charmap_t *charmap = __gg__get_charmap(field->codeset.encoding); + int const_char = charmap->figconst_character(figconst); bool set_return = figconst != zero_value_e; if( !set_return ) { @@ -15410,12 +16419,22 @@ initial_from_float128(cbl_field_t *field, _Float128 value) } if( set_return ) { - retval = (char *)xmalloc(field->data.capacity); + retval = static_cast<char *>(xmalloc(field->data.capacity+1)); + gcc_assert(retval); memset(retval, const_char, field->data.capacity); - goto done; + retval[field->data.capacity] = '\0'; + return retval; } } + // ??? Refactoring the cases below that do not need 'value' would + // make this less ugly + REAL_VALUE_TYPE value; + if( field->data.etc_type == cbl_field_data_t::value_e ) + value = TREE_REAL_CST (field->data.value_of ()); + +#if 0 + int rdigits; // There is always the infuriating possibility of a P-scaled number if( field->attr & scaled_e ) { @@ -15428,7 +16447,9 @@ initial_from_float128(cbl_field_t *field, _Float128 value) // Our result has no decimal places, and we have to multiply the value // by 10**9 to get the significant bdigits where they belong. - value *= get_power_of_ten(field->data.digits + field->data.rdigits); + REAL_VALUE_TYPE pow10 + = real_powi10 (field->data.digits + field->data.rdigits); + real_arithmetic (&value, MULT_EXPR, &value, &pow10); } else { @@ -15438,7 +16459,8 @@ initial_from_float128(cbl_field_t *field, _Float128 value) // If our caller gave us 123000000, we need to divide // it by 1000000 to line up the 123 with where we want it to go: - value /= get_power_of_ten(-field->data.rdigits); + REAL_VALUE_TYPE pow10 = real_powi10 (-field->data.rdigits); + real_arithmetic (&value, RDIV_EXPR, &value, &pow10); } // Either way, we now have everything aligned for the remainder of the // processing to work: @@ -15448,17 +16470,18 @@ initial_from_float128(cbl_field_t *field, _Float128 value) // Not P-scaled rdigits = field->data.rdigits; } +#endif switch(field->type) { case FldNumericBin5: case FldIndex: - retval = binary_initial_from_float128(field, rdigits, value); + retval = binary_initial(field); break; case FldNumericBinary: { - retval = binary_initial_from_float128(field, rdigits, value); + retval = binary_initial(field); size_t left = 0; size_t right = field->data.capacity - 1; while(left < right) @@ -15470,99 +16493,99 @@ initial_from_float128(cbl_field_t *field, _Float128 value) case FldNumericDisplay: { - retval = (char *)xmalloc(field->data.capacity); + charmap_t *charmap = __gg__get_charmap(field->codeset.encoding); + + retval = static_cast<char *>(xmalloc(field->data.capacity)); + gcc_assert(retval); char *pretval = retval; char ach[128]; bool negative; - if( value < 0 ) + if( real_isneg (&value) ) { negative = true; - value = -value; + value = real_value_negate (&value); } else { negative = false; } - digits_from_float128(ach, field, field->data.digits, rdigits, value); - - char *digits = ach; + // Convert the data.initial to a __int128 + uint32_t capacity; + uint32_t ddigits; + int32_t drdigits; + uint64_t attr; + FIXED_WIDE_INT(128) value128 = dirty_to_binary(field->data.initial, + capacity, + ddigits, + drdigits, + attr); + digits_from_int128(ach, field, field->data.digits, value128, drdigits); + + const char *digits = ach; if( (field->attr & signable_e) && (field->attr & separate_e) && (field->attr & leading_e ) ) { + // This zoned decimal value is signable, separate, and leading. if( negative ) { - *pretval++ = internal_minus; + *pretval++ = charmap->mapped_character(ascii_minus); } else { - *pretval++ = internal_plus; + *pretval++ = charmap->mapped_character(ascii_plus); } } for(size_t i=0; i<field->data.digits; i++) { - *pretval++ = internal_zero + ((*digits++) & 0x0F); + // Start by assuming it's an value that can't be signed + *pretval++ = charmap->mapped_character(ascii_0) + ((*digits++) & 0x0F); } if( (field->attr & signable_e) && (field->attr & separate_e) && !(field->attr & leading_e ) ) { + // The value is signable, separate, and trailing if( negative ) { - *pretval++ = internal_minus; + *pretval++ = charmap->mapped_character(ascii_minus); } else { - *pretval++ = internal_plus; + *pretval++ = charmap->mapped_character(ascii_plus); } } if( (field->attr & signable_e) - && !(field->attr & separate_e) - && negative) + && !(field->attr & separate_e) ) { - if( field->attr & leading_e ) - { - if( internal_is_ebcdic ) - { - retval[0] &= ~NUMERIC_DISPLAY_SIGN_BIT; - } - else - { - retval[0] |= NUMERIC_DISPLAY_SIGN_BIT; - } - } - else - { - if( internal_is_ebcdic ) - { - pretval[-1] &= ~NUMERIC_DISPLAY_SIGN_BIT; - } - else - { - pretval[-1] |= NUMERIC_DISPLAY_SIGN_BIT; - } - } + // This value is signable, and not separate. So, the sign information + // goes into the first or last byte: + char *sign_location = field->attr & leading_e ? + retval : retval + field->data.digits - 1 ; + *sign_location = charmap->set_digit_negative(*sign_location, + negative); } break; } case FldPacked: { - retval = (char *)xmalloc(field->data.capacity); + retval = static_cast<char *>(xmalloc(field->data.capacity)); + gcc_assert(retval); char *pretval = retval; char ach[128]; bool negative; - if( value < 0 ) + if( real_isneg (&value) ) { - negative = true; - value = -value; + negative = true; + value = real_value_negate (&value); } else { - negative = false; + negative = false; } // For COMP-6 (flagged by separate_e), the number of required digits is @@ -15574,9 +16597,18 @@ initial_from_float128(cbl_field_t *field, _Float128 value) size_t ndigits = (field->attr & separate_e) ? field->data.capacity * 2 : field->data.capacity * 2 - 1; - digits_from_float128(ach, field, ndigits, rdigits, value); - - char *digits = ach; + uint32_t capacity; + uint32_t ddigits; + int32_t drdigits; + uint64_t attr; + FIXED_WIDE_INT(128) value128 = dirty_to_binary(field->data.initial, + capacity, + ddigits, + drdigits, + attr); + digits_from_int128(ach, field, ndigits, value128, drdigits); + + const char *digits = ach; for(size_t i=0; i<ndigits; i++) { if( !(i & 0x01) ) @@ -15617,17 +16649,16 @@ initial_from_float128(cbl_field_t *field, _Float128 value) { if( field->data.initial ) { - retval = (char *)xmalloc(field->data.capacity+1); + retval = static_cast<char *>(xmalloc(field->data.capacity+1)); + gcc_assert(retval); if( field->attr & hex_encoded_e) { memcpy(retval, field->data.initial, field->data.capacity); } else { - size_t buffer_size = 0; - size_t length = (size_t)field->data.capacity; - memset(retval, internal_space, length); - raw_to_internal(&retval, &buffer_size, field->data.initial, length); + size_t length = field->data.capacity; + memcpy(retval, field->data.initial, length); if( strlen(field->data.initial) < length ) { // If this is true, then the initial string must've been Z'xyz' @@ -15641,35 +16672,34 @@ initial_from_float128(cbl_field_t *field, _Float128 value) case FldNumericEdited: { - retval = (char *)xmalloc(field->data.capacity+1); + charmap_t *charmap = __gg__get_charmap(field->codeset.encoding); + retval = static_cast<char *>(xmalloc(field->data.capacity+1)); + gcc_assert(retval); if( field->data.initial && field->attr & quoted_e ) { - if( field->attr & quoted_e ) + // What the programmer says the value is, the value becomes, no + // matter how wrong it might be. + size_t length = std::min( (size_t)field->data.capacity, + strlen(field->data.initial)); + for(size_t i=0; i<length; i++) { - // What the programmer says the value is, the value becomes, no - // matter how wrong it might be. - size_t length = std::min( (size_t)field->data.capacity, - strlen(field->data.initial)); - for(size_t i=0; i<length; i++) - { - retval[i] = ascii_to_internal(field->data.initial[i]); - } - if( length < (size_t)field->data.capacity ) - { - memset( retval+length, - internal_space, - (size_t)field->data.capacity - length); - } + retval[i] = field->data.initial[i]; + } + if( length < (size_t)field->data.capacity ) + { + memset( retval+length, + charmap->mapped_character(ascii_space), + (size_t)field->data.capacity - length); } } else { // It's not a quoted string, so we use data.value: bool negative; - if( value < 0 ) + if( real_isneg (&value) ) { negative = true; - value = -value; + value = real_value_negate (&value); } else { @@ -15679,20 +16709,41 @@ initial_from_float128(cbl_field_t *field, _Float128 value) char ach[128]; memset(ach, 0, sizeof(ach)); memset(retval, 0, field->data.capacity); - size_t ndigits = field->data.capacity; - if( (field->attr & blank_zero_e) && value == 0 ) + if( (field->attr & blank_zero_e) && real_iszero (&value) ) { - memset(retval, internal_space, field->data.capacity); + memset( retval, + charmap->mapped_character(ascii_space), + field->data.capacity); } else { - digits_from_float128(ach, field, ndigits, rdigits, value); + size_t ndigits = field->data.capacity; + uint32_t capacity; + uint32_t ddigits; + int32_t drdigits; + uint64_t attr; + FIXED_WIDE_INT(128) value128 = dirty_to_binary(field->data.original(), + capacity, + ddigits, + drdigits, + attr); + digits_from_int128(ach, field, ndigits, value128, drdigits); + + // __gg__string_to_numeric_edited operates in ASCII space: __gg__string_to_numeric_edited( retval, ach, field->data.rdigits, negative, field->data.picture); + // So now we convert it to the target encoding: + size_t nbytes; + const char *converted = __gg__iconverter(DEFAULT_SOURCE_ENCODING, + field->codeset.encoding, + retval, + strlen(retval), + &nbytes); + strcpy(retval, converted); } } break; @@ -15700,17 +16751,24 @@ initial_from_float128(cbl_field_t *field, _Float128 value) case FldFloat: { - retval = (char *)xmalloc(field->data.capacity); + retval = static_cast<char *>(xmalloc(field->data.capacity)); + gcc_assert(retval); switch( field->data.capacity ) { case 4: - *(_Float32 *)retval = (_Float32) value; + value = real_value_truncate (TYPE_MODE (FLOAT), value); + native_encode_real (SCALAR_FLOAT_TYPE_MODE (FLOAT), &value, + PTRCAST(unsigned char, retval), 4, 0); break; case 8: - *(_Float64 *)retval = (_Float64) value; + value = real_value_truncate (TYPE_MODE (DOUBLE), value); + native_encode_real (SCALAR_FLOAT_TYPE_MODE (DOUBLE), &value, + PTRCAST(unsigned char, retval), 8, 0); break; case 16: - *(_Float128 *)retval = (_Float128) value; + value = real_value_truncate (TYPE_MODE (FLOAT128), value); + native_encode_real (SCALAR_FLOAT_TYPE_MODE (FLOAT128), &value, + PTRCAST(unsigned char, retval), 16, 0); break; } break; @@ -15718,13 +16776,38 @@ initial_from_float128(cbl_field_t *field, _Float128 value) case FldLiteralN: { + // This requires annotation. + + // The compiler originally used ASCII for field->data.initial. Later we + // expanded the field with the addition of the codeset.encoding + // For consistency in the parser processing, the FldLiteralN is arriving + // with the Object-Computer's character encoding, and field->data.initial + // is showing up encoded. + + // But on the run-time side, if the initial string is needed, it is + // invariably more useful in ASCII. Consider converting that string to + // a floating-point value, for example. + + // So, we are going to convert the data.initial string back to ASCII + // here. Later on, when we establish the run-time encoding, we will + // check for FldLiteralN and set that to ASCII as well. See + // actually_create_the_static_field(). + + size_t nbytes; + const char *converted = __gg__iconverter(field->codeset.encoding, + DEFAULT_SOURCE_ENCODING, + field->data.initial, + strlen(field->data.initial), + &nbytes); + retval = static_cast<char *>(xmalloc(strlen(field->data.initial)+1)); + gcc_assert(retval); + strcpy(retval, converted); break; } default: break; } - done: return retval; } @@ -15874,49 +16957,29 @@ actually_create_the_static_field( cbl_field_t *new_var, build_int_cst_type(SCHAR, new_var->data.rdigits) ); next_field = TREE_CHAIN(next_field); + // INT, "encoding", + // For FldLiteralN we force the encoding to be ASCII. + // See initial_from_initial() for an explanation. + CONSTRUCTOR_APPEND_ELT(CONSTRUCTOR_ELTS(constr), + next_field, + build_int_cst_type(INT, + new_var->type == FldLiteralN ? + DEFAULT_SOURCE_ENCODING + : new_var->codeset.encoding)); + next_field = TREE_CHAIN(next_field); + + // INT, "alphabet", + CONSTRUCTOR_APPEND_ELT(CONSTRUCTOR_ELTS(constr), + next_field, + build_int_cst_type(INT, new_var->codeset.alphabet)); + next_field = TREE_CHAIN(next_field); + DECL_INITIAL(new_var_decl) = constr; } static void psa_global(cbl_field_t *new_var) { - char *mname = cobol_name_mangler(new_var->name); - char ach[2*sizeof(cbl_name_t)]; - sprintf(ach, "__gg__%s", mname); - free(mname); - - if( getenv("SHOW_GLOBAL_VARIABLES") ) - { - char ach_type[32]; - strcpy(ach_type, cbl_field_type_str(new_var->type)); - - fprintf(stderr, "struct cblc_field_t %s = {\n", ach); - fprintf(stderr, " .data = NULL ,\n" ); - fprintf(stderr, " .capacity = %d ,\n", new_var->data.capacity ); - fprintf(stderr, " .offset = %ld ,\n" , new_var->offset ); - fprintf(stderr, " .name = \"%s\" ,\n" , new_var->name ); - fprintf(stderr, " .picture = \"%s\" ,\n" , new_var->data.picture ? new_var->data.picture : "" ); - if( new_var->data.initial || new_var->type == FldPointer ) - { - fprintf(stderr, " .initial = \"%s\" ,\n" , new_var->data.picture ? new_var->data.picture : "" ); - } - else - { - fprintf(stderr, " .initial = NULL ,\n" ); - } - fprintf(stderr, " .parent = NULL,\n" ); - fprintf(stderr, " .depending_on = NULL ,\n" ); - fprintf(stderr, " .depends_on = NULL ,\n" ); - fprintf(stderr, " .occurs_lower = 0 ,\n" ); - fprintf(stderr, " .occurs_upper = 0 ,\n" ); - fprintf(stderr, " .attr = 0x%lx ,\n" , new_var->attr ); - fprintf(stderr, " .type = %s ,\n" , ach_type); - fprintf(stderr, " .level = %d ,\n" , new_var->level ); - fprintf(stderr, " .digits = %d ,\n" , new_var->data.digits ); - fprintf(stderr, " .rdigits = %d ,\n" , new_var->data.rdigits ); - fprintf(stderr, " };\n"); - } - if( strcmp(new_var->name, "_VERY_TRUE") == 0 ) { new_var->var_decl_node = boolean_true_node; @@ -15928,10 +16991,20 @@ psa_global(cbl_field_t *new_var) return; } - // global variables already have a cblc_field_t defined in constants.cc + // global variables already have a cblc_field_t defined in constants.cc. - strcpy(ach, "__gg__"); - strcat(ach, new_var->name); + // Finding their name is done by converting to lowercase, dashes become + // underscores, and "__ggsr__" is prepended. "filler" gets ignored. + + // To feed GDB-COBOL's requirements, we tack on this variable's index and + // this program's index number: + + char ach[2*sizeof(cbl_name_t)]; + + snprintf( ach, + sizeof(ach), + "__ggsr__%s", + new_var->name); for(size_t i=0; i<strlen(ach); i++) { ach[i] = _tolower(ach[i]); @@ -15941,16 +17014,6 @@ psa_global(cbl_field_t *new_var) } } - if( strcmp(new_var->name, "RETURN-CODE") == 0 ) - { - strcpy(ach, "__gg___11_return_code6"); - } - - if( strcmp(new_var->name, "UPSI-0") == 0 ) - { - strcpy(ach, "__gg___6_upsi_04"); - } - new_var->var_decl_node = gg_declare_variable(cblc_field_type_node, ach, NULL, vs_external_reference); // global variables already have a .data area defined. We can find that @@ -16026,12 +17089,14 @@ psa_new_var_decl(cbl_field_t *new_var, const char *external_record_base) && symbol_at(new_var->parent)->type == SymField ) { // We have a parent that is a field - sprintf(id_string, ".%ld_%ld", our_index, new_var->parent); + sprintf(id_string, "." HOST_SIZE_T_PRINT_DEC "_" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)our_index, (fmt_size_t)new_var->parent); } else { // The parent is zero, so it'll be implied: - sprintf(id_string, ".%ld", our_index); + sprintf(id_string, "." HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)our_index); } if(strcasecmp(new_var->name, "filler") == 0) @@ -16119,48 +17184,52 @@ psa_FldLiteralA(struct cbl_field_t *field ) // We are constructing a completely static constant structure. We know the // capacity. We'll create it from the data.initial. The cblc_field_t:data - // will be an ASCII/EBCDIC copy of the .initial data. The .initial will be - // left as ASCII. The var_decl_node will be an ordinary cblc_field_t, which - // means that at this point in time, a FldLiteralA can be used anywhere a - // FldGroup or FldAlphanumeric can be used. We are counting on the parser - // not allowing a FldLiteralA to be a left-hand-side variable. + // will be a copy of the .initial data. The var_decl_node will be an ordinary + // cblc_field_t, which means that at this point in time, a FldLiteralA can be + // used anywhere a FldGroup or FldAlphanumeric can be used. We are counting + // on the parser not allowing a FldLiteralA to be a left-hand-side variable. // First make room static size_t buffer_size = 1024; - static char *buffer = (char *)xmalloc(buffer_size); + static char *buffer = static_cast<char *>(xmalloc(buffer_size)); if( buffer_size < field->data.capacity+1 ) { buffer_size = field->data.capacity+1; - buffer = (char *)xrealloc(buffer, buffer_size); + buffer = static_cast<char *>(xrealloc(buffer, buffer_size)); } + gcc_assert(buffer); cbl_figconst_t figconst = cbl_figconst_of( field->data.initial ); gcc_assert(figconst == normal_value_e); - if( internal_codeset_is_ebcdic() ) - { - for( size_t i=0; i<field->data.capacity; i++ ) - { - buffer[i] = ascii_to_internal(field->data.initial[i]); - } - } - else - { - memcpy(buffer, field->data.initial, field->data.capacity); - } + memcpy(buffer, field->data.initial, field->data.capacity); buffer[field->data.capacity] = '\0'; // We have the original nul-terminated text at data.initial. We have a // copy of it in buffer[] in the internal codeset. + static const char name_base[] = "_literal_a_"; + // We will reuse a single static structure for each string static std::unordered_map<std::string, int> seen_before; + std::string field_string(buffer); + +#if 0 + /* This code is suppoed to re-use literals, and seems to work just fine in + x86_64-linux and on an Apple aarch64 M1 Macbook Pro. But on an M1 + mini, using -Os optimization, attempts were made in the generated + assembly language to define _literal_a_1 more than once. + + I didn't know how to try to track this one down, so I decided simply to + punt by removing the code. + + I am leaving the code here because of a conviction that it someday should + be tracked down. */ + std::unordered_map<std::string, int>::const_iterator it = seen_before.find(field_string); - static const char name_base[] = "_literal_a_"; - if( it != seen_before.end() ) { // We've seen that string before. @@ -16173,9 +17242,11 @@ psa_FldLiteralA(struct cbl_field_t *field ) vs_file_static); } else +#endif { // We have not seen that string before - static int nvar = 1; + static int nvar = 0; + nvar += 1; seen_before[field_string] = nvar; char ach[32]; @@ -16185,19 +17256,16 @@ psa_FldLiteralA(struct cbl_field_t *field ) vs_file_static); actually_create_the_static_field( field, - build_string_literal(field->data.capacity+1, + build_string_literal(field->data.capacity, buffer), - field->data.capacity+1, + field->data.capacity, field->data.initial, NULL_TREE, field->var_decl_node); - nvar += 1; - } - TRACE1 - { - TRACE1_INDENT - TRACE1_TEXT("Finished") - TRACE1_END + TREE_READONLY(field->var_decl_node) = 1; + TREE_USED(field->var_decl_node) = 1; + TREE_STATIC(field->var_decl_node) = 1; + DECL_PRESERVE_P (field->var_decl_node) = 1; } } #endif @@ -16212,6 +17280,8 @@ parser_local_add(struct cbl_field_t *new_var ) SHOW_PARSE_END } + CHECK_FIELD(new_var); + IF( member(new_var->var_decl_node, "data"), ne_op, gg_cast(UCHAR_P, null_pointer_node) ) @@ -16264,33 +17334,34 @@ parser_symbol_add(struct cbl_field_t *new_var ) { do { - fprintf(stderr, "( %d ) %s():", CURRENT_LINE_NUMBER, __func__); + fprintf(stderr, "( %d ) %s:", CURRENT_LINE_NUMBER, __func__); } while(0); - fprintf(stderr, " %2.2d %s<%s> off:%zd " - "msiz:%d cap:%d dig:%d rdig:%d attr:0x%lx loc:%p", + fprintf(stderr, " %2.2u %s<%s> off:" HOST_SIZE_T_PRINT_UNSIGNED " " + "msiz:%u cap:%u dig:%u rdig:%d attr:0x" HOST_SIZE_T_PRINT_HEX_PURE " loc:%p", new_var->level, new_var->name, cbl_field_type_str(new_var->type), - new_var->offset, + (fmt_size_t)new_var->offset, new_var->data.memsize, new_var->data.capacity, new_var->data.digits, new_var->data.rdigits, - new_var->attr, - (void*)new_var); + (fmt_size_t)new_var->attr, + static_cast<void*>(new_var)); if( is_table(new_var) ) { - fprintf(stderr," OCCURS:%zd", new_var->occurs.ntimes()); + fprintf(stderr," OCCURS:" HOST_SIZE_T_PRINT_DEC, + (fmt_size_t)new_var->occurs.ntimes()); } - cbl_field_t *parent = parent_of(new_var); + const cbl_field_t *parent = parent_of(new_var); if( parent ) { fprintf(stderr, - " parent:(%zd)%s", - new_var->parent, + " parent:(" HOST_SIZE_T_PRINT_DEC ")%s", + (fmt_size_t)new_var->parent, parent->name); } else @@ -16299,12 +17370,12 @@ parser_symbol_add(struct cbl_field_t *new_var ) size_t parent_index = new_var->parent; if( parent_index ) { - symbol_elem_t *e = symbol_at(parent_index); + const symbol_elem_t *e = symbol_at(parent_index); if( e->type == SymFile ) { fprintf(stderr, - " parent_file:(%zd)%s", - new_var->parent, + " parent_file:(" HOST_SIZE_T_PRINT_DEC ")%s", + (fmt_size_t)new_var->parent, e->elem.file.name); if( e->elem.file.attr & external_e ) { @@ -16318,7 +17389,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) { fprintf(stderr, " redefines:(%p)%s", - (void*)symbol_redefines(new_var), + static_cast<void*>(symbol_redefines(new_var)), symbol_redefines(new_var)->name); } @@ -16370,18 +17441,12 @@ parser_symbol_add(struct cbl_field_t *new_var ) { cbl_field_type_t incoming_type = new_var->type; - if( is_register_field(new_var) ) + if( new_var->attr & register_e ) { psa_global(new_var); goto done; } - if( new_var->type == FldBlob ) - { - psa_FldBlob(new_var); - goto done; - } - if( new_var->type == FldLiteralA ) { new_var->data.picture = ""; @@ -16390,18 +17455,12 @@ parser_symbol_add(struct cbl_field_t *new_var ) } size_t length_of_initial_string = 0; - const char *new_initial = NULL; - - // gg_printf("parser_symbol_add %s\n", build_string_literal( strlen(new_var->name)+1, new_var->name), NULL_TREE); - - // If we are dealing with an alphanumeric, and it is not hex_encoded, we - // want to convert to single-byte-encoding (if it happens to be UTF-8) and - // to EBCDIC, if EBCDIC is in force: + char *new_initial = NULL; // Make sure we have a new variable to work with. if( !new_var ) { - cbl_internal_error("parser_symbol_add() was called with a NULL new_var\n"); + cbl_internal_error("%<parser_symbol_add()%> was called with a NULL %<new_var%>"); } TRACE1 @@ -16418,10 +17477,12 @@ parser_symbol_add(struct cbl_field_t *new_var ) TRACE1_TEXT_ABC(" (", cbl_field_type_str(new_var->type) ,")") if( new_var->type == FldLiteralN) { + const void *p1 = (new_var->data.initial); + const long *pldata = static_cast<const long *>(p1); + long ldata = *pldata; gg_fprintf( trace_handle, 1, " [%ld]", - build_int_cst_type(LONG, - *(const long *)new_var->data.initial)); + build_int_cst_type(LONG, ldata)); } TRACE1_END } @@ -16429,7 +17490,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( is_table(new_var) && new_var->data.capacity == 0) { cbl_internal_error( - "%s(): %2.2d %s is a table, but it improperly has a capacity of zero", + "%s: %d %s is a table, but it improperly has a capacity of zero", __func__, new_var->level, new_var->name); @@ -16469,23 +17530,20 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( ancestor == new_var ) { - cbl_internal_error("parser_symbol_add(): %s is its own ancestor", - new_var->name); + cbl_internal_error("%s: %s is its own ancestor", __func__, new_var->name); } if( !ancestor && (new_var->level > LEVEL01 && new_var->level <= LEVEL49 ) ) { - cbl_internal_error("parser_symbol_add(): %2.2d %s has null ancestor", - new_var->level, - new_var->name); + cbl_internal_error("%s: %d %qs has NULL ancestor", __func__, + new_var->level, new_var->name); } // new_var's var_decl_node should be NULL at this point if( new_var->var_decl_node ) { - cbl_internal_error( "parser_symbol_add( %s ) improperly has a non-null " - "var_decl_node\n", - new_var->name); + cbl_internal_error( "%s(%s) improperly has a non-null " + "%<var_decl_node%>", __func__, new_var->name); } switch( new_var->type ) @@ -16588,24 +17646,15 @@ parser_symbol_add(struct cbl_field_t *new_var ) size_t our_index = new_var->our_index; - // During the early stages of implementing cbl_field_t::our_index, there - // were execution paths in parse.y and parser.cc that resulted in our_index - // not being set. I hereby try to use field_index() to find the index - // of this field to resolve those. I note that field_index does a linear - // search of the symbols[] table to find that index. That's why I don't - // use it routinely; it results in O(N^squared) computational complexity - // to do a linear search of the symbol table for each symbol - if( !our_index && new_var->type != FldLiteralN && !(new_var->attr & intermediate_e)) { - our_index = field_index(new_var); - if( our_index == (size_t)-1 ) - { - // Hmm. Couldn't find it. Seems odd. - our_index = 0; - } + // During the early stages of implementing cbl_field_t::our_index, there + // were execution paths in parse.y and parser.cc that resulted in + // our_index not being set. Those should be gone. + fprintf(stderr, "our_index is NULL under unanticipated circumstances"); + gcc_assert(false); } // When we create the cblc_field_t structure, we need a data pointer @@ -16614,7 +17663,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) // we calculate data as the pointer to our parent's data plus our // offset. - // declare and define the structure. This code *must* match + // Declare and define the structure. This code *must* match // the C structure declared in libgcobol.c. Towards that end, the // variables are declared in descending order of size in order to // make the packing match up. @@ -16688,7 +17737,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) && new_var->type != FldLiteralN && new_var->type != FldLiteralA ) { - cbl_internal_error( "%s(): %2.2d %s<%s> improperly has a data.capacity of zero", + cbl_internal_error( "%s: %d %s<%s> improperly has a data.capacity of zero", __func__, new_var->level, new_var->name, @@ -16708,10 +17757,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( *external_record_base ) { char achDataName[256]; - if( *external_record_base ) - { - sprintf(achDataName, "__%s_vardata", external_record_base); - } + sprintf(achDataName, "__%s_vardata", external_record_base); tree array_type = build_array_type_nelts(UCHAR, new_var->data.capacity); new_var->data_decl_node = gg_define_variable( array_type, @@ -16758,11 +17804,10 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( !bytes_to_allocate ) { - fprintf(stderr, - "bytes_to_allocate is zero for %s (symbol number %ld)\n", - new_var->name, - new_var->our_index); - gcc_assert(bytes_to_allocate); + cbl_internal_error( "%<bytes_to_allocate%> is zero for %s (symbol number " + HOST_SIZE_T_PRINT_DEC ")", + new_var->name, + (fmt_size_t)new_var->our_index); } if( new_var->type == FldIndex && new_var->level == 0 ) @@ -16785,55 +17830,47 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( bytes_to_allocate ) { - if( new_var->attr & (intermediate_e) - && new_var->type != FldLiteralN - && new_var->type != FldLiteralA ) + // We need a unique name for the allocated data for this COBOL variable: + char achDataName[256]; + if( new_var->attr & external_e ) { - // We'll malloc() data in initialize_variable - data_area = null_pointer_node; + sprintf(achDataName, "%s", new_var->name); + } + else if( new_var->name[0] == '_' ) + { + // Avoid doubling up on leading underscore + sprintf(achDataName, + "%s_data_" HOST_SIZE_T_PRINT_UNSIGNED, + new_var->name, + (fmt_size_t)sv_data_name_counter++); } else { - // We need a unique name for the allocated data for this COBOL variable: - char achDataName[256]; - if( new_var->attr & external_e ) - { - sprintf(achDataName, "%s", new_var->name); - } - else if( new_var->name[0] == '_' ) - { - // Avoid doubling up on leading underscore - sprintf(achDataName, - "%s_data_%lu", - new_var->name, - sv_data_name_counter++); - } - else - { - sprintf(achDataName, - "_%s_data_%lu", - new_var->name, - sv_data_name_counter++); - } + sprintf(achDataName, + "_%s_data_" HOST_SIZE_T_PRINT_UNSIGNED, + new_var->name, + (fmt_size_t)sv_data_name_counter++); + } - if( new_var->attr & external_e ) - { - tree array_type = build_array_type_nelts(UCHAR, bytes_to_allocate); - new_var->data_decl_node = gg_define_variable( - array_type, - achDataName, - vs_external); - data_area = gg_get_address_of(new_var->data_decl_node); - } - else - { - tree array_type = build_array_type_nelts(UCHAR, bytes_to_allocate); - new_var->data_decl_node = gg_define_variable( - array_type, - achDataName, - vs_static); - data_area = gg_get_address_of(new_var->data_decl_node); - } + if( new_var->attr & external_e ) + { + tree array_type = build_array_type_nelts(UCHAR, bytes_to_allocate); + new_var->data_decl_node = gg_define_variable( + array_type, + achDataName, + vs_external); + data_area = gg_get_address_of(new_var->data_decl_node); + } + else + { + gg_variable_scope_t vs_scope = (new_var->attr & intermediate_e) + ? vs_stack : vs_static ; + tree array_type = build_array_type_nelts(UCHAR, bytes_to_allocate); + new_var->data_decl_node = gg_define_variable( + array_type, + achDataName, + vs_scope); + data_area = gg_get_address_of(new_var->data_decl_node); } } } @@ -16841,7 +17878,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( new_var->data.initial ) { - new_initial = initial_from_float128(new_var, new_var->data.value_of()); + new_initial = initial_from_initial(new_var); } if( new_initial ) { @@ -16853,6 +17890,10 @@ parser_symbol_add(struct cbl_field_t *new_var ) length_of_initial_string = new_var->data.capacity+1; break; + case FldLiteralN: + length_of_initial_string = strlen(new_initial)+1; + break; + default: length_of_initial_string = new_var->data.capacity; break; @@ -16860,50 +17901,19 @@ parser_symbol_add(struct cbl_field_t *new_var ) } else { - new_initial = new_var->data.initial; - if( !new_initial ) - { - if( length_of_initial_string ) - { - gcc_unreachable(); - } - } - else - { - if( new_var->type == FldLiteralN ) - { - // We need to convert this string to the internal character set - // char *buffer = NULL; - // size_t buffer_size = 0; - // raw_to_internal(&buffer, - // &buffer_size, - // new_var->data.initial, - // strlen(new_var->data.initial)); - // new_initial = bufer; - // length_of_initial_string = strlen(new_var->data.initial)+1; - } - } + new_initial = static_cast<char *>(xmalloc(length_of_initial_string)); + gcc_assert(new_initial); + memcpy(new_initial, new_var->data.initial, length_of_initial_string); } actual_allocate: - // if( level_88_string ) - // { - // actually_create_the_static_field( new_var, - // data_area, - // level_88_string_size, - // level_88_string, - // immediate_parent, - // new_var_decl); - // } - // else - { - actually_create_the_static_field( new_var, - data_area, - length_of_initial_string, - new_initial, - immediate_parent, - new_var_decl); - } + actually_create_the_static_field( new_var, + data_area, + length_of_initial_string, + new_initial, + immediate_parent, + new_var_decl); + free(new_initial); if( level_88_string ) { diff --git a/gcc/cobol/genapi.h b/gcc/cobol/genapi.h index 2c135e8..802bba7 100644 --- a/gcc/cobol/genapi.h +++ b/gcc/cobol/genapi.h @@ -7,7 +7,7 @@ * * * 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 + * * 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. @@ -52,17 +52,26 @@ void parser_division( cbl_division_t division, void parser_enter_program(const char *funcname, bool is_function, int *retval); void parser_leave_program(); -void parser_accept( cbl_refer_t refer, special_name_t special_e); +void parser_accept( const cbl_refer_t &refer, + special_name_t special_e, + cbl_label_t *error, + cbl_label_t *not_error ); void parser_accept_exception( cbl_label_t *name ); void parser_accept_exception_end( cbl_label_t *name ); -void parser_accept_envar( cbl_refer_t refer, cbl_refer_t envar, - cbl_label_t *error, cbl_label_t *not_error ); -void parser_set_envar( cbl_refer_t envar, cbl_refer_t refer ); +void parser_accept_under_discussion(struct cbl_refer_t tgt, special_name_t special, + cbl_label_t *error, cbl_label_t *not_error ); +void parser_accept_envar( const cbl_refer_t &refer, + const cbl_refer_t &envar, + cbl_label_t *error, + cbl_label_t *not_error ); +void parser_set_envar( const cbl_refer_t &envar, const cbl_refer_t &refer ); -void parser_accept_command_line( cbl_refer_t tgt, cbl_refer_t src, - cbl_label_t *error, cbl_label_t *not_error ); -void parser_accept_command_line_count( cbl_refer_t tgt ); +void parser_accept_command_line(const cbl_refer_t &tgt, + const cbl_refer_t &src, + cbl_label_t *error, + cbl_label_t *not_error ); +void parser_accept_command_line_count( const cbl_refer_t &tgt ); void parser_accept_date_yymmdd( cbl_field_t *tgt ); void parser_accept_date_yyyymmdd( cbl_field_t *tgt ); @@ -72,7 +81,7 @@ void parser_accept_date_dow( cbl_field_t *tgt ); void parser_accept_date_hhmmssff( cbl_field_t *tgt ); void -parser_alphabet( cbl_alphabet_t& alphabet ); +parser_alphabet( const cbl_alphabet_t& alphabet ); void parser_alphabet_use( cbl_alphabet_t& alphabet ); @@ -81,13 +90,24 @@ parser_allocate( cbl_refer_t size_or_based, cbl_refer_t returning, bool initiali void parser_free( size_t n, cbl_refer_t refers[] ); +void parser_xml_parse( cbl_label_t *stmt, + cbl_refer_t input, + cbl_field_t *encoding, + cbl_field_t *validating, + bool returns_national, + cbl_label_t *from_proc, + cbl_label_t *to_proc ); + +void parser_xml_on_exception( cbl_label_t *name ); +void parser_xml_not_exception( cbl_label_t *name ); +void parser_xml_end( cbl_label_t *name ); + void parser_add( size_t nC, cbl_num_result_t *C, size_t nA, cbl_refer_t *A, cbl_arith_format_t format, cbl_label_t *error, - cbl_label_t *not_error, - void *compute_error = NULL); // This has to be cast to a tree pointer to int + cbl_label_t *not_error, void *compute_error = NULL); // This has to be cast to a tree pointer to int void parser_arith_error( cbl_label_t *name ); void parser_arith_error_end( cbl_label_t *name ); @@ -119,26 +139,26 @@ parser_divide(size_t nC, cbl_num_result_t *C, void *compute_error = NULL); // This has to be cast to a tree pointer to int void -parser_add( struct cbl_refer_t tgt, - struct cbl_refer_t a, struct cbl_refer_t b, +parser_add( const cbl_refer_t& tgt, + const cbl_refer_t& a, const cbl_refer_t& b, enum cbl_round_t = truncation_e ); void -parser_subtract( struct cbl_refer_t tgt, - struct cbl_refer_t a, struct cbl_refer_t b, +parser_subtract( const cbl_refer_t& tgt, + const cbl_refer_t& a, const cbl_refer_t& b, enum cbl_round_t = truncation_e ); void -parser_multiply( struct cbl_refer_t tgt, - struct cbl_refer_t a, struct cbl_refer_t b, +parser_multiply( const cbl_refer_t& tgt, + const cbl_refer_t& a, const cbl_refer_t& b, enum cbl_round_t = truncation_e ); void -parser_divide( struct cbl_refer_t quotient, - struct cbl_refer_t divisor, - struct cbl_refer_t dividend, +parser_divide( const cbl_refer_t& quotient, + const cbl_refer_t& divisor, + const cbl_refer_t& dividend, enum cbl_round_t = truncation_e, - struct cbl_refer_t remainder = cbl_refer_t()); + const cbl_refer_t& remainder = cbl_refer_t()); // void // parser_exponentiation( cbl_refer_t cref, @@ -174,7 +194,8 @@ parser_bitwise_op(struct cbl_field_t *tgt, void parser_classify( struct cbl_field_t *tgt, - struct cbl_refer_t srca, enum classify_t type ); + const struct cbl_refer_t &srca, + enum classify_t type ); void parser_op( struct cbl_refer_t cref, @@ -225,7 +246,7 @@ parser_perform_conditional_end( struct cbl_perform_tgt_t *tgt ); * For an in-line loop body, tgt->from.type == LblLoop, and tgt->to is NULL. */ void -parser_perform( struct cbl_perform_tgt_t *tgt, struct cbl_refer_t N ); +parser_perform( const cbl_perform_tgt_t *tgt, cbl_refer_t N ); /* * A simple UNTIL loop uses 1 varys element. For VARY loops, the @@ -253,18 +274,23 @@ parser_program_hierarchy( const struct cbl_prog_hier_t& hier ); void parser_end_program(const char *name=NULL); -void parser_sleep(cbl_refer_t seconds); +void parser_sleep(const cbl_refer_t &seconds); -void parser_exit( cbl_refer_t refer, ec_type_t = ec_none_e ); +void parser_exit( const cbl_refer_t& refer, ec_type_t = ec_none_e ); void parser_exit_section(void); void parser_exit_paragraph(void); void parser_exit_perform( struct cbl_perform_tgt_t *tgt, bool cycle ); void parser_exit_program(void); // exits back to COBOL only, else continue void +parser_exhibit( bool changed, bool named, + const std::vector<cbl_refer_t> &args ); +void parser_display( const struct cbl_special_name_t *upon, - struct cbl_refer_t args[], size_t n, - bool advance = DISPLAY_ADVANCE ); + const std::vector<cbl_refer_t> &args, + bool advance = DISPLAY_ADVANCE, + const cbl_label_t *not_error = nullptr, + const cbl_label_t *compute_error = nullptr ); void parser_display_field(cbl_field_t *fld); @@ -297,10 +323,10 @@ void parser_symbol_add(struct cbl_field_t *field); void -parser_initialize(struct cbl_refer_t refer, bool like_parser_symbol_add=false); +parser_initialize(const cbl_refer_t& refer, bool like_parser_symbol_add=false); void -parser_initialize_programs(size_t nprog, struct cbl_refer_t *progs); +parser_initialize_programs(size_t nprog, const struct cbl_refer_t *progs); void parser_label_label( struct cbl_label_t *label ); @@ -308,6 +334,9 @@ parser_label_label( struct cbl_label_t *label ); void parser_label_goto( struct cbl_label_t *label ); +callback_t * +parser_label_addr( struct cbl_label_t *label ); + void parser_goto( cbl_refer_t value, size_t narg, cbl_label_t * const labels[] ); @@ -315,7 +344,7 @@ void parser_alter( cbl_perform_tgt_t *tgt ); void -parser_set_conditional88( struct cbl_refer_t tgt, bool which_way ); +parser_set_conditional88( const cbl_refer_t& tgt, bool which_way ); void parser_set_numeric(struct cbl_field_t *tgt, ssize_t value); @@ -371,6 +400,12 @@ parser_file_rewrite( cbl_file_t *file, cbl_field_t *field, void parser_file_delete( cbl_file_t *file, bool sequentially ); +void parser_file_delete_file( cbl_label_t *name, + std::vector<cbl_file_t*> filenames ); +void parser_file_delete_on_exception( cbl_label_t *name ); +void parser_file_delete_not_exception( cbl_label_t *name ); +void parser_file_delete_end( cbl_label_t *name ); + #if condition_lists struct cbl_conditional_t { cbl_field_t *tgt; @@ -406,14 +441,12 @@ void parser_sort(cbl_refer_t table, bool duplicates, cbl_alphabet_t *alphabet, - size_t nkey, - cbl_key_t *keys ); + const std::vector<cbl_key_t>& keys ); void parser_file_sort( cbl_file_t *file, bool duplicates, cbl_alphabet_t *alphabet, - size_t nkey, - cbl_key_t *keys, + const std::vector<cbl_key_t>& keys, size_t ninput, cbl_file_t **inputs, size_t noutput, @@ -423,8 +456,7 @@ parser_file_sort( cbl_file_t *file, void parser_file_merge( cbl_file_t *file, cbl_alphabet_t *alphabet, - size_t nkey, - cbl_key_t *keys, + const std::vector<cbl_key_t>& keys, size_t ninput, cbl_file_t **inputs, size_t noutput, @@ -450,7 +482,7 @@ parser_intrinsic_numval_c( cbl_field_t *f, void parser_intrinsic_subst( cbl_field_t *f, - cbl_refer_t& ref1, + const cbl_refer_t& ref1, size_t argc, cbl_substitute_t * argv ); @@ -492,12 +524,12 @@ void parser_string_overflow_end( cbl_label_t *name ); void -parser_string( cbl_refer_t tgt, - cbl_refer_t pointer, - size_t nsource, - cbl_string_src_t *sources, - cbl_label_t *overflow, - cbl_label_t *not_overflow ); +parser_string(const cbl_refer_t& tgt, + const cbl_refer_t& pointer, + size_t nsource, + cbl_string_src_t *sources, + cbl_label_t *overflow, + cbl_label_t *not_overflow ); void parser_unstring( cbl_refer_t src, @@ -518,25 +550,18 @@ void parser_return_atend( cbl_file_t *file ); void parser_return_notatend( cbl_file_t *file ); void parser_return_finish( cbl_file_t *file ); -void parser_exception_prepare( const cbl_name_t statement_name, - const cbl_enabled_exceptions_array_t *enabled ); - -//void parser_exception_condition( cbl_field_t *ec ); - struct cbl_exception_file; -struct cbl_exception_files_t; void parser_exception_raise(ec_type_t ec); void parser_call_exception( cbl_label_t *name ); void parser_call_exception_end( cbl_label_t *name ); -//void parser_stash_exceptions(const cbl_enabled_exceptions_array_t *enabled); - -void parser_match_exception(cbl_field_t *index, - cbl_field_t *blob); +void parser_match_exception(cbl_field_t *index); void parser_check_fatal_exception(); void parser_clear_exception(); +void parser_push_exception(); +void parser_pop_exception(); void parser_call_targets_dump(); size_t parser_call_target_update( size_t caller, @@ -554,10 +579,11 @@ void parser_call( cbl_refer_t name, void parser_entry_activate( size_t iprog, const cbl_label_t *declarative ); -void parser_entry( cbl_field_t *name, - size_t narg = 0, cbl_ffi_arg_t args[] = NULL); +void parser_entry( const cbl_field_t *name, + size_t narg = 0, + cbl_ffi_arg_t args[] = NULL); -bool is_ascending_key(cbl_refer_t key); +bool is_ascending_key(const cbl_refer_t& key); void register_main_switch(const char *main_string); @@ -569,11 +595,6 @@ void parser_print_long(const char *fmt, long N); // fmt needs to have a %ls in i void parser_print_string(const char *ach); void parser_print_string(const char *fmt, const char *ach); // fmt needs to have a %s in it void parser_set_statement(const char *statement); - -char *initial_from_float128(cbl_field_t *field, _Float128 value); - -void parser_set_handled(ec_type_t ec_handled); -void parser_set_file_number(int file_number); void parser_exception_clear(); void parser_init_list_size(int count_of_variables); @@ -582,6 +603,9 @@ void parser_init_list(); tree file_static_variable(tree type, const char *name); -void parser_statement_begin(); +void parser_statement_begin( const cbl_name_t name, tree ecs, tree dcls ); + +tree parser_compile_ecs( const std::vector<uint64_t>& ecs ); +tree parser_compile_dcls( const std::vector<uint64_t>& dcls ); #endif diff --git a/gcc/cobol/gengen.cc b/gcc/cobol/gengen.cc index 2796b7f..fa792d6 100644 --- a/gcc/cobol/gengen.cc +++ b/gcc/cobol/gengen.cc @@ -95,7 +95,6 @@ #include "toplev.h" #include "function.h" #include "fold-const.h" -#define HOWEVER_GCC_DEFINES_TREE 1 #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -108,8 +107,6 @@ // Don't like it? Cry me a river. static const int ARG_LIMIT = 512; -static int sv_current_line_number; - // These are globally useful constants tree char_nodes[256]; @@ -137,6 +134,14 @@ tree bool_false_node; struct cbl_translation_unit_t gg_trans_unit; +// This set is used to prevent duplicated top-level program names from breaking +// the compiler when a source code module makes that mistake. +static std::unordered_set<std::string> names_we_have_seen; + +// This vector is used to process the function_decls at the point we leave +// the file. +static std::vector<tree> finalized_function_decls; + void gg_build_translation_unit(const char *filename) { @@ -258,14 +263,6 @@ gg_append_var_decl(tree var_decl) } } -location_t -location_from_lineno() - { - location_t loc; - loc = linemap_line_start(line_table, sv_current_line_number, 0); - return loc; - } - void gg_append_statement(tree stmt) { @@ -355,13 +352,12 @@ adjust_for_type(tree type) return retval; } -static char * -show_type(tree type) +gg_show_type(tree type) { if( !type ) { - cbl_internal_error("The given type is not NULL, and that's just not fair"); + cbl_internal_error("The given type is NULL, and that is just not fair"); } if( DECL_P(type) ) @@ -370,12 +366,19 @@ show_type(tree type) } if( !TYPE_P(type) ) { - cbl_internal_error("The given type is not a DECL or a TYPE"); + cbl_internal_error("The given type is not a declaration or a TYPE"); } - static char ach[1024]; + static char ach[1100]; + static char ach2[1024]; + static char ach3[1024]; switch( TREE_CODE(type) ) { + case POINTER_TYPE: + strcpy(ach2, gg_show_type(TREE_TYPE(type))); + sprintf(ach, "POINTER to %s", ach2); + break; + case VOID_TYPE: sprintf(ach, "VOID"); break; @@ -390,23 +393,20 @@ show_type(tree type) case REAL_TYPE: sprintf(ach, - "%3ld-bit REAL", + "%3" PRId64 "-bit REAL", TREE_INT_CST_LOW(TYPE_SIZE(type))); break; case INTEGER_TYPE: sprintf(ach, - "%3ld-bit %s INT", + "%3" PRId64 "-bit %s INT", TREE_INT_CST_LOW(TYPE_SIZE(type)), (TYPE_UNSIGNED(type) ? "unsigned" : " signed")); break; case FUNCTION_TYPE: - sprintf(ach, "FUNCTION"); -// sprintf(ach, -// "%3ld-bit %s INT", -// TREE_INT_CST_LOW(TYPE_SIZE(type)), -// (TYPE_UNSIGNED(type) ? "unsigned" : " signed")); + strcpy(ach3, gg_show_type(TREE_TYPE(type))); + sprintf(ach, "FUNCTION returning %s", ach3); break; default: @@ -416,7 +416,7 @@ show_type(tree type) return ach; } -void +tree gg_assign(tree dest, const tree source) { // This does the equivalent of a C/C++ "dest = source". When X1 is set, it @@ -427,6 +427,7 @@ gg_assign(tree dest, const tree source) // This routine also provides for the possibility that the assignment is // for a source that is a function invocation, as in // "dest = function_call()" + tree stmt = NULL_TREE; saw_pointer = false; tree dest_type = adjust_for_type(TREE_TYPE(dest)); @@ -449,11 +450,11 @@ gg_assign(tree dest, const tree source) if( okay ) { - tree stmt = build2_loc( location_from_lineno(), - MODIFY_EXPR, - TREE_TYPE(dest), - dest, - source); + stmt = build2_loc(gg_token_location(), + MODIFY_EXPR, + TREE_TYPE(dest), + dest, + source); gg_append_statement(stmt); } else @@ -462,20 +463,25 @@ gg_assign(tree dest, const tree source) // the same. This is a compilation-time error, since we want the caller to // have sorted the types out explicitly. If we don't throw an error here, // the gimple reduction will do so. Better to do it here, when we know - // where we are. - dbgmsg("Inefficient assignment"); - if(DECL_P(dest) && DECL_NAME(dest)) + // where we are.S + static const int debugging = 1; + if( debugging ) { - dbgmsg(" Destination is %s", IDENTIFIER_POINTER(DECL_NAME(dest))); - } - dbgmsg(" dest type is %s%s", show_type(dest_type), p2 ? "_P" : ""); - if(DECL_P(source) && DECL_NAME(source)) - { - dbgmsg(" Source is %s", IDENTIFIER_POINTER(DECL_NAME(source))); + fprintf(stderr, "Inefficient assignment\n"); + if(DECL_P(dest) && DECL_NAME(dest)) + { + fprintf(stderr, " Destination is %s\n", IDENTIFIER_POINTER(DECL_NAME(dest))); + } + fprintf(stderr, " dest type is %s%s\n", gg_show_type(dest_type), p2 ? "_P" : ""); + if(DECL_P(source) && DECL_NAME(source)) + { + fprintf(stderr, " Source is %s\n", IDENTIFIER_POINTER(DECL_NAME(source))); + } + fprintf(stderr, " source type is %s%s\n", gg_show_type(source_type), p2 ? "_P" : ""); } - dbgmsg(" source type is %s%s", show_type(source_type), p2 ? "_P" : ""); - gcc_unreachable(); + cbl_internal_error("Attempting an assignment of differing types."); } + return stmt; } tree @@ -517,11 +523,9 @@ gg_find_field_in_struct(const tree base, const char *field_name) if( !field_decl ) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### Somebody asked for the field %s.%s, which doesn't exist", + cbl_internal_error("Somebody asked for the field %s.%s, which does not exist", IDENTIFIER_POINTER(DECL_NAME(base)), field_name); - gcc_unreachable(); } return field_decl; @@ -609,7 +613,7 @@ gg_add_field_to_structure(const tree type_of_field, const char *name_of_field, t tree id_of_field = get_identifier (name_of_field); // Create the new field: - tree new_field_decl = build_decl( location_from_lineno(), + tree new_field_decl = build_decl( gg_token_location(), FIELD_DECL, id_of_field, type_of_field); @@ -886,10 +890,11 @@ gg_create_assembler_name(const char *cobol_name) static char * gg_unique_in_function(const char *var_name, gg_variable_scope_t vs_scope) { - char *retval = (char *)xmalloc(strlen(var_name)+32); + char *retval = static_cast<char *>(xmalloc(strlen(var_name)+32)); if( (vs_scope == vs_stack || vs_scope == vs_static) ) { - sprintf(retval, "%s.%ld", var_name, current_function->program_id_number); + sprintf(retval, "%s." HOST_SIZE_T_PRINT_DEC, var_name, + (fmt_size_t)current_function->program_id_number); } else { @@ -929,7 +934,7 @@ gg_declare_variable(tree type_decl, // causes the storage to be allocated. // It is routine to let the compiler assign names to stack variables. The - // assembly code doesn't use names for variables on the stack; they are + // assembly code does not use names for variables on the stack; they are // referenced by offsets to the base pointer. But static variables have to // have names, and there are places in my code generation -- Lord only knows // why -- where I didn't give the variables explicit names. We remedy that @@ -1020,10 +1025,7 @@ gg_declare_variable(tree type_decl, break; } DECL_INITIAL(var_decl) = initial_value; - if( unique_name ) - { - free(unique_name); - } + free(unique_name); return var_decl; } @@ -1038,7 +1040,7 @@ gg_define_from_declaration(tree var_decl) { // Having made sure the chain of variable declarations is nicely started, // it's time to actually define the storage with a decl_expression: - tree stmt = build1_loc (location_from_lineno(), + tree stmt = build1_loc (gg_token_location(), DECL_EXPR, TREE_TYPE(var_decl), var_decl); @@ -1769,7 +1771,7 @@ gg_build_relational_expression(tree operand_a, compare = LE_EXPR; break; } - tree relational_expression = build2_loc(location_from_lineno(), + tree relational_expression = build2_loc(gg_token_location(), compare, boolean_type_node, operand_a, @@ -1827,7 +1829,10 @@ gg_build_logical_expression(tree operand_a, } void -gg_create_goto_pair(tree *goto_expr, tree *label_expr, tree *label_addr, const char *name) +gg_create_goto_pair(tree *goto_expr, + tree *label_expr, + tree *label_addr, + const char *name) { // We are going to create a pair of expressions for our // caller. They are a matched set of goto/label expressions, @@ -1884,16 +1889,6 @@ gg_create_goto_pair(tree *goto_expr, } void -gg_goto_label_decl(tree label_decl) - { - tree goto_expr = build1_loc( location_from_lineno(), - GOTO_EXPR, - void_type_node, - label_decl); - gg_append_statement(goto_expr); - } - -void gg_create_goto_pair(tree *goto_expr, tree *label_expr) { // We are going to create a pair of expressions for our @@ -1933,7 +1928,7 @@ gg_create_goto_pair(tree *goto_expr, tree *label_expr, const char *name) void gg_goto(tree var_decl_pointer) { - tree go_to = build1_loc(location_from_lineno(), + tree go_to = build1_loc(gg_token_location(), GOTO_EXPR, void_type_node, var_decl_pointer); @@ -2148,18 +2143,6 @@ gg_printf(const char *format_string, ...) int nargs = 0; tree args[ARG_LIMIT]; - // Because this routine is intended for debugging, we are sending the - // text to STDERR - - // Because we don't actually use stderr ourselves, we just pick it up as a - // VOID_P and pass it along to fprintf() - tree t_stderr = gg_declare_variable(VOID_P, "stderr", - NULL_TREE, - vs_external_reference); - - gg_push_context(); - - args[nargs++] = t_stderr; args[nargs++] = build_string_literal(strlen(format_string)+1, format_string); va_list ap; @@ -2169,20 +2152,15 @@ gg_printf(const char *format_string, ...) { if(nargs >= ARG_LIMIT) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### You *must* be joking!"); - gcc_unreachable(); + cbl_internal_error("You *must* be joking"); } if( TREE_CODE(arg) >= NUM_TREE_CODES) { // Warning: This test is not completely reliable, because a garbage // byte could have a valid TREE_CODE. But it does help. - yywarn("You nitwit!"); - yywarn("You forgot to put a NULL_TREE at the end of a " - "gg_printf() again!"); - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - gcc_unreachable(); + cbl_internal_error("You forgot to put a %<NULL_TREE%> at the end of a " + "%<gg_printf()%> again"); } args[nargs++] = arg; @@ -2193,17 +2171,15 @@ gg_printf(const char *format_string, ...) static tree function = NULL_TREE; if( !function ) { - function = gg_get_function_address(INT, "fprintf"); + function = gg_get_function_address(INT, "__gg__fprintf_stderr"); } - tree stmt = build_call_array_loc (location_from_lineno(), + tree stmt = build_call_array_loc (gg_token_location(), INT, function, nargs, args); gg_append_statement(stmt); - - gg_pop_context(); } tree @@ -2229,9 +2205,7 @@ gg_fprintf(tree fd, int nargs, const char *format_string, ...) { if(argc >= ARG_LIMIT) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### You *must* be joking!"); - gcc_unreachable(); + cbl_internal_error("You *must* be joking"); } args[argc++] = arg; @@ -2246,7 +2220,7 @@ gg_fprintf(tree fd, int nargs, const char *format_string, ...) function = gg_get_function_address(INT, "sprintf"); } - tree stmt = build_call_array_loc (location_from_lineno(), + tree stmt = build_call_array_loc (gg_token_location(), INT, function, argc, @@ -2293,7 +2267,7 @@ void gg_memset(tree dest, const tree value, tree size) { tree the_call = - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_MEMSET), 3, dest, @@ -2307,7 +2281,7 @@ gg_memchr(tree buf, tree ch, tree length) { tree the_call = fold_convert( pvoid_type_node, - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_MEMCHR), 3, buf, @@ -2322,7 +2296,7 @@ void gg_memcpy(tree dest, const tree src, tree size) { tree the_call = build_call_expr_loc( - location_from_lineno(), + gg_token_location(), builtin_decl_explicit (BUILT_IN_MEMCPY), 3, dest, @@ -2337,7 +2311,7 @@ void gg_memmove(tree dest, const tree src, tree size) { tree the_call = build_call_expr_loc( - location_from_lineno(), + gg_token_location(), builtin_decl_explicit (BUILT_IN_MEMMOVE), 3, dest, @@ -2370,7 +2344,7 @@ void gg_strcpy(tree dest, tree src) { tree the_call = - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_STRCPY), 2, dest, @@ -2383,7 +2357,7 @@ gg_strcmp(tree A, tree B) { tree the_call = fold_convert( integer_type_node, - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_STRCMP), 2, A, @@ -2415,7 +2389,7 @@ gg_strncmp(tree char_star_A, tree char_star_B, tree size_t_N) { tree the_call = fold_convert( integer_type_node, - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_STRNCMP), 3, char_star_A, @@ -2446,7 +2420,7 @@ gg_return(tree operand) { // When there is no operand, or if the function result is void, then // we just generate a return_expr. - stmt = build1_loc(location_from_lineno(), RETURN_EXPR, void_type_node, NULL_TREE); + stmt = build1_loc(gg_token_location(), RETURN_EXPR, void_type_node, NULL_TREE); } else { @@ -2456,7 +2430,7 @@ gg_return(tree operand) function_type, DECL_RESULT(current_function->function_decl), gg_cast(function_type, operand)); - stmt = build1_loc(location_from_lineno(), RETURN_EXPR, void_type_node, modify); + stmt = build1_loc(gg_token_location(), RETURN_EXPR, void_type_node, modify); } gg_append_statement(stmt); } @@ -2464,7 +2438,7 @@ gg_return(tree operand) void chain_parameter_to_function(tree function_decl, const tree param_type, const char *name) { - tree parm = build_decl (location_from_lineno(), + tree parm = build_decl (gg_token_location(), PARM_DECL, get_identifier (name), param_type); @@ -2482,167 +2456,113 @@ chain_parameter_to_function(tree function_decl, const tree param_type, const ch } } -void -gg_modify_function_type(tree function_decl, tree return_type) - { - tree fndecl_type = build_varargs_function_type_array( return_type, - 0, // No parameters yet - NULL); // And, hence, no types - TREE_TYPE(function_decl) = fndecl_type; - tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type); - DECL_CONTEXT (resdecl) = function_decl; - DECL_RESULT (function_decl) = resdecl; - } +/* There are five ways that we use function_decls: -tree -gg_define_function_with_no_parameters(tree return_type, - const char *funcname, - const char *unmangled_name) - { - // This routine builds a function_decl, puts it on the stack, and - // gives it a context. + 1, We define a main() entry point. + 2. We call a function that turns out to be a static "t" function local to the source code module. + 3. We define an global "T" function, and possibly call it later. + 4. We call a function that we define later in the source code module. + 5. We call a function that ends up being an extern that is not defined in the source code module. - // At this time we don't know how many parameters this function expects, so - // we set things up and we'll tack on the parameters later. + Cases 3. and 4. turn out to require the same flags. Here are the combinations of + flags that are required for each flavor of function_decl. This was empirically + determind by compiling a C++ program with sample code for each type. - // Create the FUNCTION_TYPE for that array: - // int nparams = 1; - // tree types[1] = {VOID_P}; - // const char *names[1] = {"_p1"}; + | addressable | used | nothrow | static | external | public | no_instrument +main | | | | X | | X | X +local | X | X | X | X | | | X +external defined inside | X | X | X | X | | X | X +external defined elsewhere | X | X | | | X | X | - // tree fndecl_type = build_varargs_function_type_array( return_type, - // nparams, - // types); +*/ - tree fndecl_type = build_varargs_function_type_array( return_type, - 0, // No parameters yet - NULL); // And, hence, no types - // Create the FUNCTION_DECL for that FUNCTION_TYPE - tree function_decl = build_fn_decl (funcname, fndecl_type); +static std::unordered_map<std::string, tree> map_of_function_decls; - // Some of this stuff is magical, and is based on compiling C programs - // and just mimicking the results. - TREE_ADDRESSABLE(function_decl) = 1; - TREE_STATIC(function_decl) = 1; - DECL_EXTERNAL (function_decl) = 0; - DECL_PRESERVE_P (function_decl) = 0; - DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1; - DECL_ARTIFICIAL(function_decl) = 0; - TREE_NOTHROW(function_decl) = 0; - TREE_USED(function_decl) = 1; +static +std::string function_decl_key(const char *funcname, tree fndecl_type) + { + std::string retval; + retval += funcname; + retval += gg_show_type(TREE_TYPE(fndecl_type)); + return retval; + } - // This code makes COBOL nested programs actual visible on the - // source code "trans_unit_decl" level, but with non-public "static" - // visibility. - if( gg_trans_unit.function_stack.size() == 0 ) - { - // gg_trans_unit.function_stack is empty, so our context is - // the compilation module, and we need to be public: - DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl; - TREE_PUBLIC(function_decl) = 1; - } - else - { - // The stack has something in it, so we are building a nested function. - // Make the current function our context - DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl; - TREE_PUBLIC(function_decl) = 0; +tree +gg_peek_fn_decl(const char *funcname, tree fndecl_type) + { + // When funcname is found in map_of_function_decls, this routine returns + // the type of the return value of that function decl. - // Append this function to the list of functions and variables - // associated with the computation module. - gg_append_var_decl(function_decl); + tree retval = NULL_TREE; + std::string key = function_decl_key(funcname, fndecl_type); + std::unordered_map<std::string, tree>::const_iterator it = + map_of_function_decls.find(key); + if( it != map_of_function_decls.end() ) + { + // This function_decl has already been defined. + retval = TREE_TYPE(TREE_TYPE(it->second)); } - - // Establish the RESULT_DECL for the function: - tree resdecl = build_decl (location_from_lineno(), RESULT_DECL, NULL_TREE, return_type); - DECL_CONTEXT (resdecl) = function_decl; - DECL_RESULT (function_decl) = resdecl; - - // The function_decl has a .function member, a pointer to struct_function. - // This is quietly, almost invisibly, extremely important. You need to - // call this routine after DECL_RESULT has been established: - - allocate_struct_function(function_decl, false); - - struct gg_function_t new_function = {}; - new_function.context_count = 0; - new_function.function_decl = function_decl; - new_function.our_name = IDENTIFIER_POINTER(DECL_NAME(function_decl)); - new_function.our_unmangled_name = xstrdup(unmangled_name); - new_function.function_address = gg_get_function_address(VOID, new_function.our_name); - - // Each program on the stack gets a unique identifier. This is used, for - // example, to make sure that static variables have unique names. - static size_t program_id = 0; - new_function.program_id_number = program_id++; - - // With everything established, put this function_decl on the stack - gg_trans_unit.function_stack.push_back(new_function); - - // All we need is a context, and we are ready to go: - gg_push_context(); - return function_decl; + return retval; } -void -gg_tack_on_function_parameters(tree function_decl, ...) +tree +gg_build_fn_decl(const char *funcname, tree fndecl_type) { - int nparams = 0; + tree function_decl; - tree types[ARG_LIMIT]; - const char *names[ARG_LIMIT]; - - va_list params; - va_start(params, function_decl); - for(;;) + std::string key = function_decl_key(funcname, fndecl_type); + std::unordered_map<std::string, tree>::const_iterator it = + map_of_function_decls.find(key); + if( it != map_of_function_decls.end() ) { - tree var_type = va_arg(params, tree); - if( !var_type ) - { - break; - } - - if( TREE_CODE(var_type) >= NUM_TREE_CODES) - { - // Warning: This test is not completely reliable, because a garbage - // byte could have a valid TREE_CODE. But it does help. - yywarn("You nitwit!"); - yywarn("You forgot to put a NULL_TREE at the end of a " - "gg_define_function() again!"); - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - gcc_unreachable(); - } + // This function_decl has already been defined. Just return it; the caller + // is responsible for modifying it, if necessary. + function_decl = it->second; + } + else + { + // When creating a never-seen function_decl, we default to the type used + // for calling a function defined elsewhere. It's up to our caller to + // modify the flags, for example if this is part of creating a function. - const char *name = va_arg(params, const char *); + function_decl = build_fn_decl(funcname, fndecl_type); - types[nparams] = var_type; - names[nparams] = name; - nparams += 1; - if(nparams > ARG_LIMIT) - { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### %d parameters? Really? Are you insane?",ARG_LIMIT+1); - gcc_unreachable(); - } - } - va_end(params); + // These are the bits shown in the table in the comment up above + TREE_ADDRESSABLE(function_decl) = 1; + TREE_USED(function_decl) = 1; + TREE_NOTHROW(function_decl) = 0; + TREE_STATIC(function_decl) = 0; + DECL_EXTERNAL (function_decl) = 1; + TREE_PUBLIC (function_decl) = 1; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 0; - // Chain the names onto the variables list: - for(int i=0; i<nparams; i++) - { - chain_parameter_to_function(function_decl, types[i], names[i]); + DECL_PRESERVE_P (function_decl) = 0; + DECL_ARTIFICIAL(function_decl) = 0; + map_of_function_decls[key] = function_decl; } + return function_decl; } -void -gg_define_function(tree return_type, const char *funcname, ...) +tree +gg_define_function( tree return_type, + const char *funcname, + const char *unmangled_name, + ...) { // This routine builds a function_decl, puts it on the stack, and // gives it a context. - // After the funcname, we expect the formal parameters: pairs of types/names - // terminated by a NULL_TREE + // At this time we don't know how many parameters this function expects, so + // we set things up and we'll tack on the parameters later. + + /* There is some bookkeeping we need to do to avoid crashing. + + It's possible for the source code to have two top-level functions with + the same name. This is a compile-time error, but the GCC processing gets + upset when it happens. We'll prevent it from happening here: + + */ int nparams = 0; @@ -2650,7 +2570,7 @@ gg_define_function(tree return_type, const char *funcname, ...) const char *names[ARG_LIMIT]; va_list params; - va_start(params,funcname); + va_start(params, unmangled_name); for(;;) { tree var_type = va_arg(params, tree); @@ -2663,11 +2583,8 @@ gg_define_function(tree return_type, const char *funcname, ...) { // Warning: This test is not completely reliable, because a garbage // byte could have a valid TREE_CODE. But it does help. - yywarn("You nitwit!"); - yywarn("You forgot to put a NULL_TREE at the end of a " - "gg_define_function() again!"); - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - gcc_unreachable(); + cbl_internal_error("You forgot to put a %<NULL_TREE%> at the end of a " + "%<gg_define_function()%> again"); } const char *name = va_arg(params, const char *); @@ -2677,32 +2594,32 @@ gg_define_function(tree return_type, const char *funcname, ...) nparams += 1; if(nparams > ARG_LIMIT) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### %d parameters? Really? Are you insane?", - ARG_LIMIT+1); - gcc_unreachable(); + cbl_internal_error("%d parameters? Really? Are you insane?", ARG_LIMIT+1); } } va_end(params); - // Create the FUNCTION_TYPE for that array: + char ach[32]; + std::unordered_set<std::string>::const_iterator it = + names_we_have_seen.find(funcname); + if( it != names_we_have_seen.end() ) + { + static int bum_counter = 1; + // We have seen this name before. Replace it with something unique: + sprintf(ach, "..no_dupes.%d", bum_counter++); + funcname = ach; + } + else + { + names_we_have_seen.insert(funcname); + } + tree fndecl_type = build_varargs_function_type_array( return_type, nparams, types); // Create the FUNCTION_DECL for that FUNCTION_TYPE - tree function_decl = build_fn_decl (funcname, fndecl_type); - - // Some of this stuff is magical, and is based on compiling C programs - // and just mimicking the results. - TREE_ADDRESSABLE(function_decl) = 1; - TREE_STATIC(function_decl) = 1; - DECL_EXTERNAL (function_decl) = 0; - DECL_PRESERVE_P (function_decl) = 0; - DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1; - DECL_ARTIFICIAL(function_decl) = 0; - TREE_NOTHROW(function_decl) = 0; - TREE_USED(function_decl) = 1; + tree function_decl = gg_build_fn_decl (funcname, fndecl_type); // This code makes COBOL nested programs actual visible on the // source code "trans_unit_decl" level, but with non-public "static" @@ -2710,22 +2627,40 @@ gg_define_function(tree return_type, const char *funcname, ...) if( gg_trans_unit.function_stack.size() == 0 ) { // gg_trans_unit.function_stack is empty, so our context is - // the compilation module, and we need to be public: + // the compilation module, and we need to be public because this is a + // top-level function with global scope: + + // These are the bits shown in the table for gg_build_fn_decl() + TREE_ADDRESSABLE(function_decl) = 1; + TREE_USED(function_decl) = 1; + TREE_NOTHROW(function_decl) = 1; + TREE_STATIC(function_decl) = 1; + DECL_EXTERNAL (function_decl) = 0; + TREE_PUBLIC (function_decl) = 1; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1; DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl; - TREE_PUBLIC(function_decl) = 1; } else { - // The stack has something in it, so we are building a nested function. - // Make the current function our context + // The stack has something in it, so we are building a contained + // program-id. Such function are implemented local static functions. + // + // It's not necessarily true that a static call to such a function will be + // part of the source code (the call can be through a variable), and so + // optimization routines can decide the function isn't used and can + // therefore be optimized away. The preserve flag prevents that. + + // These are the bits shown in the table for gg_build_fn_decl() + TREE_ADDRESSABLE(function_decl) = 1; + TREE_USED(function_decl) = 1; + TREE_NOTHROW(function_decl) = 1; + TREE_STATIC(function_decl) = 1; + DECL_EXTERNAL (function_decl) = 0; + TREE_PUBLIC (function_decl) = 0; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1; DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl; - - // We need to make it public, because otherwise COBOL CALL "func" - // won't be able to find it, because dlopen/dlsym won't find it. - TREE_PUBLIC(function_decl) = 0; - - // Append this function to the list of functions and variables - // associated with the computation module. + DECL_CONTEXT(function_decl) = gg_trans_unit.trans_unit_decl; + DECL_PRESERVE_P (function_decl) = 1; gg_append_var_decl(function_decl); } @@ -2736,7 +2671,7 @@ gg_define_function(tree return_type, const char *funcname, ...) } // Establish the RESULT_DECL for the function: - tree resdecl = build_decl (location_from_lineno(), RESULT_DECL, NULL_TREE, return_type); + tree resdecl = build_decl (gg_token_location(), RESULT_DECL, NULL_TREE, return_type); DECL_CONTEXT (resdecl) = function_decl; DECL_RESULT (function_decl) = resdecl; @@ -2749,6 +2684,9 @@ gg_define_function(tree return_type, const char *funcname, ...) struct gg_function_t new_function = {}; new_function.context_count = 0; new_function.function_decl = function_decl; + new_function.our_name = IDENTIFIER_POINTER(DECL_NAME(function_decl)); + new_function.our_unmangled_name = xstrdup(unmangled_name); + new_function.function_address = gg_get_address_of(function_decl); // Each program on the stack gets a unique identifier. This is used, for // example, to make sure that static variables have unique names. @@ -2760,6 +2698,19 @@ gg_define_function(tree return_type, const char *funcname, ...) // All we need is a context, and we are ready to go: gg_push_context(); + return function_decl; + } + +void +gg_modify_function_type(tree function_decl, tree return_type) + { + tree fndecl_type = build_varargs_function_type_array( return_type, + 0, // No parameters yet + NULL); // And, hence, no types + TREE_TYPE(function_decl) = fndecl_type; + tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type); + DECL_CONTEXT (resdecl) = function_decl; + DECL_RESULT (function_decl) = resdecl; } tree @@ -2791,11 +2742,8 @@ gg_get_function_decl(tree return_type, const char *funcname, ...) { // Warning: This test is not completely reliable, because a garbage // byte could have a valid TREE_CODE. But it does help. - yywarn("You nitwit!"); - yywarn("You forgot to put a NULL_TREE at the end of a " - "gg_define_function() again!"); - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - gcc_unreachable(); + cbl_internal_error("You forgot to put a %<NULL_TREE%> at the end of a " + "%<gg_define_function()%> again"); } const char *name = va_arg(params, const char *); @@ -2805,10 +2753,8 @@ gg_get_function_decl(tree return_type, const char *funcname, ...) nparams += 1; if(nparams > ARG_LIMIT) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### %d parameters? Really? Are you insane?", + cbl_internal_error("%d parameters? Really? Are you insane?", ARG_LIMIT+1); - gcc_unreachable(); } } va_end(params); @@ -2855,7 +2801,7 @@ gg_get_function_decl(tree return_type, const char *funcname, ...) } // Establish the RESULT_DECL for the function: - tree resdecl = build_decl (location_from_lineno(), RESULT_DECL, NULL_TREE, return_type); + tree resdecl = build_decl (gg_token_location(), RESULT_DECL, NULL_TREE, return_type); DECL_CONTEXT (resdecl) = function_decl; DECL_RESULT (function_decl) = resdecl; @@ -2881,63 +2827,73 @@ gg_finalize_function() // Finish off the context gg_pop_context(); - if( gg_trans_unit.function_stack.back().is_truly_nested ) - { - // This code is for true nested functions. - - ///////// DANGER, WILL ROBINSON! - ///////// This is all well and good. It does not, however, work. - ///////// I tried to implement it because I had a Brilliant Idea for - ///////// building COBOL paragraphs in a way that would easily allow - ///////// the GDB "NEXT" command to step over a PERFORM <paragraph>. - ///////// But, alas, I realized that it was just not going to work. - ///////// - ///////// Pity. - ///////// - ///////// But at that point, I was here, and I am leaving this uncooked - ///////// code in case I someday want to return to it. If it becomes - ///////// your job, rather than mine, I encourage you to write a C - ///////// program that uses the GNU extensions that allow true nested - ///////// functions, and reverse engineer the "finish_function" - ///////// function, and get it working. - ///////// - ///////// Good luck. Bob Dubner, 2022-08-13 - - // Because this is a nested function, let's make sure that it actually - // has a function that it is nested within - gcc_assert(gg_trans_unit.function_stack.size() > 1 ); - - /* Genericize before inlining. Delay genericizing nested functions - until their parent function is genericized. Since finalizing - requires GENERIC, delay that as well. */ - - // This is the comment in gcc/c/c-decl.c: - - /* Register this function with cgraph just far enough to get it - added to our parent's nested function list. Handy, since the - C front end doesn't have such a list. */ - - static cgraph_node *node = cgraph_node::get_create (current_function->function_decl); - gcc_assert(node); - - } - else - { - // This makes the function visible on the source code module level. - cgraph_node::finalize_function (current_function->function_decl, true); - } + /* Because COBOL functions can be misleadingly referenced before they + defined, and because our compiler is single pass, we need to defer + actually passing the function_decls to the middle end until we are + done with the entire compilation unit. + + An actual example: + + IDENTIFICATION DIVISION. + PROGRAM-ID. A. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 CWD PIC X(100). + 01 LEN_OF_CWD PIC 999 VALUE 100. + PROCEDURE DIVISION. + CALL "getcwd" USING BY REFERENCE CWD BY VALUE LEN_OF_CWD + DISPLAY CWD + goback. + END PROGRAM A. + IDENTIFICATION DIVISION. + PROGRAM-ID. B. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 CWD PIC X(100). + 01 RETURNED-CWD PIC X(100). + 01 LEN_OF_CWD PIC 999 VALUE 100. + PROCEDURE DIVISION. + CALL "getcwd" USING BY REFERENCE CWD BY VALUE LEN_OF_CWD RETURNING RETURNED-CWD + DISPLAY RETURNED-CWD + goback. + END PROGRAM B. + + When we encounter the first call to getcwd, we have no clue as to the + type of the return value, so we assume it is COBOL_FUNCTION_RETURN_TYPE + + When we encounter the second call, we learn that it returns CHAR_P. But + an attempt to change the return type of the function_decl will result + in problems if the function_decl of A is processed by the middle end + before we get a chance to change the getcwd functiona_decl. + + Hence the need for finalized_function_decls, which gets processed + at the end of the file. */ + + finalized_function_decls.push_back(current_function->function_decl); dump_function (TDI_original, current_function->function_decl); if( gg_trans_unit.function_stack.back().context_count ) { - cbl_internal_error("Residual context count!"); + cbl_internal_error("Residual context count"); } gg_trans_unit.function_stack.pop_back(); } void +gg_leaving_the_source_code_file() + { + for( std::vector<tree>::const_iterator it=finalized_function_decls.begin(); + it != finalized_function_decls.end(); + it++ ) + { + //This makes the function visible on the source code module level. + cgraph_node::finalize_function(*it, true); + } + } + +void gg_push_context() { // Sit back, relax, prepare to be amazed. @@ -3076,14 +3032,12 @@ gg_call_expr(tree return_type, const char *function_name, ...) { if(nargs >= ARG_LIMIT) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### You *must* be joking!"); - gcc_unreachable(); + cbl_internal_error("You *must* be joking"); } tree arg = va_arg(ap, tree); - if( !arg ) + if( arg == NULL_TREE ) { break; } @@ -3104,7 +3058,7 @@ gg_call_expr(tree return_type, const char *function_name, ...) tree the_func_addr = build1(ADDR_EXPR, build_pointer_type (TREE_TYPE(function_decl)), function_decl); - tree the_call = build_call_array_loc(location_from_lineno(), + tree the_call = build_call_array_loc(gg_token_location(), return_type, the_func_addr, nargs, @@ -3133,14 +3087,12 @@ gg_call(tree return_type, const char *function_name, ...) { if(nargs >= ARG_LIMIT) { - yywarn("###### %10s in %s:%d", __func__, __FILE__,__LINE__ ); - yywarn("###### You *must* be joking!"); - gcc_unreachable(); + cbl_internal_error("You *must* be joking"); } tree arg = va_arg(ap, tree); - if( !arg ) + if( arg == NULL_TREE ) { break; } @@ -3161,7 +3113,7 @@ gg_call(tree return_type, const char *function_name, ...) tree the_func_addr = build1(ADDR_EXPR, build_pointer_type (TREE_TYPE(function_decl)), function_decl); - tree the_call = build_call_array_loc(location_from_lineno(), + tree the_call = build_call_array_loc(gg_token_location(), return_type, the_func_addr, nargs, @@ -3171,7 +3123,7 @@ gg_call(tree return_type, const char *function_name, ...) } tree -gg_call_expr_list(tree return_type, tree function_name, int param_count, tree args[]) +gg_call_expr_list(tree return_type, tree function_pointer, int param_count, tree args[]) { // Generalized caller. param_count is the count of params in the arg[]] @@ -3186,9 +3138,9 @@ gg_call_expr_list(tree return_type, tree function_name, int param_count, tree ar // Avoid that with something like // gg_assign( dest, gg_call_expr_list(...) ); - tree the_call = build_call_array_loc(location_from_lineno(), + tree the_call = build_call_array_loc(gg_token_location(), return_type, - function_name, + function_pointer, param_count, args); // This routine returns the call_expr; the caller will have to deal with it @@ -3221,7 +3173,7 @@ void gg_exit(tree exit_code) { tree the_call = - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_EXIT), 1, exit_code); @@ -3232,7 +3184,7 @@ void gg_abort() { tree the_call = - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_ABORT), 0); gg_append_statement(the_call); @@ -3243,7 +3195,7 @@ gg_strlen(tree psz) { tree the_call = fold_convert( size_type_node, - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_STRLEN), 1, psz)); @@ -3255,7 +3207,7 @@ gg_strdup(tree psz) { tree the_call = fold_convert( build_pointer_type(char_type_node), - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_STRDUP), 1, psz)); @@ -3269,7 +3221,7 @@ gg_malloc(tree size) { tree the_call = fold_convert( pvoid_type_node, - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_MALLOC), 1, size)); @@ -3281,7 +3233,7 @@ gg_realloc(tree base, tree size) { tree the_call = fold_convert( pvoid_type_node, - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_REALLOC), 2, base, @@ -3305,7 +3257,7 @@ void gg_free(tree pointer) { tree the_call = - build_call_expr_loc(location_from_lineno(), + build_call_expr_loc(gg_token_location(), builtin_decl_explicit (BUILT_IN_FREE), 1, pointer); @@ -3348,7 +3300,8 @@ tree gg_array_of_size_t( size_t N, size_t *values) { tree retval = gg_define_variable(build_pointer_type(SIZE_T)); - gg_assign(retval, gg_cast(build_pointer_type(SIZE_T), gg_malloc( build_int_cst_type(SIZE_T, N * sizeof(size_t))))); + tree sz = build_int_cst_type(SIZE_T, N * int_size_in_bytes(SIZE_T)); + gg_assign(retval, gg_cast(build_pointer_type(SIZE_T), gg_malloc(sz))); for(size_t i=0; i<N; i++) { gg_assign(gg_array_value(retval, i), build_int_cst_type(SIZE_T, values[i])); @@ -3359,8 +3312,8 @@ gg_array_of_size_t( size_t N, size_t *values) tree gg_array_of_bytes( size_t N, unsigned char *values) { - tree retval = gg_define_variable(build_pointer_type(UCHAR)); - gg_assign(retval, gg_cast(build_pointer_type(UCHAR), gg_malloc( build_int_cst_type(UCHAR, N * sizeof(unsigned char))))); + tree retval = gg_define_variable(UCHAR_P); + gg_assign(retval, gg_cast(UCHAR_P, gg_malloc( build_int_cst_type(SIZE_T, N)))); for(size_t i=0; i<N; i++) { gg_assign(gg_array_value(retval, i), build_int_cst_type(UCHAR, values[i])); @@ -3405,18 +3358,6 @@ gg_string_literal(const char *string) return build_string_literal(strlen(string)+1, string); } -void -gg_set_current_line_number(int line_number) - { - sv_current_line_number = line_number; - } - -int -gg_get_current_line_number() - { - return sv_current_line_number; - } - tree gg_trans_unit_var_decl(const char *var_name) { @@ -3429,8 +3370,31 @@ gg_trans_unit_var_decl(const char *var_name) return NULL_TREE; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format" + +void +gg_insert_into_assembler(const char ach[]) + { + if( !optimize ) + { + // Create the required generic tag + tree asm_expr = build5_loc( gg_token_location(), + ASM_EXPR, + VOID, + build_string(strlen(ach), ach), + NULL_TREE, + NULL_TREE, + NULL_TREE, + NULL_TREE); + + // And insert it as a statement + gg_append_statement(asm_expr); + } + } + void -gg_insert_into_assembler(const char *format, ...) +gg_insert_into_assemblerf(const char *format, ...) { // Temporarily defeat all ASM_EXPR for optimized code per PR119214 // The correct solution using LABEL_DECL is forthcoming @@ -3449,18 +3413,31 @@ gg_insert_into_assembler(const char *format, ...) vsnprintf(ach, sizeof(ach), format, ap); va_end(ap); - // Create the required generic tag - tree asm_expr = build5_loc( location_from_lineno(), - ASM_EXPR, - VOID, - build_string(strlen(ach), ach), - NULL_TREE, - NULL_TREE, - NULL_TREE, - NULL_TREE); - //SET_EXPR_LOCATION (asm_expr, UNKNOWN_LOCATION); + gg_insert_into_assembler(ach); + } + } +#pragma GCC diagnostic pop - // And insert it as a statement - gg_append_statement(asm_expr); +static location_t sv_token_location_override = 0; + +void +token_location_override(location_t loc) + { + sv_token_location_override = loc; + } + +location_t +gg_token_location() + { + location_t retval; + if( sv_token_location_override ) + { + retval = sv_token_location_override; + sv_token_location_override = 0; } + else + { + retval = current_token_location(); + } + return retval; } diff --git a/gcc/cobol/gengen.h b/gcc/cobol/gengen.h index 8c1bc8d..e785ac7 100644 --- a/gcc/cobol/gengen.h +++ b/gcc/cobol/gengen.h @@ -206,11 +206,6 @@ struct gg_function_t // logical way: All programs are siblings, with the context being the source // code module. The nested aspect is not reflected in the GENERIC tree. - // Truly nested functions are implemented within the generic tree; the - // nested function is completely inside the outer function. This was - // implemented to support paragraphs as callable entities. - bool is_truly_nested; - // This variable, which appears on the stack, contains the exit_address // for the terminating proc of a PERFORM A or PERFORM A THROUGH B tree perform_exit_address; @@ -300,7 +295,7 @@ extern tree gg_trunc(tree integer_type, tree float_var); extern tree gg_cast(tree type, tree var); // Assignment, that is to say, A = B -extern void gg_assign(tree dest, const tree source); +extern tree gg_assign(tree dest, const tree source); // struct creation and field access // Create struct, and access a field in a struct @@ -456,13 +451,16 @@ extern tree gg_strncmp(tree char_star_A, tree char_star_B, tree size_t_N); extern void gg_return(tree operand = NULL_TREE); // These routines are the preample and postamble that bracket everything else -extern void gg_define_function(tree return_type, const char *funcname, ...); -extern tree gg_define_function_with_no_parameters(tree return_type, - const char *funcname, - const char *unmangled_name); +extern tree gg_build_fn_decl(const char *funcname, tree fndecl_type); +extern tree gg_peek_fn_decl(const char *funcname); +extern tree gg_define_function( tree return_type, + const char *funcname, + const char *unmangled_name, + ...); extern void chain_parameter_to_function( tree function_decl, const tree param_type, const char *name); +extern void gg_modify_function_type(tree function_decl, tree return_type); extern void gg_finalize_function(); extern void gg_push_context(); @@ -471,7 +469,9 @@ extern void gg_pop_context(); // These are a generalized call constructor. The first for when you just want // the function called, because you don't care about the return value. The others // are for when you do need the return value. -extern tree gg_call_expr_list(tree return_type, tree function_name, int param_count, tree[]); +extern tree gg_call_expr_list(tree return_type, + tree function_pointer, + int param_count, tree[]); // The following is a garden-variety call, with known return type and known // but in the case where the return value is unimportant. @@ -495,7 +495,6 @@ void gg_create_goto_pair( tree *goto_expr, tree *label_expr, tree *label_addr, tree *label_decl); -void gg_goto_label_decl(tree label_decl); // Used for implementing SECTIONS and PARAGRAPHS. When you have a // void *pointer = &&label, gg_goto is the same as @@ -505,9 +504,6 @@ void gg_goto(tree pointer); void gg_record_statement_list_start(); tree gg_record_statement_list_finish(); -// These routines are in support of PERFORM PARAGRAPH -extern tree gg_get_function_decl(tree return_type, const char *funcname, ...); - // Used to call system exit() extern void gg_exit(tree exit_code); extern void gg_abort(); @@ -528,17 +524,21 @@ extern tree gg_indirect(tree pointer, tree byte_offset = NULL_TREE); extern tree gg_string_literal(const char *string); #define CURRENT_LINE_NUMBER (cobol_location().first_line) -location_t location_from_lineno(); - -// When set to true, use UNKNOWN_LOCATION instead of CURRENT_LINE_NUMBER -extern void gg_set_current_line_number(int line_number); -extern int gg_get_current_line_number(); +extern location_t gg_token_location(); +extern location_t current_token_location(); +extern location_t current_location_minus_one(); +extern void current_location_minus_one_clear(); +extern void token_location_override(location_t loc); extern tree gg_trans_unit_var_decl(const char *var_name); -tree gg_open(tree char_star_A, tree int_B); -tree gg_close(tree int_A); -tree gg_get_indirect_reference(tree pointer, tree offset); -void gg_insert_into_assembler(const char *format, ...); -void gg_modify_function_type(tree function_decl, tree return_type); +extern tree gg_open(tree char_star_A, tree int_B); +extern tree gg_close(tree int_A); +extern tree gg_get_indirect_reference(tree pointer, tree offset); + +extern void gg_insert_into_assembler(const char ach[]); +extern void gg_insert_into_assemblerf(const char *format, ...) ATTRIBUTE_PRINTF_1; + +extern char *gg_show_type(tree type); +extern void gg_leaving_the_source_code_file(); #endif diff --git a/gcc/cobol/genmath.cc b/gcc/cobol/genmath.cc index 42f01cd..7d6ae8c 100644 --- a/gcc/cobol/genmath.cc +++ b/gcc/cobol/genmath.cc @@ -30,7 +30,6 @@ #include "cobol-system.h" #include "coretypes.h" #include "tree.h" -#define HOWEVER_GCC_DEFINES_TREE 1 #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -43,7 +42,7 @@ #include "gengen.h" #include "structs.h" #include "../../libgcobol/gcobolio.h" -#include "../../libgcobol/libgcobol.h" +#include "../../libgcobol/charmaps.h" #include "show_parse.h" void @@ -54,7 +53,8 @@ set_up_on_exception_label(cbl_label_t *arithmetic_label) if( !arithmetic_label->structs.arith_error ) { arithmetic_label->structs.arith_error - = (cbl_arith_error_t *)xmalloc(sizeof(struct cbl_arith_error_t) ); + = static_cast<cbl_arith_error_t *> + (xmalloc(sizeof(struct cbl_arith_error_t))); // Set up the address pairs for this clause gg_create_goto_pair(&arithmetic_label->structs.arith_error->over.go_to, &arithmetic_label->structs.arith_error->over.label); @@ -74,8 +74,8 @@ set_up_compute_error_label(cbl_label_t *compute_label) if( !compute_label->structs.compute_error ) { compute_label->structs.compute_error - = (cbl_compute_error_t *) - xmalloc(sizeof(struct cbl_compute_error_t) ); + = static_cast<cbl_compute_error_t *> + (xmalloc(sizeof(struct cbl_compute_error_t))); compute_label->structs.compute_error->compute_error_code = gg_define_int(0); } @@ -97,8 +97,8 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, size_t nA, cbl_refer_t *A, size_t nB, cbl_refer_t *B, cbl_arith_format_t format, - cbl_label_t *error, - cbl_label_t *not_error, + const cbl_label_t *error, + const cbl_label_t *not_error, tree compute_error, // Pointer to int const char *operation, cbl_refer_t *remainder = NULL) @@ -114,7 +114,6 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, { TRACE1_HEADER TRACE1_TEXT_ABC("calling ", operation, "") - TRACE1_END for(size_t ii=0; ii<nA; ii++) { TRACE1_INDENT @@ -131,7 +130,6 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, build_int_cst_type(SIZE_T, ii)); TRACE1_REFER("", B[ii], ""); } - TRACE1_END } // We need to split up cbl_num_result_t into two arrays, one for the refer_t @@ -139,7 +137,7 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, // Allocate nC+1 in case this is a divide with a REMAINDER - cbl_refer_t *results = (cbl_refer_t *)xmalloc((nC+1) * sizeof( cbl_refer_t )); + std::vector<cbl_refer_t> results(nC + 1); int ncount = 0; if( nC+1 <= MIN_FIELD_BLOCK_SIZE ) @@ -181,6 +179,7 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, temp_field.data.rdigits = remainder->field->data.rdigits ; temp_field.data.initial = remainder->field->data.initial ; temp_field.data.picture = remainder->field->data.picture ; + temp_field.codeset = remainder->field->codeset ; parser_symbol_add(&temp_field); temp_remainder.field = &temp_field; @@ -209,7 +208,7 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, build_array_of_treeplets(1, nA, A); build_array_of_treeplets(2, nB, B); - build_array_of_treeplets(3, ncount, results); + build_array_of_treeplets(3, ncount, results.data()); gg_call(VOID, operation, @@ -225,7 +224,6 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, { for(size_t ii=0; ii<nC; ii++) { - break; // Breaks on ADD 1 SUB2 GIVING SUB4 both PIC S9(3) COMP TRACE1_INDENT gg_fprintf( trace_handle, 1, "result: C[%ld]: ", @@ -255,9 +253,6 @@ arithmetic_operation(size_t nC, cbl_num_result_t *C, { SHOW_PARSE_END } - - // We need to release all of the refers we allocated: - free(results); } static void @@ -309,7 +304,7 @@ arithmetic_error_handler( cbl_label_t *error, } static bool -is_somebody_float(size_t nA, cbl_refer_t *A) +is_somebody_float(size_t nA, const cbl_refer_t *A) { bool retval = false; for(size_t i=0; i<nA; i++) @@ -324,7 +319,7 @@ is_somebody_float(size_t nA, cbl_refer_t *A) } static bool -is_somebody_float(size_t nC, cbl_num_result_t *C) +is_somebody_float(size_t nC, const cbl_num_result_t *C) { bool retval = false; for(size_t i=0; i<nC; i++) @@ -339,7 +334,7 @@ is_somebody_float(size_t nC, cbl_num_result_t *C) } static bool -all_results_binary(size_t nC, cbl_num_result_t *C) +all_results_binary(size_t nC, const cbl_num_result_t *C) { bool retval = true; @@ -401,7 +396,6 @@ fast_add( size_t nC, cbl_num_result_t *C, { Analyze(); // All targets are non-PICTURE binaries: - //gg_insert_into_assembler("# DUBNER addition START"); tree term_type = largest_binary_term(nA, A); if( term_type ) { @@ -415,7 +409,7 @@ fast_add( size_t nC, cbl_num_result_t *C, get_binary_value( sum, NULL, A[0].field, - refer_offset_source(A[0])); + refer_offset(A[0])); // Add in the rest of them: for(size_t i=1; i<nA; i++) @@ -423,7 +417,7 @@ fast_add( size_t nC, cbl_num_result_t *C, get_binary_value( addend, NULL, A[i].field, - refer_offset_source(A[i])); + refer_offset(A[i])); gg_assign(sum, gg_add(sum, addend)); } //gg_printf("The intermediate sum is %ld\n", gg_cast(LONG, sum), NULL_TREE); @@ -433,7 +427,7 @@ fast_add( size_t nC, cbl_num_result_t *C, { tree dest_type = tree_type_from_size(C[i].refer.field->data.capacity, 0); tree dest_addr = gg_add(member(C[i].refer.field->var_decl_node, "data"), - refer_offset_dest(C[i].refer)); + refer_offset(C[i].refer)); tree ptr = gg_cast(build_pointer_type(dest_type), dest_addr); if( format == giving_e ) { @@ -497,12 +491,12 @@ fast_subtract(size_t nC, cbl_num_result_t *C, tree sum = gg_define_variable(term_type); tree addend = gg_define_variable(term_type); - get_binary_value(sum, NULL, A[0].field, refer_offset_dest(A[0])); + get_binary_value(sum, NULL, A[0].field, refer_offset(A[0])); // Add in the rest of them: for(size_t i=1; i<nA; i++) { - get_binary_value(sum, NULL, A[i].field, refer_offset_dest(A[i])); + get_binary_value(sum, NULL, A[i].field, refer_offset(A[i])); gg_assign(sum, gg_add(sum, addend)); } //gg_printf("The intermediate sum is %ld\n", gg_cast(LONG, sum), NULL_TREE); @@ -510,7 +504,7 @@ fast_subtract(size_t nC, cbl_num_result_t *C, if( format == giving_e ) { // We now subtract the sum from B[0] - get_binary_value(addend, NULL, B[0].field, refer_offset_dest(B[0])); + get_binary_value(addend, NULL, B[0].field, refer_offset(B[0])); gg_assign(sum, gg_subtract(addend, sum)); } @@ -519,7 +513,7 @@ fast_subtract(size_t nC, cbl_num_result_t *C, { tree dest_type = tree_type_from_size(C[i].refer.field->data.capacity, 0); tree dest_addr = gg_add(member(C[i].refer.field->var_decl_node, "data"), - refer_offset_dest(C[i].refer)); + refer_offset(C[i].refer)); tree ptr = gg_cast(build_pointer_type(dest_type), dest_addr); if( format == giving_e ) { @@ -577,16 +571,12 @@ fast_multiply(size_t nC, cbl_num_result_t *C, tree valA = gg_define_variable(term_type); tree valB = gg_define_variable(term_type); - get_binary_value(valA, NULL, A[0].field, refer_offset_dest(A[0])); + get_binary_value(valA, NULL, A[0].field, refer_offset(A[0])); if( nB ) { // This is a MULTIPLY Format 2 - get_binary_value(valB, NULL, B[0].field, refer_offset_dest(B[0])); - } - - if(nB) - { + get_binary_value(valB, NULL, B[0].field, refer_offset(B[0])); gg_assign(valA, gg_multiply(valA, valB)); } @@ -595,7 +585,7 @@ fast_multiply(size_t nC, cbl_num_result_t *C, { tree dest_type = tree_type_from_size(C[i].refer.field->data.capacity, 0); tree dest_addr = gg_add(member(C[i].refer.field->var_decl_node, "data"), - refer_offset_dest(C[i].refer)); + refer_offset(C[i].refer)); tree ptr = gg_cast(build_pointer_type(dest_type), dest_addr); if( nB ) { @@ -621,7 +611,7 @@ static bool fast_divide(size_t nC, cbl_num_result_t *C, size_t nA, cbl_refer_t *A, size_t nB, cbl_refer_t *B, - cbl_refer_t remainder) + const cbl_refer_t &remainder) { bool retval = false; if( all_results_binary(nC, C) ) @@ -655,13 +645,13 @@ fast_divide(size_t nC, cbl_num_result_t *C, tree divisor = gg_define_variable(term_type); tree dividend = gg_define_variable(term_type); tree quotient = NULL_TREE; - get_binary_value(divisor, NULL, A[0].field, refer_offset_dest(A[0])); + get_binary_value(divisor, NULL, A[0].field, refer_offset(A[0])); if( nB ) { // This is a MULTIPLY Format 2, where we are dividing A into B and // assigning that to C - get_binary_value(dividend, NULL, B[0].field, refer_offset_dest(B[0])); + get_binary_value(dividend, NULL, B[0].field, refer_offset(B[0])); quotient = gg_define_variable(term_type); // Yes, in this case the divisor and dividend are switched. Things are @@ -672,9 +662,11 @@ fast_divide(size_t nC, cbl_num_result_t *C, // We now either divide into C[n] or assign dividend/divisor to C[n]: for(size_t i=0; i<nC; i++ ) { - tree dest_type = tree_type_from_size(C[i].refer.field->data.capacity, 0); - tree dest_addr = gg_add(member(C[i].refer.field->var_decl_node, "data"), - refer_offset_dest(C[i].refer)); + tree dest_type = + tree_type_from_size(C[i].refer.field->data.capacity, 0); + tree dest_addr = gg_add(member( C[i].refer.field->var_decl_node, + "data"), + refer_offset(C[i].refer)); tree ptr = gg_cast(build_pointer_type(dest_type), dest_addr); if( nB ) { @@ -689,16 +681,15 @@ fast_divide(size_t nC, cbl_num_result_t *C, } // This is where we handle any remainder, keeping in mind that for - // nB != 0, the actual dividend is in the value we have named "divisor". - // - // And, yes, I hate comments like that, too. + // nB != 0, the actual dividend is in the value we have named + // "divisor". // We calculate the remainder by calculating // dividend minus quotient * divisor if( remainder.field ) { - tree dest_addr = gg_add(member(remainder.field->var_decl_node, "data"), - refer_offset_dest(remainder)); + dest_addr = gg_add( member(remainder.field->var_decl_node, "data"), + refer_offset(remainder)); dest_type = tree_type_from_size(remainder.field->data.capacity, 0); ptr = gg_cast(build_pointer_type(dest_type), dest_addr); @@ -727,7 +718,7 @@ parser_add( size_t nC, cbl_num_result_t *C, SHOW_PARSE { SHOW_PARSE_HEADER - fprintf(stderr, " A[%ld]:", nA); + fprintf(stderr, " A[" HOST_SIZE_T_PRINT_DEC "]:", (fmt_size_t)nA); for(size_t i=0; i<nA; i++) { if(i > 0) @@ -739,7 +730,7 @@ parser_add( size_t nC, cbl_num_result_t *C, fprintf(stderr, "%s", format==giving_e? " GIVING" : ""); - fprintf(stderr, " C[%ld]:", nC); + fprintf(stderr, " C[" HOST_SIZE_T_PRINT_DEC "]:", (fmt_size_t)nC); for(size_t i=0; i<nC; i++) { if(i > 0) @@ -758,12 +749,6 @@ parser_add( size_t nC, cbl_num_result_t *C, TRACE1_END } - tree compute_error = (tree)compute_error_p; - if( compute_error == NULL ) - { - gg_assign(var_decl_default_compute_error, integer_zero_node); - compute_error = gg_get_address_of(var_decl_default_compute_error); - } bool handled = false; if( fast_add( nC, C, @@ -774,6 +759,13 @@ parser_add( size_t nC, cbl_num_result_t *C, } else { + tree compute_error = (tree)compute_error_p; + if( compute_error == NULL ) + { + gg_assign(var_decl_default_compute_error, integer_zero_node); + compute_error = gg_get_address_of(var_decl_default_compute_error); + } + bool computation_is_float = is_somebody_float(nA, A) || is_somebody_float(nC, C); // We now start deciding which arithmetic routine we are going to use: @@ -994,9 +986,9 @@ parser_add( size_t nC, cbl_num_result_t *C, } void -parser_add( cbl_refer_t cref, - cbl_refer_t aref, - cbl_refer_t bref, +parser_add( const cbl_refer_t& cref, + const cbl_refer_t& aref, + const cbl_refer_t& bref, cbl_round_t rounded) { // This is the simple and innocent C = A + B @@ -1216,9 +1208,9 @@ parser_divide( size_t nC, cbl_num_result_t *C, // C = A / B } void -parser_multiply(cbl_refer_t cref, - cbl_refer_t aref, - cbl_refer_t bref, +parser_multiply(const cbl_refer_t& cref, + const cbl_refer_t& aref, + const cbl_refer_t& bref, cbl_round_t rounded ) { cbl_num_result_t C[1]; @@ -1239,11 +1231,11 @@ parser_multiply(cbl_refer_t cref, } void -parser_divide( cbl_refer_t cref, - cbl_refer_t aref, - cbl_refer_t bref, +parser_divide( const cbl_refer_t& cref, + const cbl_refer_t& aref, + const cbl_refer_t& bref, cbl_round_t rounded, - cbl_refer_t remainder_ref ) + const cbl_refer_t& remainder_ref ) { cbl_num_result_t C[1]; C[0].rounded = rounded; @@ -1391,12 +1383,12 @@ parser_op( struct cbl_refer_t cref, break; } default: - cbl_internal_error( "parser_op() doesn't know how to " - "evaluate \"%s = %s %c %s\"\n", - cref.field->name, - aref.field->name, - op, - bref.field->name); + cbl_internal_error( "%<parser_op()%> doesn%'t know how to " + "evaluate %<%s = %s %c %s%>", + cref.field->name, + aref.field->name, + op, + bref.field->name); break; } } @@ -1414,7 +1406,7 @@ parser_subtract(size_t nC, cbl_num_result_t *C, // C = B - A SHOW_PARSE { SHOW_PARSE_HEADER - fprintf(stderr, " A[%ld]:", nA); + fprintf(stderr, " A[" HOST_SIZE_T_PRINT_DEC "]:", (fmt_size_t)nA); for(size_t i=0; i<nA; i++) { if(i > 0) @@ -1424,7 +1416,7 @@ parser_subtract(size_t nC, cbl_num_result_t *C, // C = B - A fprintf(stderr, "%s", A[i].field->name); } - fprintf(stderr, " B[%ld]:", nB); + fprintf(stderr, " B[" HOST_SIZE_T_PRINT_DEC "]:", (fmt_size_t)nB); for(size_t i=0; i<nB; i++) { if(i > 0) @@ -1434,7 +1426,7 @@ parser_subtract(size_t nC, cbl_num_result_t *C, // C = B - A fprintf(stderr, "%s", B[i].field->name); } - fprintf(stderr, " C[%ld]:", nC); + fprintf(stderr, " C[" HOST_SIZE_T_PRINT_DEC "]:", (fmt_size_t)nC); for(size_t i=0; i<nC; i++) { if(i > 0) @@ -1454,13 +1446,6 @@ parser_subtract(size_t nC, cbl_num_result_t *C, // C = B - A bool handled = false; - tree compute_error = (tree)compute_error_p; - if( compute_error == NULL ) - { - gg_assign(var_decl_default_compute_error, integer_zero_node); - compute_error = gg_get_address_of(var_decl_default_compute_error); - } - if( fast_subtract(nC, C, nA, A, nB, B, @@ -1470,6 +1455,12 @@ parser_subtract(size_t nC, cbl_num_result_t *C, // C = B - A } else { + tree compute_error = (tree)compute_error_p; + if( compute_error == NULL ) + { + gg_assign(var_decl_default_compute_error, integer_zero_node); + compute_error = gg_get_address_of(var_decl_default_compute_error); + } bool computation_is_float = is_somebody_float(nA, A) || is_somebody_float(nC, C); @@ -1706,9 +1697,9 @@ parser_subtract(size_t nC, cbl_num_result_t *C, // C = B - A } void -parser_subtract(cbl_refer_t cref, // cref = aref - bref - cbl_refer_t aref, - cbl_refer_t bref, +parser_subtract(const cbl_refer_t& cref, // cref = aref - bref + const cbl_refer_t& aref, + const cbl_refer_t& bref, cbl_round_t rounded ) { cbl_num_result_t C[1]; diff --git a/gcc/cobol/genutil.cc b/gcc/cobol/genutil.cc index c0e6631..63f37f6 100644 --- a/gcc/cobol/genutil.cc +++ b/gcc/cobol/genutil.cc @@ -27,10 +27,12 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +// cppcheck-suppress-file duplicateBreak + #include "cobol-system.h" #include "coretypes.h" #include "tree.h" -#define HOWEVER_GCC_DEFINES_TREE 1 #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -43,26 +45,19 @@ #include "genutil.h" #include "structs.h" #include "../../libgcobol/gcobolio.h" -#include "../../libgcobol/libgcobol.h" #include "../../libgcobol/charmaps.h" #include "show_parse.h" #include "../../libgcobol/exceptl.h" #include "exceptg.h" -bool internal_codeset_is_ebcdic() { return gcobol_feature_internal_ebcdic(); } - bool exception_location_active = true; bool skip_exception_processing = true; bool suppress_dest_depends = false; -#define SET_EXCEPTION_CODE(a) do{set_exception_code((a));}while(0); - std::vector<std::string>current_filename; tree var_decl_exception_code; // int __gg__exception_code; -tree var_decl_exception_handled; // int __gg__exception_handled; -tree var_decl_exception_file_number; // int __gg__exception_file_number; tree var_decl_exception_file_status; // int __gg__exception_file_status; tree var_decl_exception_file_name; // const char *__gg__exception_file_name; tree var_decl_exception_statement; // const char *__gg__exception_statement; @@ -74,7 +69,6 @@ tree var_decl_exception_paragraph; // const char *__gg__exception_paragraph; tree var_decl_default_compute_error; // int __gg__default_compute_error; tree var_decl_rdigits; // int __gg__rdigits; -tree var_decl_odo_violation; // int __gg__odo_violation; tree var_decl_unique_prog_id; // size_t __gg__unique_prog_id; tree var_decl_entry_location; // This is for managing ENTRY statements @@ -111,8 +105,25 @@ tree var_decl_treeplet_4s; // SIZE_T_P , "__gg__treeplet_4s" // wasn't successful figuring out how to create an actual NOP assembly language // instruction, I instead gg_assign(var_decl_nop, integer_zero_node) tree var_decl_nop; // int __gg__nop; + +// Indicates which routine main() called tree var_decl_main_called; // int __gg__main_called; +// Indicates the target label for an ENTRY statement +tree var_decl_entry_label; // void* __gg__entry_label + +#if 0 +#define REFER(a) +#else +#define REFER(a) do \ + { \ + if( getenv("REFER") ) \ + { \ + fprintf(stderr, "REFER %s %s\n", __func__, a); \ + } \ + }while(0); +#endif + int get_scaled_rdigits(cbl_field_t *field) { @@ -227,11 +238,17 @@ tree_type_from_digits(size_t digits, int signable) } void -get_integer_value(tree value, +get_integer_value(tree value, // We know this is a LONG cbl_field_t *field, tree offset, bool check_for_fractional_digits) { + if( field->type == FldLiteralN && field->data.rdigits==0 ) + { + gg_assign(value, gg_cast(LONG, field->data_decl_node)); + return; + } + Analyze(); // Call this routine when you know the result has to be an integer with no // rdigits. This routine became necessary the first time I saw an @@ -268,166 +285,159 @@ get_integer_value(tree value, gg_assign(value, gg_cast(TREE_TYPE(value), temp)); } -static tree -get_data_offset_dest(cbl_refer_t &refer, - int *pflags = NULL) +static +tree // This is a SIZE_T +get_any_capacity(cbl_field_t *field) { - Analyze(); - // This routine returns a tree which is the size_t offset to the data in the - // refer/field + if( field->attr & (any_length_e | intermediate_e) ) + { + return member(field->var_decl_node, "capacity"); + } + else + { + return build_int_cst_type(SIZE_T, field->data.capacity); + } + } - // Because this is for destination/receiving variables, OCCURS DEPENDING ON - // is not checked. +/* This routine, used by both get_data_offset and refer_refmod_length, + fetches the refmod_from and refmod_length. If ec-bound-ref-mod checking + is enabled, it does those checks and sets the exception condition when they + are violated. - tree retval = gg_define_variable(SIZE_T); - gg_assign(retval, size_t_zero_node); + The return value for refstart is the actual offset, that is val(7:3) returns + the value 7-1, that is, 6. + */ +static +void +get_and_check_refstart_and_reflen( tree refstart,// LONG returned value + tree reflen, // LONG returned value + const cbl_refer_t &refer) + { + const cbl_enabled_exceptions_t& + enabled_exceptions( cdf_enabled_exceptions() ); - // We have a refer. - // At the very least, we have an constant offset - int all_flags = 0; - int all_flag_bit = 1; + if( !enabled_exceptions.match(ec_bound_ref_mod_e) ) + { + // This is normal operation -- no exception checking. Thus, we won't + // be trying to check for boundaries or integerness. And the programmer + // is accepting the responsibility for bad code: "If you specify + // disaster, disaster is what you get." - static tree value64 = gg_define_variable(LONG, ".._gdod_value64", vs_file_static); + get_integer_value(refstart, + refer.refmod.from->field, + refer_offset(*refer.refmod.from)); + gg_decrement(refstart); - if( refer.nsubscript ) - { - // We have at least one subscript: + if( refer.refmod.len ) + { + // The length was specified, so that's what we return: + get_integer_value(reflen, + refer.refmod.len->field, + refer_offset(*refer.refmod.len)); + } + else + { + // The length was not specified, so we need to return the distance + // between refmod.from and the end of the field: + gg_assign(reflen, gg_subtract( get_any_capacity(refer.field), refstart) ); + } + return; + } - // Figure we have three subscripts, so nsubscript is 3 - // Figure that the subscripts are {5, 4, 3} + // ec_bound_ref_mode_e checking is enabled: - // We expect that starting from refer.field, that three of our ancestors -- - // call them A1, A2, and A3 -- have occurs clauses. + get_integer_value(refstart, + refer.refmod.from->field, + refer_offset(*refer.refmod.from), + CHECK_FOR_FRACTIONAL_DIGITS); - // We need to start with the rightmost subscript, and work our way up through - // our parents. As we find each parent with an OCCURS, we increment qual_data - // by (subscript-1)*An->data.capacity + IF( var_decl_rdigits, + ne_op, + integer_zero_node ) + { + // The value for refstart had non-zero decimal places. This is an + // error condition: + set_exception_code(ec_bound_ref_mod_e); + gg_assign(refstart, gg_cast(LONG, integer_one_node)); + gg_assign(var_decl_rdigits, integer_zero_node); + } + ELSE + ENDIF - // Establish the field_t pointer for walking up through our ancestors: - cbl_field_t *parent = refer.field; + // Make refstart zero-based: + gg_decrement(refstart); - // Note the backwards test, because refer->nsubscript is an unsigned value - for(size_t i=refer.nsubscript-1; i<refer.nsubscript; i-- ) + IF( refstart, lt_op, build_int_cst_type(LONG, 0 ) ) + { + // A negative value for refstart is an error condition: + set_exception_code(ec_bound_ref_mod_e); + gg_assign(refstart, gg_cast(LONG, integer_zero_node)); + // Set reflen to one here, because otherwise it won't be established. + gg_assign(reflen, gg_cast(TREE_TYPE(reflen), integer_one_node)); + } + ELSE + { + IF( refstart, gt_op, gg_cast(TREE_TYPE(refstart), get_any_capacity(refer.field)) ) { - // We need to search upward for an ancestor with occurs_max: - while(parent) - { - if( parent->occurs.ntimes() ) - { - break; - } - parent = parent_of(parent); - } - // we might have an error condition at this point: - if( !parent ) - { - cbl_internal_error("Too many subscripts"); - } - // Pick up the integer value of the subscript: - static tree subscript = gg_define_variable(LONG, "..gdod_subscript", vs_file_static); - - if( process_this_exception(ec_bound_subscript_e) ) + // refstart greater than zero is an error condition: + set_exception_code(ec_bound_ref_mod_e); + gg_assign(refstart, gg_cast(LONG, integer_zero_node)); + // Set reflen to one here, because otherwise it won't be established. + gg_assign(reflen, gg_cast(TREE_TYPE(reflen), integer_one_node)); + } + ELSE + { + if( refer.refmod.len ) { - get_integer_value(value64, - refer.subscripts[i].field, - refer_offset_dest(refer.subscripts[i]), + get_integer_value(reflen, + refer.refmod.len->field, + refer_offset(*refer.refmod.len), CHECK_FOR_FRACTIONAL_DIGITS); IF( var_decl_rdigits, ne_op, integer_zero_node ) { - if( enabled_exceptions.match(ec_bound_subscript_e) ) - { - // The subscript isn't an integer - SET_EXCEPTION_CODE(ec_bound_subscript_e); - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), integer_zero_node)); - } - else - { - rt_error("error: a table subscript is not an integer"); - } - } - ELSE - { - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), value64)); - } - ENDIF - } - else - { - get_integer_value(subscript, - refer.subscripts[i].field, - refer_offset_dest(refer.subscripts[i])); - } - - // gg_printf("%s(): We have a subscript of %d from %s\n", - // gg_string_literal(__func__), - // subscript, - // gg_string_literal(refer.subscripts[i].field->name), - // NULL_TREE); - - if( (refer.subscripts[i].field->attr & FIGCONST_MASK) == zero_value_e ) - { - // This refer is a figconst ZERO; we treat it as an ALL ZERO - // This is our internal representation for ALL, as in TABLE(ALL) - - // Set the subscript to 1 - gg_assign(subscript, - build_int_cst_type( TREE_TYPE(subscript), 1)); - // Flag this position as ALL - all_flags |= all_flag_bit; - } - all_flag_bit <<= 1; - - // Subscript is now a one-based integer - // Make it zero-based: - - gg_decrement(subscript); - if( process_this_exception(ec_bound_subscript_e) ) - { - // gg_printf("process_this_exception is true\n", NULL_TREE); - IF( subscript, lt_op, gg_cast(TREE_TYPE(subscript), integer_zero_node) ) - { - // The subscript is too small - SET_EXCEPTION_CODE(ec_bound_subscript_e); - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), integer_zero_node)); + // length is not an integer, which is an error condition + set_exception_code(ec_bound_ref_mod_e); + gg_assign(reflen, gg_cast(LONG, integer_one_node)); + gg_assign(var_decl_rdigits, integer_zero_node); } ELSE { - // gg_printf("parent->occurs.ntimes() is %d\n", build_int_cst_type(INT, parent->occurs.ntimes()), NULL_TREE); - IF( subscript, - ge_op, - build_int_cst_type(TREE_TYPE(subscript), parent->occurs.ntimes()) ) + // The length is an integer, so we can keep going. + IF( reflen, lt_op, gg_cast(LONG, integer_one_node) ) { - // The subscript is too large - if( enabled_exceptions.match(ec_bound_subscript_e) ) - { - SET_EXCEPTION_CODE(ec_bound_subscript_e); - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), integer_zero_node)); - } - else - { - rt_error("error: table subscript is too large"); - } + // length is too small, which is an error condition. + set_exception_code(ec_bound_ref_mod_e); + gg_assign(reflen, gg_cast(LONG, integer_one_node)); } ELSE { - // We have a good subscript: - // Check for an ODO violation: - if( parent->occurs.depending_on ) + IF( gg_add(refstart, reflen), + gt_op, + gg_cast(TREE_TYPE(refstart), get_any_capacity(refer.field)) ) { - cbl_field_t *depending_on = cbl_field_of(symbol_at(parent->occurs.depending_on)); - get_integer_value(value64, depending_on); - IF( subscript, ge_op, value64 ) - { - gg_assign(var_decl_odo_violation, integer_one_node); - } - ELSE - ENDIF + // Start + Length is too large, which yet again is an error + // condition + set_exception_code(ec_bound_ref_mod_e); + + // Our intentions are honorable. But at this point, where + // we notice that start + length is too long, the + // get_data_offset routine has already been run and + // it's too late to actually change the refstart. There are + // theoretical solutions to this -- mainly, + // get_data_offset needs to check the start + len for + // validity. But I am not going to do it now. Think of this + // as the TODO item. + gg_assign(refstart, gg_cast(LONG, integer_zero_node)); + gg_assign(reflen, gg_cast(LONG, integer_one_node)); } - - tree augment = gg_multiply(subscript, build_int_cst_type(INT, parent->data.capacity)); - gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, augment))); + ELSE + { + // There are no problems, so there is no error condition, and + // refstart and reflen are correct. + } + ENDIF } ENDIF } @@ -435,121 +445,111 @@ get_data_offset_dest(cbl_refer_t &refer, } else { - // Assume a good subscript: - // Check for an ODO violation: - if( parent->occurs.depending_on ) - { - cbl_field_t *depending_on = cbl_field_of(symbol_at(parent->occurs.depending_on)); - get_integer_value(value64, depending_on); - IF( subscript, ge_op, value64 ) - { - gg_assign(var_decl_odo_violation, integer_one_node); - } - ELSE - ENDIF - } - tree augment = gg_multiply(subscript, build_int_cst_type(INT, parent->data.capacity)); - gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, augment))); + // There is no refmod length, so we default to the remaining characters + gg_assign(reflen, gg_subtract(get_any_capacity(refer.field), + refstart)); } - parent = parent_of(parent); } + ENDIF } + ENDIF + } - if( refer.refmod.from ) - { - // We have a refmod to deal with - static tree refstart = gg_define_variable(LONG, "..gdos_refstart", vs_file_static); - - if( process_this_exception(ec_bound_ref_mod_e) ) - { - get_integer_value(value64, - refer.refmod.from->field, - refer_offset_source(*refer.refmod.from), - CHECK_FOR_FRACTIONAL_DIGITS); - IF( var_decl_rdigits, - ne_op, - integer_zero_node ) - { - // refmod offset is not an integer, and has to be - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_one_node)); - } - else - { - rt_error("error: a refmod FROM is not an integer"); - } - } - ELSE - gg_assign(refstart, value64); - ENDIF - } - else - { - get_integer_value(value64, - refer.refmod.from->field, - refer_offset_source(*refer.refmod.from) - ); - gg_assign(refstart, value64); - } +void +get_depending_on_value_from_odo(tree retval, cbl_field_t *odo) + { + /* This routine, called only when we know there is an OCCURS DEPENDING ON + clause, returns the current value of the DEPENDING ON variable. When + ec_bound_odo_e is turned on, and there is any kind of ec-bound-odo + error condition, the value returned is occurs.bounds.lower. + + This should ensure that there is no memory violation in the event of a + declarative with a RESUME NEXT STATEMENT, or before the default_condition + processing can do a controlled exit. + */ + const cbl_enabled_exceptions_t& + enabled_exceptions( cdf_enabled_exceptions() ); + cbl_field_t *depending_on; + depending_on = cbl_field_of(symbol_at(odo->occurs.depending_on)); - // Make refstart zero-based: - gg_decrement(refstart); + if( !enabled_exceptions.match(ec_bound_odo_e) ) + { + // With no exception testing, just pick up the value. If there is a + // the programmer will simply have to live with the consequences. + get_integer_value(retval, + depending_on, + NULL); + return; + } - if( process_this_exception(ec_bound_ref_mod_e) ) - { - IF( refstart, lt_op, gg_cast(LONG, integer_zero_node) ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - } - else - { - rt_error("error: refmod FROM is less than one"); - } - } - ELSE - { - IF( refstart, gt_op, build_int_cst_type(LONG, refer.field->data.capacity) ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - } - else - { - rt_error("error: refmod FROM is too large"); - } - } - ELSE - ENDIF - } - ENDIF - } + // Bounds checking is enabled, so we test the DEPENDING ON value to be + // between the lower and upper OCCURS limits: + get_integer_value(retval, + depending_on, + NULL, + CHECK_FOR_FRACTIONAL_DIGITS); - // We have a good refstart - gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, refstart))); + IF( var_decl_rdigits, ne_op, integer_zero_node ) + { + // This needs to evaluate to an integer + set_exception_code(ec_bound_odo_e); + gg_assign(retval, build_int_cst_type( TREE_TYPE(retval), + odo->occurs.bounds.lower)); + gg_assign(var_decl_rdigits, integer_zero_node); } + ELSE + ENDIF - if( pflags ) + IF( retval, gt_op, build_int_cst_type(TREE_TYPE(retval), + odo->occurs.bounds.upper) ) { - *pflags = all_flags; + set_exception_code(ec_bound_odo_e); + gg_assign(retval, build_int_cst_type( TREE_TYPE(retval), + odo->occurs.bounds.lower)); } + ELSE + { + IF( retval, lt_op, build_int_cst_type(TREE_TYPE(retval), + odo->occurs.bounds.lower) ) + { + set_exception_code(ec_bound_odo_e); + gg_assign(retval, build_int_cst_type( TREE_TYPE(retval), + odo->occurs.bounds.lower)); + } + ELSE + ENDIF + IF( retval, lt_op, gg_cast(TREE_TYPE(retval), integer_zero_node) ) + { + set_exception_code(ec_bound_odo_e); + gg_assign(retval, gg_cast(TREE_TYPE(retval), integer_zero_node)); + } + ELSE + ENDIF + } + ENDIF + } -// gg_printf("*****>>>>> %s(): returning %p\n", -// gg_string_literal(__func__), -// retval, -// NULL_TREE); - return retval; +static +void +get_depending_on_value(tree retval, const cbl_refer_t &refer) + { + /* This routine, called only when we know there is an OCCURS DEPENDING ON + clause, returns the current value of the DEPENDING ON variable. When + ec_bound_odo_e is turned on, and there is any kind of ec-bound-odo + error condition, the value returned is occurs.bounds.lower. + + This should ensure that there is no memory violation in the event of a + declarative with a RESUME NEXT STATEMENT, or before the default_condition + processing can do a controlled exit. + */ + cbl_field_t *odo = symbol_find_odo(refer.field); + get_depending_on_value_from_odo(retval, odo); } -static tree -get_data_offset_source(cbl_refer_t &refer, - int *pflags = NULL) +static +tree +get_data_offset(const cbl_refer_t &refer, + int *pflags = NULL) { Analyze(); // This routine returns a tree which is the size_t offset to the data in the @@ -564,12 +564,10 @@ get_data_offset_source(cbl_refer_t &refer, // We have a refer. // At the very least, we have an constant offset int all_flags = 0; - int all_flag_bit = 1; - static tree value64 = gg_define_variable(LONG, ".._gdos_value64", vs_file_static); - - if( refer.nsubscript ) + if( refer.nsubscript() ) { + REFER("subscript"); // We have at least one subscript: // Figure we have three subscripts, so nsubscript is 3 @@ -585,8 +583,9 @@ get_data_offset_source(cbl_refer_t &refer, // Establish the field_t pointer for walking up through our ancestors: cbl_field_t *parent = refer.field; + int all_flag_bit = 1; // Note the backwards test, because refer->nsubscript is an unsigned value - for(size_t i=refer.nsubscript-1; i<refer.nsubscript; i-- ) + for(size_t i=refer.nsubscript()-1; i<refer.nsubscript(); i-- ) { // We need to search upward for an ancestor with occurs_max: while(parent) @@ -603,49 +602,8 @@ get_data_offset_source(cbl_refer_t &refer, cbl_internal_error("Too many subscripts"); } // Pick up the integer value of the subscript: -// static tree subscript = gg_define_variable(LONG, "..gdos_subscript", vs_file_static); tree subscript = gg_define_variable(LONG); - if( process_this_exception(ec_bound_subscript_e) ) - { - get_integer_value(value64, - refer.subscripts[i].field, - refer_offset_source(refer.subscripts[i]), - CHECK_FOR_FRACTIONAL_DIGITS); - IF( var_decl_rdigits, - ne_op, - integer_zero_node ) - { - if( enabled_exceptions.match(ec_bound_subscript_e) ) - { - // The subscript isn't an integer - SET_EXCEPTION_CODE(ec_bound_subscript_e); - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), integer_zero_node)); - } - else - { - rt_error("error: a table subscript is not an integer"); - } - } - ELSE - { - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), value64)); - } - ENDIF - } - else - { - get_integer_value(subscript, - refer.subscripts[i].field, - refer_offset_source(refer.subscripts[i])); - } - - // gg_printf("%s(): We have a subscript of %d from %s\n", - // gg_string_literal(__func__), - // subscript, - // gg_string_literal(refer.subscripts[i].field->name), - // NULL_TREE); - if( (refer.subscripts[i].field->attr & FIGCONST_MASK) == zero_value_e ) { // This refer is a figconst ZERO; we treat it as an ALL ZERO @@ -657,161 +615,105 @@ get_data_offset_source(cbl_refer_t &refer, // Flag this position as ALL all_flags |= all_flag_bit; } - all_flag_bit <<= 1; - - // Subscript is now a one-based integer - // Make it zero-based: - - gg_decrement(subscript); - if( process_this_exception(ec_bound_subscript_e) ) + else { - // gg_printf("process_this_exception is true\n", NULL_TREE); - IF( subscript, lt_op, gg_cast(TREE_TYPE(subscript), integer_zero_node) ) + const cbl_enabled_exceptions_t& + enabled_exceptions( cdf_enabled_exceptions() ); + if( !enabled_exceptions.match(ec_bound_subscript_e) ) { - // The subscript is too small - SET_EXCEPTION_CODE(ec_bound_subscript_e); - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), integer_zero_node)); + // With no exception testing, just pick up the value + get_integer_value(subscript, + refer.subscripts[i].field, + refer_offset(refer.subscripts[i])); } - ELSE + else { - // gg_printf("parent->occurs.ntimes() is %d\n", build_int_cst_type(INT, parent->occurs.ntimes()), NULL_TREE); - IF( subscript, - ge_op, - build_int_cst_type(TREE_TYPE(subscript), parent->occurs.ntimes()) ) + get_integer_value(subscript, + refer.subscripts[i].field, + refer_offset(refer.subscripts[i]), + CHECK_FOR_FRACTIONAL_DIGITS); + IF( var_decl_rdigits, + ne_op, + integer_zero_node ) { - // The subscript is too large - if( enabled_exceptions.match(ec_bound_subscript_e) ) - { - SET_EXCEPTION_CODE(ec_bound_subscript_e); - gg_assign(subscript, gg_cast(TREE_TYPE(subscript), integer_zero_node)); - } - else - { - rt_error("error: table subscript is too large"); - } + // The subscript isn't an integer + set_exception_code(ec_bound_subscript_e); + gg_assign(subscript, build_int_cst_type(TREE_TYPE(subscript), 1)); + gg_assign(var_decl_rdigits, integer_zero_node); } ELSE { - // We have a good subscript: - // Check for an ODO violation: - if( parent->occurs.depending_on ) + IF( subscript, lt_op, gg_cast(TREE_TYPE(subscript), + integer_one_node) ) + { + // The subscript is too small + set_exception_code(ec_bound_subscript_e); + gg_assign(subscript, build_int_cst_type(TREE_TYPE(subscript), + 1)); + } + ELSE { - cbl_field_t *depending_on = cbl_field_of(symbol_at(parent->occurs.depending_on)); - get_integer_value(value64, depending_on); - IF( subscript, ge_op, value64 ) + IF( subscript, + ge_op, + build_int_cst_type( TREE_TYPE(subscript), + parent->occurs.ntimes()) ) { - gg_assign(var_decl_odo_violation, integer_one_node); + // The subscript is too large + set_exception_code(ec_bound_subscript_e); + gg_assign(subscript, build_int_cst_type(TREE_TYPE(subscript), + 1)); } ELSE + { + } ENDIF } - - tree augment = gg_multiply(subscript, build_int_cst_type(INT, parent->data.capacity)); - gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, augment))); + ENDIF } - ENDIF - } ENDIF + } } - else + + all_flag_bit <<= 1; + + // Although we strictly don't need to look at the ODO value at this + // point, we do want it checked for the purposes of ec-bound-odo + + const cbl_enabled_exceptions_t& + enabled_exceptions( cdf_enabled_exceptions() ); + + if( enabled_exceptions.match(ec_bound_odo_e) ) { - // Assume a good subscript: - // Check for an ODO violation: if( parent->occurs.depending_on ) { - cbl_field_t *depending_on = cbl_field_of(symbol_at(parent->occurs.depending_on)); - get_integer_value(value64, depending_on); - IF( subscript, ge_op, value64 ) - { - gg_assign(var_decl_odo_violation, integer_one_node); - } - ELSE - ENDIF + static tree value64 = gg_define_variable( LONG, + ".._gdos_value64", + vs_file_static); + cbl_field_t *odo = symbol_find_odo(parent); + get_depending_on_value_from_odo(value64, odo); } - tree augment = gg_multiply(subscript, build_int_cst_type(INT, parent->data.capacity)); - gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, augment))); } + + // Subscript is now a one-based integer + // Make it zero-based: + + gg_decrement(subscript); + + tree augment = gg_multiply(subscript, get_any_capacity(parent)); + gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, augment))); + parent = parent_of(parent); } } if( refer.refmod.from ) { + REFER("refmod refstart"); // We have a refmod to deal with static tree refstart = gg_define_variable(LONG, "..gdo_refstart", vs_file_static); + static tree reflen = gg_define_variable(LONG, "..gdo_reflen", vs_file_static); + get_and_check_refstart_and_reflen(refstart, reflen, refer); - if( process_this_exception(ec_bound_ref_mod_e) ) - { - get_integer_value(value64, - refer.refmod.from->field, - refer_offset_source(*refer.refmod.from), - CHECK_FOR_FRACTIONAL_DIGITS); - IF( var_decl_rdigits, - ne_op, - integer_zero_node ) - { - // refmod offset is not an integer, and has to be - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_one_node)); - } - else - { - rt_error("error: a refmod FROM is not an integer"); - } - } - ELSE - gg_assign(refstart, value64); - ENDIF - } - else - { - get_integer_value(value64, - refer.refmod.from->field, - refer_offset_source(*refer.refmod.from) - ); - gg_assign(refstart, value64); - } - - // Make refstart zero-based: - gg_decrement(refstart); - - if( process_this_exception(ec_bound_ref_mod_e) ) - { - IF( refstart, lt_op, gg_cast(LONG, integer_zero_node) ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - } - else - { - rt_error("error: refmod FROM is less than one"); - } - } - ELSE - { - IF( refstart, gt_op, build_int_cst_type(LONG, refer.field->data.capacity) ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - } - else - { - rt_error("error: refmod FROM is too large"); - } - } - ELSE - ENDIF - } - ENDIF - } - - // We have a good refstart gg_assign(retval, gg_add(retval, gg_cast(SIZE_T, refstart))); } @@ -820,14 +722,11 @@ get_data_offset_source(cbl_refer_t &refer, *pflags = all_flags; } - -// gg_printf("*****>>>>> %s(): returning %p\n", -// gg_string_literal(__func__), -// retval, -// NULL_TREE); return retval; } +static tree tree_type_from_field(const cbl_field_t *field); + void get_binary_value( tree value, tree rdigits, @@ -856,16 +755,16 @@ get_binary_value( tree value, return; } - static tree pointer = gg_define_variable(UCHAR_P, "..gbv_pointer", vs_file_static); - static tree pend = gg_define_variable(UCHAR_P, "..gbv_pend", vs_file_static); - + static tree pointer = gg_define_variable( UCHAR_P, + "..gbv_pointer", + vs_file_static); switch(field->type) { case FldLiteralN: { if( SCALAR_FLOAT_TYPE_P(value) ) { - gg_assign(value, gg_cast(TREE_TYPE(value), field->literal_decl_node)); + cbl_internal_error("cannot get %<float%> value from %s", field->name); } else { @@ -895,8 +794,9 @@ get_binary_value( tree value, // We need to check early on for HIGH-VALUE and LOW-VALUE // Pick up the byte tree digit = gg_get_indirect_reference(source_address, NULL_TREE); - IF( digit, eq_op, build_int_cst(UCHAR, 0xFF) ) + IF( digit, eq_op, build_int_cst(UCHAR, DEGENERATE_HIGH_VALUE) ) { + // We are dealing with HIGH-VALUE if( hilo ) { gg_assign(hilo, integer_one_node); @@ -907,12 +807,14 @@ get_binary_value( tree value, build_int_cst_type( TREE_TYPE(rdigits), get_scaled_rdigits(field))); } - gg_assign(value, build_int_cst_type(TREE_TYPE(value), 0xFFFFFFFFFFFFFFFUL)); + gg_assign(value, build_int_cst_type(TREE_TYPE(value), + 0x7FFFFFFFFFFFFFFFUL)); } ELSE { - IF( digit, eq_op, build_int_cst(UCHAR, 0x00) ) + IF( digit, eq_op, build_int_cst(UCHAR, DEGENERATE_LOW_VALUE) ) { + // We are dealing with LOW-VALUE if( hilo ) { gg_assign(hilo, integer_minus_one_node); @@ -920,26 +822,25 @@ get_binary_value( tree value, } ELSE { - // Establish rdigits: + // We are dealing with an ordinary NumericDisplay value + gg_assign(pointer, source_address); + if( rdigits ) { gg_assign(rdigits, - build_int_cst_type( TREE_TYPE(rdigits), - get_scaled_rdigits(field))); + build_int_cst_type(TREE_TYPE(rdigits), + get_scaled_rdigits(field))); } - // Zero out the destination - gg_assign(value, gg_cast(TREE_TYPE(value), integer_zero_node)); - // Pick up a pointer to the source bytes: - - gg_assign(pointer, source_address); - - // This is the we-are-done pointer - gg_assign(pend, gg_add( pointer, - build_int_cst_type(SIZE_T, field->data.capacity))); - - static tree signbyte = gg_define_variable(UCHAR, "..gbv_signbyte", vs_file_static); - - // The big decision is whether or not the variable is signed: + // This will be the 128-bit value of the character sequence + static tree val128 = gg_define_variable(INT128, + "..gbv_val128", + vs_file_static); + // This is a pointer to the sign byte + static tree signp = gg_define_variable(UCHAR_P, + "..gbv_signp", + vs_file_static); + // We need to figure out where the sign information, if any is to be + // found: if( field->attr & signable_e ) { // The variable is signed @@ -949,12 +850,17 @@ get_binary_value( tree value, if( field->attr & leading_e) { // The first byte is '+' or '-' + gg_assign(signp, source_address); + // Increment pointer to point to the first actual digit gg_increment(pointer); } else { // The final byte is '+' or '-' - gg_decrement(pend); + gg_assign(signp, + gg_add(source_address, + build_int_cst_type( SIZE_T, + field->data.digits))); } } else @@ -962,219 +868,35 @@ get_binary_value( tree value, // The sign byte is internal if( field->attr & leading_e) { - // The first byte has the sign bit: - gg_assign(signbyte, - gg_get_indirect_reference(source_address, NULL_TREE)); - if( internal_codeset_is_ebcdic() ) - { - // We need to make sure the EBCDIC sign bit is ON, for positive - gg_assign(gg_get_indirect_reference(source_address, NULL_TREE), - gg_bitwise_or(signbyte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT))); - } - else - { - // We need to make sure the ascii sign bit is Off, for positive - gg_assign(gg_get_indirect_reference(source_address, NULL_TREE), - gg_bitwise_and( signbyte, - build_int_cst_type( UCHAR, - ~NUMERIC_DISPLAY_SIGN_BIT))); - } + // The first byte has the sign bit. + gg_assign(signp, source_address); } else { - // The final byte has the sign bit: - gg_assign(signbyte, - gg_get_indirect_reference(source_address, - build_int_cst_type(SIZE_T, - field->data.capacity-1))); - if( internal_codeset_is_ebcdic() ) - { - // We need to make sure the EBCDIC sign bit is ON, for positive - gg_assign(gg_get_indirect_reference(source_address, - build_int_cst_type( SIZE_T, - field->data.capacity-1)), - gg_bitwise_or(signbyte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT))); - } - else - { - // We need to make sure the ASCII sign bit is Off, for positive - gg_assign(gg_get_indirect_reference(source_address, - build_int_cst_type( SIZE_T, - field->data.capacity-1)), - gg_bitwise_and( signbyte, - build_int_cst_type( UCHAR, - ~NUMERIC_DISPLAY_SIGN_BIT))); - } + // The final byte has the sign bit. + gg_assign(signp, + gg_add(source_address, + build_int_cst_type( SIZE_T, + field->data.digits-1))); } } } - // We can now set up the byte-by-byte processing loop: - if( internal_codeset_is_ebcdic() ) - { - // We are working in EBCDIC - WHILE( pointer, lt_op, pend ) - { - // Pick up the byte - digit = gg_get_indirect_reference(pointer, NULL_TREE); - IF( digit, lt_op, build_int_cst_type(UCHAR, EBCDIC_ZERO) ) - { - // break on a non-digit - gg_assign(pointer, pend); - } - ELSE - { - IF( digit, gt_op, build_int_cst_type(UCHAR, EBCDIC_NINE) ) - { - // break on a non-digit - gg_assign(pointer, pend); - } - ELSE - { - // Whether ASCII or EBCDIC, the bottom four bits tell the tale: - // Multiply our accumulator by ten: - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - // And add in the current digit - gg_assign(value, - gg_add(value, gg_cast(TREE_TYPE(value), gg_bitwise_and( digit, - build_int_cst_type(UCHAR, 0x0F) )))); - gg_increment(pointer); - } - ENDIF - } - ENDIF - } - WEND - } else { - // We are working in ASCII: - WHILE( pointer, lt_op, pend ) - { - // Pick up the byte - digit = gg_get_indirect_reference(pointer, NULL_TREE); - // Whether ASCII or EBCDIC, the bottom four bits tell the tale: - // Multiply our accumulator by ten: - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - // And add in the current digit - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_bitwise_and(digit, build_int_cst_type(UCHAR, 0x0F))))); - gg_increment(pointer); - } - WEND + // This value is unsigned, so just use the first location: + gg_assign(signp, source_address); } - // Value contains the binary value. The last thing is to apply -- and - // undo -- the signable logic: - - if( field->attr & signable_e ) - { - // The variable is signed - if( field->attr & separate_e ) - { - // The sign byte is separate - if( field->attr & leading_e) - { - // The first byte is '+' or '-' - if( internal_codeset_is_ebcdic() ) - { - // We are operating in EBCDIC, so we look for a 96 (is minus sign) - IF( gg_get_indirect_reference(source_address, NULL_TREE), - eq_op, - build_int_cst_type(UCHAR, 96) ) - { - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF - } - else - { - // We are operating in ASCII - IF( gg_get_indirect_reference(source_address, NULL_TREE), - eq_op, - build_int_cst_type(UCHAR, '-') ) - { - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF - } - } - else - { - // The final byte is '+' or '-' - if( internal_codeset_is_ebcdic() ) - { - // We are operating in EBCDIC, so we look for a 96 (is minus sign) - IF( gg_get_indirect_reference(source_address, build_int_cst_type(SIZE_T, field->data.capacity-1)), - eq_op, - build_int_cst_type(UCHAR, 96) ) - { - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF - } - else - { - // We are operating in ASCII - IF( gg_get_indirect_reference(source_address, build_int_cst_type(SIZE_T, field->data.capacity-1)), - eq_op, - build_int_cst_type(UCHAR, '-') ) - { - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF - } - } - } - else - { - // The sign byte is internal. Check the sign bit - if(internal_codeset_is_ebcdic()) - { - IF( gg_bitwise_and( signbyte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), eq_op, build_int_cst_type(UCHAR, 0) ) - { - // The EBCDIC sign bit was OFF, so negate the result - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF - } - else - { - IF( gg_bitwise_and( signbyte, - build_int_cst_type( UCHAR, - NUMERIC_DISPLAY_SIGN_BIT)), ne_op, build_int_cst_type(UCHAR, 0) ) - { - // The ASCII sign bit was on, so negate the result - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF - } - // It's time to put back the original data: - if( field->attr & leading_e) - { - // The first byte has the sign bit: - gg_assign(gg_get_indirect_reference(source_address, NULL_TREE), - signbyte); - } - else - { - // The final byte has the sign bit: - gg_assign(gg_get_indirect_reference(source_address, - build_int_cst_type(SIZE_T, field->data.capacity-1)), - signbyte); - } - } - } + gg_assign(val128, + gg_call_expr( INT128, + "__gg__numeric_display_to_binary", + signp, + pointer, + build_int_cst_type(INT, field->data.digits), + build_int_cst_type(INT, field->codeset.encoding), + NULL_TREE)); + // Assign the value we got from the string to our "return" value: + gg_assign(value, gg_cast(TREE_TYPE(value), val128)); } ENDIF } @@ -1223,7 +945,9 @@ get_binary_value( tree value, vs_file_static); if( field->attr & signable_e ) { - IF( gg_array_value(gg_cast(build_pointer_type(SCHAR), source)), lt_op, gg_cast(SCHAR, integer_zero_node) ) + IF( gg_array_value(gg_cast(build_pointer_type(SCHAR), source)), + lt_op, + gg_cast(SCHAR, integer_zero_node) ) { gg_assign(extension, build_int_cst_type(UCHAR, 0xFF)); } @@ -1306,45 +1030,23 @@ get_binary_value( tree value, case FldPacked: { - // Zero out the destination: - gg_assign(value, gg_cast(TREE_TYPE(value), integer_zero_node)); - gg_assign(pointer, get_data_address(field, field_offset)); - gg_assign(pend, - gg_add(pointer, - build_int_cst_type(SIZE_T, field->data.capacity-1))); - - // Convert all but the last byte of the packed decimal sequence - WHILE( pointer, lt_op, pend ) - { - // Convert the first nybble - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_rshift(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst(UINT, 4))))); - - // Convert the second nybble - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_bitwise_and(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst_type(UCHAR, 0xF))))); - gg_increment(pointer); - } - WEND - - // This is the final byte: - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_rshift(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst(UINT, 4))))); - - IF( gg_bitwise_and(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst_type(UCHAR, 0xF)), eq_op, build_int_cst_type(UCHAR, 0x0D) ) - { - gg_assign(value, gg_negate(value)); - } - ELSE + if( rdigits ) { - IF( gg_bitwise_and(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst_type(UCHAR, 0xF)), eq_op, build_int_cst_type(UCHAR, 0x0B) ) - { - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF + gg_assign(rdigits, + build_int_cst_type( TREE_TYPE(rdigits), + get_scaled_rdigits(field))); } - ENDIF + tree dest_type = TREE_TYPE(value); + + gg_assign(value, + gg_cast(dest_type, + gg_call_expr(INT128, + "__gg__packed_to_binary", + get_data_address( field, + field_offset), + build_int_cst_type(INT, + field->data.capacity), + NULL_TREE))); break; } @@ -1366,18 +1068,13 @@ get_binary_value( tree value, break; } - case FldAlphanumeric: - { - - } - - default: { - fprintf(stderr, "%s(): We know not how to" - " get a binary value from %s\n", - __func__, - cbl_field_type_str(field->type) ); + char *err = xasprintf("%s(): We know not how to" + " get a binary value from %s\n", + __func__, + cbl_field_type_str(field->type) ); + cbl_internal_error("%s", err); abort(); break; } @@ -1395,8 +1092,8 @@ get_binary_value( tree value, } } -tree -tree_type_from_field(cbl_field_t *field) +static tree +tree_type_from_field(const cbl_field_t *field) { gcc_assert(field); return tree_type_from_size(field->data.capacity, field->attr & signable_e); @@ -1420,17 +1117,14 @@ get_data_address( cbl_field_t *field, } } -// Ignore pedantic because we know 128-bit computation is not ISO C++14. -#pragma GCC diagnostic ignored "-Wpedantic" - -__int128 +FIXED_WIDE_INT(128) get_power_of_ten(int n) { // 2** 64 = 1.8E19 // 2**128 = 3.4E38 - __int128 retval = 1; + FIXED_WIDE_INT(128) retval = 1; static const int MAX_POWER = 19 ; - static const __int128 pos[MAX_POWER+1] = + static const unsigned long long pos[MAX_POWER+1] = { 1ULL, // 00 10ULL, // 01 @@ -1467,7 +1161,7 @@ get_power_of_ten(int n) else { // 19 through 38 is handled in a second step, because when this was written, - // GCC couldn't handle __int128 constants: + // GCC couldn't handle 128-bit constants: retval = pos[n/2]; retval *= retval; if( n & 1 ) @@ -1489,40 +1183,31 @@ scale_by_power_of_ten_N(tree value, Analyzer.Message("takes int N"); if( N == 0 ) { - if( check_for_fractional ) - { - gg_assign(var_decl_rdigits, integer_zero_node); - } } else if( N > 0 ) { - if( check_for_fractional ) - { - gg_assign(var_decl_rdigits, integer_zero_node); - } tree value_type = TREE_TYPE(value); - __int128 power_of_ten = get_power_of_ten(N); - gg_assign(value, gg_multiply(value, build_int_cst_type( value_type, + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(N); + gg_assign(value, gg_multiply(value, wide_int_to_tree( value_type, power_of_ten))); } if( N < 0 ) { tree value_type = TREE_TYPE(value); - __int128 power_of_ten = get_power_of_ten(-N); + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(-N); if( check_for_fractional ) { - IF( gg_mod(value, build_int_cst_type( value_type, - power_of_ten)), + IF( gg_mod(value, wide_int_to_tree( value_type, + power_of_ten)), ne_op, gg_cast(value_type, integer_zero_node) ) { gg_assign(var_decl_rdigits, integer_one_node); } ELSE - gg_assign(var_decl_rdigits, integer_zero_node); ENDIF } - gg_assign(value, gg_divide(value, build_int_cst_type( value_type, + gg_assign(value, gg_divide(value, wide_int_to_tree( value_type, power_of_ten))); } } @@ -1701,7 +1386,7 @@ tree_type_from_size(size_t bytes, int signable) static bool -refer_has_depends(cbl_refer_t &refer, refer_type_t refer_type) +refer_has_depends(const cbl_refer_t &refer, refer_type_t refer_type) { if( suppress_dest_depends ) { @@ -1719,7 +1404,7 @@ refer_has_depends(cbl_refer_t &refer, refer_type_t refer_type) // Check if there there is an occurs with a depending_on in the hierarchy bool proceed = false; - cbl_field_t *odo = symbol_find_odo(refer.field); + const cbl_field_t *odo = symbol_find_odo(refer.field); cbl_field_t *depending_on; if( odo && odo != refer.field ) { @@ -1745,7 +1430,7 @@ refer_has_depends(cbl_refer_t &refer, refer_type_t refer_type) { parent1 = p; } - cbl_field_t *parent2 = depending_on; + const cbl_field_t *parent2 = depending_on; while( (p = parent_of(parent2)) ) { parent2 = p; @@ -1807,8 +1492,9 @@ set_exception_code_func(ec_type_t ec, int /*line*/, int from_raise_statement) } bool -process_this_exception(ec_type_t ec) +process_this_exception(const ec_type_t ec) { + const cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); bool retval; if( enabled_exceptions.match(ec) || !skip_exception_processing ) { @@ -1840,7 +1526,7 @@ copy_little_endian_into_place(cbl_field_t *dest, tree value, int rhs_rdigits, bool check_for_error, - tree &size_error) + const tree &size_error) { if( check_for_error ) { @@ -1865,12 +1551,12 @@ copy_little_endian_into_place(cbl_field_t *dest, } ENDIF - __int128 power_of_ten = get_power_of_ten( dest->data.digits - - dest->data.rdigits - + rhs_rdigits ); + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( dest->data.digits + - dest->data.rdigits + + rhs_rdigits ); IF( gg_cast(INT128, abs_value), ge_op, - build_int_cst_type(INT128, power_of_ten) ) + wide_int_to_tree(INT128, power_of_ten) ) { // Flag the size error gg_assign(size_error, integer_one_node); @@ -1916,7 +1602,7 @@ build_array_of_treeplets( int ngroup, refers[i].field ? gg_get_address_of(refers[i].field->var_decl_node) : gg_cast(cblc_field_p_type_node, null_pointer_node)); gg_assign(gg_array_value(var_decl_treeplet_1o, i), - refer_offset_source(refers[i])); + refer_offset(refers[i])); gg_assign(gg_array_value(var_decl_treeplet_1s, i), refer_size_source(refers[i])); } @@ -1928,7 +1614,7 @@ build_array_of_treeplets( int ngroup, refers[i].field ? gg_get_address_of(refers[i].field->var_decl_node) : gg_cast(cblc_field_p_type_node, null_pointer_node)); gg_assign(gg_array_value(var_decl_treeplet_2o, i), - refer_offset_source(refers[i])); + refer_offset(refers[i])); gg_assign(gg_array_value(var_decl_treeplet_2s, i), refer_size_source(refers[i])); } @@ -1940,7 +1626,7 @@ build_array_of_treeplets( int ngroup, refers[i].field ? gg_get_address_of(refers[i].field->var_decl_node) : gg_cast(cblc_field_p_type_node, null_pointer_node)); gg_assign(gg_array_value(var_decl_treeplet_3o, i), - refer_offset_source(refers[i])); + refer_offset(refers[i])); gg_assign(gg_array_value(var_decl_treeplet_3s, i), refer_size_source(refers[i])); } @@ -1952,7 +1638,7 @@ build_array_of_treeplets( int ngroup, refers[i].field ? gg_get_address_of(refers[i].field->var_decl_node) : gg_cast(cblc_field_p_type_node, null_pointer_node)); gg_assign(gg_array_value(var_decl_treeplet_4o, i), - refer_offset_source(refers[i])); + refer_offset(refers[i])); gg_assign(gg_array_value(var_decl_treeplet_4s, i), refer_size_source(refers[i])); } @@ -1997,7 +1683,7 @@ build_array_of_fourplets( int ngroup, gg_assign(gg_array_value(var_decl_treeplet_1f, i), gg_get_address_of(refers[i].field->var_decl_node)); gg_assign(gg_array_value(var_decl_treeplet_1o, i), - refer_offset_source(refers[i], &flag_bits)); + refer_offset(refers[i], &flag_bits)); gg_assign(gg_array_value(var_decl_treeplet_1s, i), refer_size_source(refers[i])); gg_assign(gg_array_value(var_decl_fourplet_flags, i), @@ -2054,370 +1740,79 @@ char * get_literal_string(cbl_field_t *field) { assert(field->type == FldLiteralA); - char *buffer = NULL; - size_t buffer_length = 0; - if( buffer_length < field->data.capacity+1 ) - { - buffer_length = field->data.capacity+1; - buffer = (char *)xrealloc(buffer, buffer_length); - } - for(size_t i=0; i<field->data.capacity; i++) - { - buffer[i] = ascii_to_internal(field->data.initial[i]); - } - buffer[field->data.capacity] = '\0'; + size_t buffer_length = field->data.capacity+1; + char *buffer = static_cast<char *>(xcalloc(1, buffer_length)); + + size_t charsout; + const char *converted = __gg__iconverter(DEFAULT_SOURCE_ENCODING, + field->codeset.encoding, + field->data.initial, + field->data.capacity, + &charsout); + memcpy(buffer, converted, field->data.capacity+1); return buffer; } bool -refer_is_clean(cbl_refer_t &refer) +refer_is_clean(const cbl_refer_t &refer) { - if( !refer.field ) + if( !refer.field || refer.field->type == FldLiteralN ) { // It is routine for a refer to have no field. It happens when the parser - // passes us a refer for an optional parameter that has been ommitted, for + // passes us a refer for an optional parameter that has been omitted, for // example. + + // It is also the case that a FldLiteralN will never have suscripts, or the + // like. return true; } return !refer.all && !refer.addr_of - && !refer.nsubscript + && !refer.nsubscript() && !refer.refmod.from && !refer.refmod.len && !refer_has_depends(refer, refer_source) ; } -void -REFER_CHECK(const char *func, - int line, - cbl_refer_t &refer - ) - { - static int counter=1; - - if( counter == 5 ) - { - fprintf(stderr, "DING! %d\n", counter); - } +/* This routine returns the length portion of a refmod(start:length) reference. + It extracts both the start and the length so that it can add them together + to make sure that result falls within refer.capacity. - fprintf(stderr, - "ct REFER_CHECK(%d): %s():%d %s\n", - counter, - func, - line, - refer.field->name); - - gg_printf("rt REFER_CHECK(%d): %s():%d %s (%s)\n", - build_int_cst_type(INT, counter), - gg_string_literal(func), - build_int_cst_type(INT, line), - gg_string_literal(refer.field->name), - gg_string_literal(cbl_field_type_str(refer.field->type)), - NULL_TREE); - counter+=1; - } - + This routine shouldn't be called unless there is refmod involved. + */ static tree // size_t -refer_refmod_length(cbl_refer_t &refer) +refer_refmod_length(const cbl_refer_t &refer) { Analyze(); - if( refer.refmod.from || refer.refmod.len ) - { - // First, check for compile-time errors - bool any_length = !!(refer.field->attr & any_length_e); - tree rt_capacity; - static tree value64 = gg_define_variable(LONG, "..rrl_value64", vs_file_static); - static tree refstart = gg_define_variable(LONG, "..rrl_refstart", vs_file_static); - static tree reflen = gg_define_variable(LONG, "..rrl_reflen", vs_file_static); - - if( any_length ) - { - rt_capacity = - gg_cast(LONG, - member(refer.field->var_decl_node, "capacity")); - } - else - { - rt_capacity = - build_int_cst_type(LONG, refer.field->data.capacity); - } - - gg_assign(reflen, gg_cast(TREE_TYPE(reflen), integer_one_node)); - - if( process_this_exception(ec_bound_ref_mod_e) ) - { - get_integer_value(value64, - refer.refmod.from->field, - refer_offset_source(*refer.refmod.from), - CHECK_FOR_FRACTIONAL_DIGITS); - IF( var_decl_rdigits, - ne_op, - integer_zero_node ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_one_node)); - } - else - { - rt_error("a refmod FROM value is not an integer"); - } - } - ELSE - gg_assign(refstart, value64); - ENDIF - } - else - { - get_integer_value(value64, - refer.refmod.from->field, - refer_offset_source(*refer.refmod.from) - ); - gg_assign(refstart, value64); - } - - // Make refstart zero-based: - gg_decrement(refstart); + REFER("refstart and reflen"); + static tree refstart = gg_define_variable(LONG, "..rrl_refstart", vs_file_static); + static tree reflen = gg_define_variable(LONG, "..rrl_reflen", vs_file_static); - if( process_this_exception(ec_bound_ref_mod_e) ) - { - IF( refstart, lt_op, build_int_cst_type(LONG, 0 ) ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - } - else - { - rt_error("a refmod FROM value is less than zero"); - } - } - ELSE - { - IF( refstart, gt_op, rt_capacity ) - { - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - } - else - { - rt_error("a refmod FROM value is too large"); - } - } - ELSE - { - if( refer.refmod.len ) - { - get_integer_value(value64, - refer.refmod.len->field, - refer_offset_source(*refer.refmod.len), - CHECK_FOR_FRACTIONAL_DIGITS); - IF( var_decl_rdigits, - ne_op, - integer_zero_node ) - { - // length is not an integer - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(reflen, gg_cast(LONG, integer_one_node)); - } - else - { - rt_error("a refmod LENGTH is not an integer"); - } - } - ELSE - { - gg_assign(reflen, gg_cast(LONG, value64)); - } - ENDIF - - IF( reflen, lt_op, gg_cast(LONG, integer_one_node) ) - { - // length is too small - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - gg_assign(reflen, gg_cast(LONG, integer_one_node)); - } - else - { - rt_error("a refmod LENGTH is less than one"); - } - } - ELSE - { - IF( gg_add(refstart, reflen), - gt_op, - rt_capacity ) - { - // Start + Length is too large - if( enabled_exceptions.match(ec_bound_ref_mod_e) ) - { - SET_EXCEPTION_CODE(ec_bound_ref_mod_e); - - // Our intentions are honorable. But at this point, where - // we notice that start + length is too long, the - // get_data_offset_source routine has already been run and - // it's too late to actually change the refstart. There are - // theoretical solutions to this -- mainly, - // get_data_offset_source needs to check the start + len for - // validity. But I am not going to do it now. Think of this - // as the TODO item. - gg_assign(refstart, gg_cast(LONG, integer_zero_node)); - gg_assign(reflen, gg_cast(LONG, integer_one_node)); - } - else - { - rt_error("refmod START + LENGTH is too large"); - } - } - ELSE - ENDIF - } - ENDIF - } - else - { - // There is no refmod length, so we default to the remaining characters - tree subtract_expr = gg_subtract( rt_capacity, - refstart); - gg_assign(reflen, subtract_expr); - } - } - ENDIF - } - ENDIF - } - else - { - if( refer.refmod.len ) - { - get_integer_value(value64, - refer.refmod.len->field, - refer_offset_source(*refer.refmod.len) - ); - gg_assign(reflen, gg_cast(LONG, value64)); - } - else - { - // There is no refmod length, so we default to the remaining characters - gg_assign(reflen, gg_subtract(rt_capacity, - refstart)); - } - } + get_and_check_refstart_and_reflen( refstart, reflen, refer); - // Arrive here with valid values for refstart and reflen: + // Arrive here with a valid value for reflen: - return gg_cast(SIZE_T, reflen); - } - else - { - return size_t_zero_node; - } + return gg_cast(SIZE_T, reflen); } static tree // size_t -refer_fill_depends(cbl_refer_t &refer) +refer_fill_depends(const cbl_refer_t &refer) { + REFER(""); // This returns a positive number which is the amount a depends-limited // capacity needs to be reduced. Analyze(); cbl_field_t *odo = symbol_find_odo(refer.field); - cbl_field_t *depending_on; - depending_on = cbl_field_of(symbol_at(odo->occurs.depending_on)); - // refer.field has a relevant DEPENDING ON clause - - // gg_printf("var is %s type is %s\n", - // gg_string_literal(refer.field->name), - // gg_string_literal(cbl_field_type_str(refer.field->type)), - // NULL_TREE); - // gg_printf(" odo is %s\n", gg_string_literal(odo->name), NULL_TREE); - - // gg_printf(" depending_on is %s\n", gg_string_literal(depending_on->name), NULL_TREE); - // fprintf(stderr, - // "symbol_find_odo found %s, with depending_on %s\n", - // odo->name, - // depending_on->name); static tree value64 = gg_define_variable(LONG, "..rfd_value64", vs_file_static); - if( process_this_exception(ec_bound_odo_e) ) - { - get_integer_value(value64, - depending_on, - NULL, - CHECK_FOR_FRACTIONAL_DIGITS); - IF( var_decl_rdigits, ne_op, integer_zero_node ) - { - // This needs to evaluate to an integer - if( enabled_exceptions.match(ec_bound_odo_e) ) - { - SET_EXCEPTION_CODE(ec_bound_odo_e); - gg_assign(value64, build_int_cst_type(TREE_TYPE(value64), odo->occurs.bounds.upper)); - } - else - { - rt_error("DEPENDING ON is not an integer"); - } - } - ELSE - ENDIF - } - else - { - get_integer_value(value64, depending_on); - } - if( process_this_exception(ec_bound_odo_e) ) - { - IF( value64, gt_op, build_int_cst_type(TREE_TYPE(value64), odo->occurs.bounds.upper) ) - { - SET_EXCEPTION_CODE(ec_bound_odo_e); - gg_assign(value64, build_int_cst_type(TREE_TYPE(value64), odo->occurs.bounds.upper)); - } - ELSE - { - IF( value64, lt_op, build_int_cst_type(TREE_TYPE(value64), odo->occurs.bounds.lower) ) - { - if( enabled_exceptions.match(ec_bound_odo_e) ) - { - SET_EXCEPTION_CODE(ec_bound_odo_e); - gg_assign(value64, build_int_cst_type(TREE_TYPE(value64), odo->occurs.bounds.lower)); - } - else - { - rt_error("DEPENDING ON is less than OCCURS lower limit"); - } - } - ELSE - ENDIF - IF( value64, lt_op, gg_cast(TREE_TYPE(value64), integer_zero_node) ) - { - if( enabled_exceptions.match(ec_bound_odo_e) ) - { - SET_EXCEPTION_CODE(ec_bound_odo_e); - gg_assign(value64, gg_cast(TREE_TYPE(value64), integer_zero_node)); - } - else - { - rt_error("DEPENDING ON is greater than OCCURS upper limit"); - } - } - ELSE - ENDIF - } - ENDIF - } + get_depending_on_value(value64, refer); + // value64 is >= zero and < bounds.upper // We multiply the ODO value by the size of the data capacity to get the @@ -2433,90 +1828,59 @@ refer_fill_depends(cbl_refer_t &refer) } tree // size_t -refer_offset_dest(cbl_refer_t &refer) +refer_offset(const cbl_refer_t &refer, + int *pflags) { - Analyze(); - // This has to be on the stack, because there are places where this routine - // is called twice before the results are used. + // This routine calculates the effect of a refer offset on the + // refer.field->data location. When there are subscripts, the data location + // gets augmented by the (subscript-1)*element_size calculation. And when + // there is a refmod, the data location additionally gets augmented by + // (refmod.from-1) if( !refer.field ) { + // It's common for the field to be missing. It generally means that an + // optional parameter wasn't supplied. return size_t_zero_node; } - if( !refer.nsubscript ) + if( refer.field->type == FldLiteralN || refer.field->type == FldLiteralA ) { - return get_data_offset_dest(refer); + // We know that literals have no offset + return size_t_zero_node; } - gg_assign(var_decl_odo_violation, integer_zero_node); - - tree retval = gg_define_variable(SIZE_T); - gg_assign(retval, get_data_offset_dest(refer)); - if( process_this_exception(ec_bound_odo_e) ) - { - IF( var_decl_odo_violation, ne_op, integer_zero_node ) - { - if( enabled_exceptions.match(ec_bound_odo_e) ) - { - SET_EXCEPTION_CODE(ec_bound_odo_e); - } - else - { - rt_error("receiving item subscript not in DEPENDING ON range"); - } - } - ELSE - ENDIF - } + tree retval = get_data_offset(refer, pflags); return retval; } -tree // size_t -refer_size_dest(cbl_refer_t &refer) +static +tree // size_t +refer_size(const cbl_refer_t &refer, refer_type_t refer_type) { Analyze(); - //static tree retval = gg_define_variable(SIZE_T, "..rsd_retval", vs_file_static); - tree retval = gg_define_variable(SIZE_T); + static tree retval = gg_define_variable(SIZE_T, "..rs_retval", vs_file_static); if( !refer.field ) { return size_t_zero_node; } + if( refer_is_clean(refer) ) { - // When the refer has no modifications, we return zero, which is interpreted - // as "use the original length" - if( refer.field->attr & (intermediate_e | any_length_e) ) - { - return member(refer.field->var_decl_node, "capacity"); - } - else - { - return build_int_cst_type(SIZE_T, refer.field->data.capacity); - } + return get_any_capacity(refer.field); } // Step the first: Get the actual full length: - if( refer.field->attr & (intermediate_e | any_length_e) ) - { - // This is an intermediate; use the length that might have changed - // because of a FUNCTION TRIM, or whatnot. - - // We also pick up capacity for variables that were specified in - // linkage as ANY LENGTH - gg_assign(retval, member(refer.field->var_decl_node, "capacity")); - } - if( refer_has_depends(refer, refer_dest) ) + if( refer_has_depends(refer, refer_type) ) { // Because there is a depends, we might have to change the length: gg_assign(retval, refer_fill_depends(refer)); } else { - // Use the compile-time value - gg_assign(retval, build_int_cst_type(SIZE_T, refer.field->data.capacity)); + gg_assign(retval, get_any_capacity(refer.field)); } if( refer.refmod.from || refer.refmod.len ) @@ -2525,7 +1889,7 @@ refer_size_dest(cbl_refer_t &refer) // retval is the ODO based total length. // refmod is the length resulting from refmod(from:len) // We have to reduce retval by the effect of refmod: - tree diff = gg_subtract(build_int_cst_type(SIZE_T, refer.field->data.capacity), + tree diff = gg_subtract(get_any_capacity(refer.field), refmod); gg_assign(retval, gg_subtract(retval, diff)); } @@ -2533,113 +1897,74 @@ refer_size_dest(cbl_refer_t &refer) } tree // size_t -refer_offset_source(cbl_refer_t &refer, - int *pflags) +refer_size_dest(const cbl_refer_t &refer) { - if( !refer.field ) - { - return size_t_zero_node; - } - if( !refer.nsubscript ) - { - return get_data_offset_source(refer); - } - - Analyze(); - - tree retval = gg_define_variable(SIZE_T); - gg_assign(var_decl_odo_violation, integer_zero_node); - - gg_assign(retval, get_data_offset_source(refer, pflags)); - if( process_this_exception(ec_bound_odo_e) ) - { - IF( var_decl_odo_violation, ne_op, integer_zero_node ) - { - if( enabled_exceptions.match(ec_bound_odo_e) ) - { - SET_EXCEPTION_CODE(ec_bound_odo_e); - } - else - { - rt_error("sending item subscript not in DEPENDING ON range"); - } - } - ELSE - ENDIF - } - return retval; + return refer_size(refer, refer_dest); } tree // size_t -refer_size_source(cbl_refer_t &refer) +refer_size_source(const cbl_refer_t &refer) { - if( !refer.field ) - { - return size_t_zero_node; - } - if( refer_is_clean(refer) ) - { - // When the refer has no modifications, we return zero, which is interpreted - // as "use the original length" - if( refer.field->attr & (intermediate_e | any_length_e) ) - { - return member(refer.field->var_decl_node, "capacity"); - } - else - { - return build_int_cst_type(SIZE_T, refer.field->data.capacity); - } - } + /* There are oddities involved with refer_size_source and refer_size_dest. + See the comments in refer_has_depends for some explanation. There are + other considerations, as well. For example, consider a move, where you + have both a source and a dest. Given that refer_size returns a static, + there are ways that the source and dest can trip over each other. - Analyze(); + The logic here avoids all known cases where they might trip over each + other. But there conceivably might be others,. - // Step the first: Get the actual full length: - static tree retval = gg_define_variable(SIZE_T, "..rss_retval", vs_file_static); - if( refer.field->attr & (intermediate_e | any_length_e) ) - { - // This is an intermediate; use the length that might have changed - // because of a FUNCTION TRIM, or whatnot. + You have been warned. - // We also pick up capacity for variables that were specified in - // linkage as ANY LENGTH - gg_assign(retval, - member(refer.field->var_decl_node, "capacity")); - } + */ - if( refer_has_depends(refer, refer_source) ) - { - // Because there is a depends, we might have to change the length: - gg_assign(retval, refer_fill_depends(refer)); - } - else + // This test has to be here, otherwise there are failures in regression + // testing. + if( !refer.field ) { - // Use the compile-time value - gg_assign(retval, build_int_cst_type(SIZE_T, refer.field->data.capacity)); + return size_t_zero_node; } - if( refer.refmod.from || refer.refmod.len ) + // This test has to be here, otherwise there are failures in regression + // testing. + if( refer_is_clean(refer) ) { - tree refmod = refer_refmod_length(refer); - // retval is the ODO based total length. - // refmod is the length resulting from refmod(from:len) - // We have to reduce retval by the effect of refmod: - tree diff = gg_subtract(build_int_cst_type(SIZE_T, refer.field->data.capacity), - refmod); - gg_assign(retval, gg_subtract(retval, diff)); + return get_any_capacity(refer.field); } + + // This assignment has to be here. Simply returning refer_size() results + // in regression testing errors. + static tree retval = gg_define_variable(SIZE_T, "..rss_retval", vs_file_static); + gg_assign(retval, refer_size(refer, refer_source)); return retval; } tree -qualified_data_source(cbl_refer_t &refer) +qualified_data_location(const cbl_refer_t &refer) { return gg_add(member(refer.field->var_decl_node, "data"), - refer_offset_source(refer)); + refer_offset(refer)); } -tree -qualified_data_dest(cbl_refer_t &refer) - { - return gg_add(member(refer.field->var_decl_node, "data"), - refer_offset_dest(refer)); - } +uint64_t +get_time_nanoseconds() +{ + // This code was unabashedly stolen from gcc/timevar.cc. + // It returns the Unix epoch with nine decimal places. + + uint64_t retval = 0; + +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + clock_gettime (CLOCK_REALTIME, &ts); + retval = ts.tv_sec * 1000000000 + ts.tv_nsec; + return retval; +#endif +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + gettimeofday (&tv, NULL); + retval = tv.tv_sec * 1000000000 + tv.tv_usec * 1000; + return retval; +#endif + return retval; +} diff --git a/gcc/cobol/genutil.h b/gcc/cobol/genutil.h index b2868f7..251b1c1 100644 --- a/gcc/cobol/genutil.h +++ b/gcc/cobol/genutil.h @@ -30,13 +30,6 @@ #ifndef _GENUTIL_H_ #define _GENUTIL_H_ -#define EBCDIC_MINUS (0x60) -#define EBCDIC_PLUS (0x4E) -#define EBCDIC_ZERO (0xF0) -#define EBCDIC_NINE (0xF9) - -bool internal_codeset_is_ebcdic(); - extern bool exception_location_active; extern bool skip_exception_processing; @@ -45,8 +38,6 @@ extern bool suppress_dest_depends; extern std::vector<std::string>current_filename; extern tree var_decl_exception_code; // int __gg__exception_code; -extern tree var_decl_exception_handled; // int __gg__exception_handled; -extern tree var_decl_exception_file_number; // int __gg__exception_file_number; extern tree var_decl_exception_file_status; // int __gg__exception_file_status; extern tree var_decl_exception_file_name; // const char *__gg__exception_file_name; extern tree var_decl_exception_statement; // const char *__gg__exception_statement; @@ -58,7 +49,6 @@ extern tree var_decl_exception_paragraph; // const char *__gg__exception_para extern tree var_decl_default_compute_error; // int __gg__default_compute_error; extern tree var_decl_rdigits; // int __gg__rdigits; -extern tree var_decl_odo_violation; // int __gg__odo_violation; extern tree var_decl_unique_prog_id; // size_t __gg__unique_prog_id; extern tree var_decl_entry_location; // This is for managing ENTRY statements @@ -87,15 +77,15 @@ extern tree var_decl_treeplet_3s; // SIZE_T_P , "__gg__treeplet_3 extern tree var_decl_treeplet_4f; // cblc_field_pp_type_node , "__gg__treeplet_4f" extern tree var_decl_treeplet_4o; // SIZE_T_P , "__gg__treeplet_4o" extern tree var_decl_treeplet_4s; // SIZE_T_P , "__gg__treeplet_4s" - extern tree var_decl_nop; // int __gg__nop extern tree var_decl_main_called; // int __gg__main_called +extern tree var_decl_entry_label; // void* __gg__entry_label int get_scaled_rdigits(cbl_field_t *field); int get_scaled_digits(cbl_field_t *field); tree tree_type_from_digits(size_t digits, int signable); tree tree_type_from_size(size_t bytes, int signable); -tree tree_type_from_field(cbl_field_t *field); + void get_binary_value( tree value, tree rdigits, cbl_field_t *field, @@ -104,10 +94,7 @@ void get_binary_value( tree value, tree get_data_address( cbl_field_t *field, tree offset); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -__int128 get_power_of_ten(int n); -#pragma GCC diagnostic pop +FIXED_WIDE_INT(128) get_power_of_ten(int n); void scale_by_power_of_ten_N(tree value, int N, bool check_for_fractional = false); @@ -124,9 +111,9 @@ void set_exception_code_func(ec_type_t ec, int line, int from_raise_statement=0); #define set_exception_code(ec) set_exception_code_func(ec, __LINE__) -bool process_this_exception(ec_type_t ec); +bool process_this_exception(const ec_type_t ec); #define CHECK_FOR_FRACTIONAL_DIGITS true -void get_integer_value(tree value, +void get_integer_value(tree value, // This is always a LONG cbl_field_t *field, tree offset=NULL, // size_t bool check_for_fractional_digits=false); @@ -136,7 +123,7 @@ void copy_little_endian_into_place(cbl_field_t *dest, tree value, int rhs_rdigits, bool check_for_error, - tree &size_error); + const tree &size_error); tree build_array_of_size_t( size_t N, const size_t *values); void parser_display_internal_field(tree file_descriptor, @@ -144,23 +131,14 @@ void parser_display_internal_field(tree file_descriptor, bool advance=DISPLAY_NO_ADVANCE); char *get_literal_string(cbl_field_t *field); -bool refer_is_clean(cbl_refer_t &refer); +bool refer_is_clean(const cbl_refer_t &refer); -tree refer_offset_source(cbl_refer_t &refer, - int *pflags=NULL); -tree refer_size_source(cbl_refer_t &refer); -tree refer_offset_dest(cbl_refer_t &refer); -tree refer_size_dest(cbl_refer_t &refer); +tree refer_offset(const cbl_refer_t &refer, + int *pflags=NULL); +tree refer_size_source(const cbl_refer_t &refer); +tree refer_size_dest(const cbl_refer_t &refer); -void REFER_CHECK( const char *func, - int line, - cbl_refer_t &refer - ); -#define refer_check(a) REFER_CHECK(__func__, __LINE__, a) - -tree qualified_data_source(cbl_refer_t &refer); - -tree qualified_data_dest(cbl_refer_t &refer); +tree qualified_data_location(const cbl_refer_t &refer); void build_array_of_treeplets( int ngroup, size_t N, @@ -169,4 +147,8 @@ void build_array_of_treeplets( int ngroup, void build_array_of_fourplets( int ngroup, size_t N, cbl_refer_t *refers); +void get_depending_on_value_from_odo(tree retval, cbl_field_t *odo); +uint64_t get_time_nanoseconds(); + + #endif diff --git a/gcc/cobol/inspect.h b/gcc/cobol/inspect.h index 9e86a0b..96399f5 100644 --- a/gcc/cobol/inspect.h +++ b/gcc/cobol/inspect.h @@ -53,181 +53,162 @@ static inline bool is_active( const cbl_refer_t& refer ) { return NULL != refer.field; } -template <typename DATA> -struct cbx_inspect_qual_t { +struct cbl_inspect_qual_t { bool initial; - DATA identifier_4; + cbl_refer_t identifier_4; - cbx_inspect_qual_t() : initial(false), identifier_4(DATA()) {} - cbx_inspect_qual_t( bool initial, const DATA& identifier_4 ) + cbl_inspect_qual_t() : initial(false), identifier_4(cbl_refer_t()) {} + cbl_inspect_qual_t( bool initial, const cbl_refer_t& identifier_4 ) : initial(initial), identifier_4(identifier_4) - { - //if( identifier_4.field ) yywarn("%s:qualifying field is '%s'", __func__, identifier_4.field->name); - } - cbx_inspect_qual_t( const cbx_inspect_qual_t& that ) + {} + cbl_inspect_qual_t( const cbl_inspect_qual_t& that ) : initial(that.initial) , identifier_4(that.identifier_4) - { - //if( identifier_4.field ) yywarn("%s:qualifying field is '%s'", __func__, identifier_4.field->name); - } + {} - cbx_inspect_qual_t& operator=( const cbx_inspect_qual_t& that ) { + cbl_inspect_qual_t& operator=( const cbl_inspect_qual_t& that ) { initial = that.initial; identifier_4 = that.identifier_4; - //if( identifier_4.field ) yywarn("%s:qualifying field is '%s'", __func__, identifier_4.field->name); return *this; } bool active() const { return is_active(identifier_4); } - - void clear() { - initial = false; - identifier_4.clear(); - } }; -typedef cbx_inspect_qual_t<cbl_refer_t> cbl_inspect_qual_t; - /* * Data for INSPECT X TALLYING Y FOR. Captures information for operands of * CHARACTERS/ALL/LEADING. The CHARACTERS/ALL/LEADING control is kept at the * next higher level, and may be repeated for each tally. * - * cbx_inspect_match_t::matching is not used with CHARACTERS + * cbl_inspect_match_t::matching is not used with CHARACTERS */ -template <typename DATA> -struct cbx_inspect_match_t { - DATA matching; // identifier-3/5 or literal-1/3 - cbx_inspect_qual_t<DATA> before, after; // phrase 1 - - cbx_inspect_match_t( - const DATA& matching = DATA(), - cbx_inspect_qual_t<DATA> before = cbx_inspect_qual_t<DATA>(), - cbx_inspect_qual_t<DATA> after = cbx_inspect_qual_t<DATA>() - ) - : matching(matching) + +class cbl_inspect_match_t { + friend void dump_inspect_match( const cbl_inspect_match_t& M ); + cbl_refer_t match; // identifier-3/5 or literal-1/3 + cbl_refer_t tally; // collected too soon, belongs to next phrase + public: + cbl_inspect_qual_t before, after; // phrase 1 + + cbl_inspect_match_t() {} + explicit + cbl_inspect_match_t( const cbl_refer_t& matching, + const cbl_inspect_qual_t& before = cbl_inspect_qual_t(), + const cbl_inspect_qual_t& after = cbl_inspect_qual_t() ) + : match(matching) , before(before) , after(after) {} // match all characters bool match_any() const { return !(before.active() || after.active()); } -}; -typedef cbx_inspect_match_t<cbl_refer_t> cbl_inspect_match_t; + void save_premature_tally( const cbl_refer_t& tally ) { + this->tally = tally; // put it here temporarily + } + cbl_refer_t premature_tally() { + if( !tally.field ) { std::swap(match, tally); } + return tally; + } + + const cbl_refer_t& matching( const cbl_refer_t& match ) { + return this->match = match; + } + const cbl_refer_t& matching() const { return match; } + + bool empty() const { + return !is_active(match) && !before.active() && !after.active(); + } +}; /* * Data for INSPECT X REPLACING. The CHARACTERS/ALL/LEADING/FIRST control is * kept at the next higher level, and may be repeated. */ -template <typename DATA> -struct cbx_inspect_replace_t : public cbx_inspect_match_t<DATA> { - DATA replacement; - - cbx_inspect_replace_t( const DATA& matching = DATA(), - const DATA& replacement = DATA() ) - : cbx_inspect_match_t<DATA>(matching) - , replacement(replacement) - {} - cbx_inspect_replace_t( const DATA& matching, - const DATA& replacement, - const cbx_inspect_qual_t<DATA>& before, - const cbx_inspect_qual_t<DATA>& after ) - : cbx_inspect_match_t<DATA>(matching, before, after) +struct cbl_inspect_replace_t : public cbl_inspect_match_t { + cbl_refer_t replacement; + + cbl_inspect_replace_t() {} + cbl_inspect_replace_t( const cbl_refer_t& matching, + const cbl_refer_t& replacement, + const cbl_inspect_qual_t& before, + const cbl_inspect_qual_t& after ) + : cbl_inspect_match_t(matching, before, after) , replacement(replacement) {} }; -typedef cbx_inspect_replace_t<cbl_refer_t> cbl_inspect_replace_t; - // One partial tally or substitution. -template <typename DATA> -struct cbx_inspect_oper_t { +struct cbl_inspect_oper_t { cbl_inspect_bound_t bound; // CHARACTERS/ALL/LEADING/FIRST - size_t n_identifier_3; // N matches/replaces - // either tallies or replaces is NULL - cbx_inspect_match_t<DATA> *matches; - cbx_inspect_replace_t<DATA> *replaces; + // either tallies or replaces is empty + std::vector<cbl_inspect_match_t> matches; + std::vector<cbl_inspect_replace_t> replaces; - cbx_inspect_oper_t( cbl_inspect_bound_t bound, - std::list<cbx_inspect_match_t<DATA>> matches ) + cbl_inspect_oper_t() : bound(bound_characters_e) {} + + explicit cbl_inspect_oper_t( const cbl_inspect_match_t& match, + cbl_inspect_bound_t bound = bound_characters_e ) : bound(bound) - , n_identifier_3( matches.size()) - , matches(NULL) - , replaces(NULL) - { - this->matches = new cbx_inspect_match_t<DATA>[n_identifier_3]; - std::copy( matches.begin(), matches.end(), this->matches ); - } - - cbx_inspect_oper_t( cbl_inspect_bound_t bound, - std::list<cbx_inspect_replace_t<DATA>> replaces ) + { + matches.push_back(match); + } + explicit cbl_inspect_oper_t( const cbl_inspect_replace_t& replace, + cbl_inspect_bound_t bound = bound_characters_e ) : bound(bound) - , n_identifier_3( replaces.size() ) - , matches(NULL) - , replaces(NULL) - { - this->replaces = new cbx_inspect_replace_t<DATA>[n_identifier_3]; - std::copy( replaces.begin(), replaces.end(), this->replaces ); - } - - cbx_inspect_oper_t() - : bound(bound_characters_e) - , n_identifier_3(0) - , matches(NULL) - , replaces(NULL) - { - assert( is_valid() ); - } - - bool is_valid() const { - if( matches && replaces ) return false; - if( matches || replaces ) return n_identifier_3 > 0; - return n_identifier_3 == 0; + { + replaces.push_back(replace); } -}; -typedef cbx_inspect_oper_t<cbl_refer_t> cbl_inspect_oper_t; + cbl_inspect_oper_t( cbl_inspect_bound_t bound, + const std::vector<cbl_inspect_match_t>& matches ) + : bound(bound) + , matches(matches) + {} -// One whole tally or substitution. For REPLACING, nbound == 1 -template <typename DATA> -struct cbx_inspect_t { - DATA tally; // identifier-2: NULL without a tally - size_t nbound; // Each FOR or REPLACING operation starts with a cbl_inspect_bound_t - cbx_inspect_oper_t<DATA> *opers; - - cbx_inspect_t( const DATA& tally = DATA() ) - : tally(tally) - , nbound(0) - , opers(NULL) + cbl_inspect_oper_t( cbl_inspect_bound_t bound, + const std::vector<cbl_inspect_replace_t>& replaces ) + : bound(bound) + , replaces(replaces) {} - cbx_inspect_t( const DATA& tally, cbx_inspect_oper_t<DATA> oper ) - : tally(tally) - , nbound(1) - , opers(NULL) - { - this->opers = new cbx_inspect_oper_t<DATA>[1]; - this->opers[0] = oper; - } - cbx_inspect_t( const DATA& tally, - const std::list<cbx_inspect_oper_t<DATA>>& opers ) - : tally(tally) - , nbound( opers.size() ) - , opers(NULL) - { - this->opers = new cbx_inspect_oper_t<DATA>[nbound]; - std::copy( opers.begin(), opers.end(), this->opers ); - } + + // N matches/replaces + size_t n_identifier_3() const { + return std::max( matches.size(), replaces.size() ); + } + + bool is_valid() const { // only one or the other, never both + bool invalid = !matches.empty() && !replaces.empty(); + return ! invalid; + } }; -typedef cbx_inspect_t<cbl_refer_t> cbl_inspect_t; +// One whole tally or substitution. For REPLACING, nbound == 1 +// FOR and REPLACING start with a cbl_inspect_bound_t +struct cbl_inspect_t : public std::vector<cbl_inspect_oper_t> { + cbl_refer_t tally; // field is NULL for REPLACING + cbl_inspect_t() {} + cbl_inspect_t( size_t n, const cbl_inspect_oper_t& oper ) + : std::vector<cbl_inspect_oper_t>(n, oper) + {} + cbl_inspect_t( const cbl_refer_t& tally, + const std::vector<cbl_inspect_oper_t>& opers ) + : std::vector<cbl_inspect_oper_t>(opers) + , tally(tally) + {} + + size_t nbound() const { return size(); } +}; +typedef std::vector<cbl_inspect_t> cbl_inspect_opers_t; /* * Runtime */ -void parser_inspect( cbl_refer_t input, bool backward, - size_t ninspect, cbl_inspect_t *inspects ); +void parser_inspect( const cbl_refer_t& input, + bool backward, + cbl_inspect_opers_t& inspects ); + void parser_inspect_conv( cbl_refer_t input, bool backward, cbl_refer_t original, cbl_refer_t replacement, diff --git a/gcc/cobol/lang-specs.h b/gcc/cobol/lang-specs.h index 78e84c0..0032b63 100644 --- a/gcc/cobol/lang-specs.h +++ b/gcc/cobol/lang-specs.h @@ -34,7 +34,7 @@ {".CBL", "@cobol", 0, 0, 0}, {"@cobol", "cobol1 %i %(cc1_options) " - "%{D*} %{E} %{I*} %{fmax-errors*} %{fsyntax-only*} " + "%{D*} %{E} %{I*} %{M} %{fmax-errors*} %{fsyntax-only*} " "%{fcobol-exceptions*} " "%{copyext} " "%{fstatic-call} %{fdefaultbyte} " @@ -42,6 +42,52 @@ "%{preprocess} " "%{dialect} " "%{include} " + "%{Wno-apply-commit} " + "%{Wno-file-code-set} " + "%{Wno-high-order-bit} " + "%{Wno-bad-line-directive} " + "%{Wno-bad-numeric} " + "%{Wno-binary-long-long} " + "%{Wno-call-giving} " + "%{Wno-cdf-dollar} " + "%{Wno-cdf-invalid-parameter} " + "%{Wno-cdf-name-not-found} " + "%{Wno-cobol-eject} " + "%{Wno-cobol-resume} " + "%{Wno-cobol-volatile} " + "%{Wno-comp-6} " + "%{Wno-comp-x} " + "%{Wno-copybook-found} " + "%{Wno-ec-unknown} " + "%{Wno-entry-convention} " + "%{Wno-iconv-error} " + "%{Wno-include-file-found} " + "%{Wno-include-file-not-found} " + "%{Wno-inspect-trailing} " + "%{Wno-length-of} " + "%{Wno-level-1-occurs} " + "%{Wno-level-78} " + "%{Wno-level-78-defined} " + "%{Wno-literal-concat} " + "%{Wno-locale-error} " + "%{Wno-move-corresponding} " + "%{Wno-move-pointer} " + "%{Wno-nllanginfo-error} " + "%{Wno-operator-space} " + "%{Wno-preprocessor-error} " + "%{Wno-procedure-not-found} " + "%{Wno-procedure-pointer} " + "%{Wno-replace-error} " + "%{Wno-returning-number} " + "%{Wno-segment-error} " + "%{Wno-segment-negative} " + "%{Wno-stop-number} " + "%{Wno-stray-indicator} " + "%{Wno-usage-typename} " + "%{Wno-recording-mode} " + "%{Wno-set-locale-to} " + "%{Wno-set-to-locale} " "%{nomain} " "%{!fsyntax-only:%(invoke_as)} " , 0, 0, 0}, + diff --git a/gcc/cobol/lang.opt b/gcc/cobol/lang.opt index 42c4020..bc56c01 100644 --- a/gcc/cobol/lang.opt +++ b/gcc/cobol/lang.opt @@ -38,87 +38,330 @@ Cobol Joined Separate ;; -I <dir> Add copybook search directory ; Documented in c.opt +M +Cobol +; Documented in c.opt + + dialect Cobol Joined Separate Enum(dialect_type) EnumBitSet Var(cobol_dialect) -Accept COBOL constructs used by non-ISO compilers +Accept COBOL constructs used by non-ISO compilers. Enum Name(dialect_type) Type(int) UnknownError(Unrecognized COBOL dialect name: %qs) EnumValue -Enum(dialect_type) String(gcc) Value(0x04) Canonical +Enum(dialect_type) String(iso) Value(0x100) EnumValue -Enum(dialect_type) String(ibm) Value(0x01) +Enum(dialect_type) String(gcc) Value(0x01) Canonical EnumValue -Enum(dialect_type) String(mf) Value(0x02) +Enum(dialect_type) String(ibm) Value(0x02) EnumValue -Enum(dialect_type) String(gnu) Value(0x04) +Enum(dialect_type) String(mf) Value(0x04) + +EnumValue +Enum(dialect_type) String(gnu) Value(0x08) fcobol-exceptions Cobol Joined Separate Var(cobol_exceptions) --fcobol-exceptions=<n> Enable some exceptions by default +-fcobol-exceptions=<n> Enable some exceptions by default. copyext Cobol Joined Separate Var(cobol_copyext) Init(0) -Define alternative implicit copybook filename extension +Define alternative implicit copybook filename extension. + +;; warnings + +; Par78CdfDefinedW +Wlevel-78-defined +Cobol Warning Var(level_78_defined, 1) Init(1) +Warn if CDF defines Level 78 constant. + +; MfBinaryLongLong +Wbinary-long-long +Cobol Warning Var(binary_long_long, 1) Init(1) +Warn if BINARY-LONG-LONG is used. + +; MfCallGiving +Wcall-giving +Cobol Warning Var(call_giving, 1) Init(1) +Warn if CALL ... GIVING is used. + +; MfCdfDollar +Wcdf-dollar +Cobol Warning Var(cdf_dollar, 1) Init(1) +Warn if CDF %<$IF%> is used. + +; MfComp6 +Wcomp-6 +Cobol Warning Var(comp_6, 1) Init(1) +Warn if COMPUTATIONAL-6 is used. + +; MfCompX +Wcomp-x +Cobol Warning Var(comp_x, 1) Init(1) +Warn if COMPUTATIONAL is used with PICTURE X. + +; MfTrailing +Winspect-trailing +Cobol Warning Var(inspect_trailing, 1) Init(1) +Warn if INSPECT ... TRAILING is used. + +; MfLevel_1_Occurs +Wlevel-1-occurs +Cobol Warning Var(level_1_occurs, 1) Init(1) +Warn if Level 01 is used with OCCURS. + +; MfLevel78 +Wlevel-78 +Cobol Warning Var(level_78, 1) Init(1) +Warn if Level 78 is used. + +; MfMovePointer +Wmove-pointer +Cobol Warning Var(move_pointer, 1) Init(1) +Warn if MOVE POINTER is used. + +; MfReturningNum +Wreturning-number +Cobol Warning Var(returning_number, 1) Init(1) +Warn if RETURNING <number> is used. + +; MfUsageTypename +Wusage-typename +Cobol Warning Var(usage_typename, 1) Init(1) +Warn if USAGE IS TYPENAME is used. + +; ParNumstrW +Wbad-numeric +Cobol Warning Var(bad_numeric, 1) Init(1) +Warn if numeric string is invalid. + +; CdfParameterW +Wcdf-invalid-parameter +Cobol Warning Var(cdf_invalid_parameter, 1) Init(1) +Warn if referenced CDF PARAMETER is not defined. + +; CdfNotFoundW +Wcdf-name-not-found +Cobol Warning Var(cdf_name_not_found, 1) Init(1) +Warn if referenced CDF name is not defined. + +; LexInputN +Wcopybook-found +Cobol Warning Var(copybook_found, 1) Init(1) +Print message when copybook is processed. + +; EcUnknownW +Wec-unknown +Cobol Warning Var(ec_unknown, 1) Init(1) +Warn if unimplemented/unknown exception condition is referenced. + +; ParInfoI +Wentry-convention +Cobol Warning Var(entry_convention, 1) Init(1) +Print message when ENTRY CONVENTION is specified. + +; LexIncludeOkN +Winclude-file-found +Cobol Warning Var(include_file_found, 1) Init(1) +Print message when include file is processed. + +; ParLiteral2W +Wliteral-concat +Cobol Warning Var(literal_concat, 1) Init(1) +Warn if concatenated literals use different encodings. + +; ParLocaleW +Wlocale-error +Cobol Warning Var(locale_error, 1) Init(1) +Warn if locale(3) fails. + +; ParNoCorrespondingW +Wmove-corresponding +Cobol Warning Var(warn_corresponding, 1) Init(1) +Warn if COBOL MOVE has no corresponding fields. + +; ParLangInfoW +Wnllanginfo-error +Cobol Warning Var(nllanginfo_error, 1) Init(1) +Warn if nlanglanginfo(3) fails. + +; IbmLengthOf +Wlength-of +Cobol Warning Var(cobol_length_of, 1) Init(1) +Warn if LENGTH OF is used. + +; IbmProcedurePointer +Wprocedure-pointer +Cobol Warning Var(procedure_pointer, 1) Init(1) +Warn if PROCEDURE POINTER is used. + +; IbmSectionSegmentW +Wsegment +Cobol Warning Var(cobol_segment, 1) Init(1) +Warn if SECTION segments are used. + +; IsoResume +Wcobol-resume +Cobol Warning Var(cobol_resume, 1) Init(1) +Warn if resume is used (instead of error for IBM). + +;; unimplemented syntax + +; SynApplyCommit +Wapply-commit +Cobol Warning Var(apply_commit, 1) Init(1) +Warn if APPLY COMMIT is used. + +; SynHighOrderBit +Whigh-order-bit +Cobol Warning Var(high_order_bit, 1) Init(1) +Warn if HIGH-ORDER-LEFT HIGH-ORDER-RIGHT is used. + +; SynFileCodeSet +Wfile-code-set +Cobol Warning Var(file_code_set, 1) Init(1) +Warn if FILE CODE SET is used. + +; SynRecordingMode +Wrecording-mode +Cobol Warning Var(recording_mode, 1) Init(1) +Warn if RECORDING MODE is used. + +; SynSetLocaleTo +Wset-locale-to +Cobol Warning Var(set_locale_to, 1) Init(1) +Warn if SET LOCALE ... TO is used. + +; SynSetToLocale +Wset-to-locale +Cobol Warning Var(set_to_locale, 1) Init(1) +Warn if SET ... TO LOCALE is used. + +;; errors to warnings + +; LexLineE +Wbad-line-directive +Cobol Warning Var(bad_line_directive, 1) Init(1) +Warn if a line directive is malformed (instead of error). + +; IbmEqualAssignE +Wequal-assign +Cobol Warning Var(equal_assign, 1) Init(1) +Warn if EQUAL used as assignment operator (instead of error). + +; ParIconvE +Wiconv-error +Cobol Warning Var(iconv_error, 1) Init(1) +Warn if iconv(3) cannot convert between encodings (instead of error). + +; LexIncludeE +Winclude-file-not-found +Cobol Warning Var(include_file_not_found, 1) Init(1) +Warn if include file is not found (instead of error). + +; LexPreprocessE +Wpreprocessor-error +Cobol Warning Var(preprocessor_error, 1) Init(1) +Warn if a preprocessor fails (instead of error). + +; ParUnresolvedProcE +Wprocedure-not-found +Cobol Warning Var(procedure_not_found, 1) Init(1) +Warn if a referenced procedure is not found (instead of error). + +; LexReplaceE +Wreplace-error +Cobol Warning Var(replace_error, 1) Init(1) +Warn if REPLACE cannot be processed (instead of error). + +; IbmSectionRangeE +Wsegment-error +Cobol Warning Var(segment_error, 1) Init(1) +Warn if a SEGMENT section is invalid (instead of error). + +; IbmSectionNegE +Wsegment-negative +Cobol Warning Var(segment_negative, 1) Init(1) +Warn if a SEGMENT range is negative (instead of error). + +; LexIndicatorE +Wstray-indicator +Cobol Warning Var(stray_indicator, 1) Init(1) +Warn if indicator column has no recognized meaning (instead of error). + +; LexSeparatorE +Woperator-space +Cobol Warning Var(operator_space, 1) Init(1) +Warn if relational operator not followed by space (instead of error). + +; IbmEjectE +Wcobol-eject +Cobol Warning Var(cobol_eject, 1) Init(1) +Warn if IBM-style EJECT is used (instead of error). + +; IbmStopNumber +Wstop-number +Cobol Warning Var(stop_number, 1) Init(1) +Warn if IBM-style STOP <number> is used (instead of error). + +; IbmVolatileE +Wcobol-volatile +Cobol Warning Var(cobol_volatile, 1) Init(1) +Warn if VOLATILE is used (instead of error if -dialect ibm). + +;; end error-suppression options fdefaultbyte Cobol RejectNegative Joined Separate UInteger Var(cobol_default_byte) -Set Working-Storage data items to the supplied value +Set Working-Storage data items to the supplied value. fflex-debug Cobol Var(yy_flex_debug, 1) Init(0) -Enable Cobol lex debugging +Enable Cobol lex debugging. ffixed-form Cobol RejectNegative Assume that the source file is fixed form. -fsyntax-only -Cobol RejectNegative -; Documented in c.opt - ffree-form Cobol RejectNegative Assume that the source file is free form. findicator-column Cobol RejectNegative Joined Separate UInteger Var(indicator_column) Init(0) IntegerRange(0, 8) --findicator-column=<n> Column after which Region A begins +-findicator-column=<n> Column after which Region A begins. finternal-ebcdic Cobol Var(cobol_ebcdic, 1) Init(0) --finternal-ebcdic Internal processing is in EBCDIC Code Page 1140 - -fmax-errors -Cobol Joined Separate -; Documented in C +-finternal-ebcdic Internal processing is in EBCDIC Code Page 1140. fstatic-call Cobol Var(cobol_static_call, 1) Init(1) -Enable/disable static linkage for CALL literals +Enable/disable static linkage for CALL literals. ftrace-debug Cobol Var(cobol_trace_debug, 1) Init(0) -Enable Cobol parser debugging +Enable Cobol parser debugging. fyacc-debug Cobol Var(yy_debug, 1) Init(0) -Enable Cobol yacc debugging +Enable Cobol yacc debugging. preprocess Cobol Joined Separate Var(cobol_preprocess) -preprocess <source_filter> before compiling +Preprocess <source_filter> before compiling. iprefix Cobol Joined Separate ; Documented in C include -Cobol Joined Separate Var(cobol_include) +Cobol Joined Separate ; Documented in C isysroot @@ -131,14 +374,14 @@ Cobol Joined Separate main Cobol --main The first program-id in the next source file is called by a generated main() entry point +-main The first program-id in the next source file is called by a generated main() entry point. main= Cobol Joined Var(cobol_main_string) --main=<source_file> source_file/PROGRAM-ID is called by the generated main() +-main=<source_file> source_file/PROGRAM-ID is called by the generated main(). nomain Cobol --nomain No main() function is created from COBOL source files +-nomain No main() function is created from COBOL source files. ; This comment is to ensure we retain the blank line above. diff --git a/gcc/cobol/lang.opt.urls b/gcc/cobol/lang.opt.urls index 6a5dc1c..e628ae5 100644 --- a/gcc/cobol/lang.opt.urls +++ b/gcc/cobol/lang.opt.urls @@ -8,19 +8,19 @@ UrlSuffix(gcc/Preprocessor-Options.html#index-D-1) ; duplicate: 'gcc/Overall-Options.html#index-E' I -UrlSuffix(gcc/Directory-Options.html#index-I) LangUrlSuffix_D(gdc/Directory-Options.html#index-I) +UrlSuffix(gcc/Directory-Options.html#index-I) LangUrlSuffix_D(gdc/Directory-Options.html#index-I) LangUrlSuffix_Algol68(ga68/Directory-options.html#index-I) + +M +UrlSuffix(gcc/Preprocessor-Options.html#index-M) LangUrlSuffix_D(gdc/Code-Generation.html#index-M) ffixed-form LangUrlSuffix_Fortran(gfortran/Fortran-Dialect-Options.html#index-ffixed-form) -fsyntax-only -UrlSuffix(gcc/Warning-Options.html#index-fsyntax-only) LangUrlSuffix_D(gdc/Warnings.html#index-fno-syntax-only) LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-fsyntax-only) - ffree-form LangUrlSuffix_Fortran(gfortran/Fortran-Dialect-Options.html#index-ffree-form) -fmax-errors -UrlSuffix(gcc/Warning-Options.html#index-fmax-errors) LangUrlSuffix_D(gdc/Warnings.html#index-fmax-errors) +preprocess +UrlSuffix(gcc/Overall-Options.html#index-preprocess) iprefix UrlSuffix(gcc/Directory-Options.html#index-iprefix) LangUrlSuffix_D(gdc/Directory-Options.html#index-iprefix) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-iprefix) diff --git a/gcc/cobol/lexio.cc b/gcc/cobol/lexio.cc index 82bacf2..58cd3ff 100644 --- a/gcc/cobol/lexio.cc +++ b/gcc/cobol/lexio.cc @@ -28,6 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" #include <ext/stdio_filebuf.h> #include "cobol-system.h" #include "cbldiag.h" @@ -37,15 +38,24 @@ extern int yy_flex_debug; -static struct { - bool first_file, explicitly; - int column, right_margin; - bool inference_pending() { - bool tf = first_file && !explicitly; - first_file = false; - return tf; +source_format_t& cdf_source_format(); + +void +source_format_t::infer( const char *bol, bool want_reference_format ) { + if( bol ) { + left = 7; + if( want_reference_format ) { + right = 73; + } } -} indicator = { true, false, 0, 0 }; + dbgmsg("%s:%d: %s format detected", __func__, __LINE__, + description()); +} + + +// public source format test functions +bool is_fixed_format() { return cdf_source_format().is_fixed(); } +bool is_reference_format() { return cdf_source_format().is_reffmt(); } static bool debug_mode = false; @@ -61,11 +71,10 @@ static bool debug_mode = false; */ static inline int left_margin() { - return indicator.column == 0? indicator.column : indicator.column - 1; + return cdf_source_format().left_margin(); } static inline int right_margin() { - return indicator.right_margin == 0? - indicator.right_margin : indicator.right_margin - 1; + return cdf_source_format().right_margin(); } /* @@ -74,22 +83,9 @@ static inline int right_margin() { * When setting back to 0 (free), the right margin is also reset to 0. */ void -cobol_set_indicator_column( int column ) -{ - indicator.explicitly = true; - if( column == 0 ) indicator.right_margin = 0; - if( column < 0 ) { - column = -column; - indicator.right_margin = 73; - } - indicator.column = column; -} +cobol_set_indicator_column( int column ); -bool is_fixed_format() { return indicator.column == 7; } -bool is_reference_format() { - return indicator.column == 7 && indicator.right_margin == 73; -} -bool include_debug() { return indicator.column == 7 && debug_mode; } +bool include_debug() { return is_fixed_format() && debug_mode; } bool set_debug( bool tf ) { return debug_mode = tf && is_fixed_format(); } static bool nonblank( const char ch ) { return !isblank(ch); } @@ -103,7 +99,7 @@ start_of_line( char *bol, char *eol ) { static inline char * continues_at( char *bol, char *eol ) { - if( indicator.column == 0 ) return NULL; // cannot continue in free format + if( cdf_source_format().is_free() ) return NULL; // cannot continue in free format bol += left_margin(); if( *bol != '-' ) return NULL; // not a continuation line return start_of_line(++bol, eol); @@ -112,8 +108,8 @@ continues_at( char *bol, char *eol ) { // Return pointer to indicator column. Test ch if provided. // NULL means no indicator column or tested value not present. static inline char * -indicated( char *bol, char *eol, char ch = '\0' ) { - if( indicator.column == 0 && *bol != '*' ) { +indicated( char *bol, const char *eol, char ch = '\0' ) { + if( cdf_source_format().left_margin() == 0 && *bol != '*' ) { return NULL; // no indicator column in free format, except for comments } gcc_assert(bol != NULL); @@ -129,10 +125,10 @@ indicated( char *bol, char *eol, char ch = '\0' ) { static char * remove_inline_comment( char *bol, char *eol ) { - static char ends = '\0'; char *nl = std::find(bol, eol, '\n'); if( bol < nl ) { + static char ends = '\0'; std::swap(*nl, ends); char *comment = strstr(bol, "*>"); if( comment ) { @@ -197,10 +193,10 @@ maybe_add_space(const span_t& pattern, replace_t& recognized) { } if( befter[0] == blank || befter[1] == blank ) { - char *s = xasprintf( "%s%.*s%s", - befter[0], - recognized.after.size(), recognized.after.p, - befter[1] ); + const char *s = xasprintf( "%s%.*s%s", + befter[0], + recognized.after.size(), recognized.after.p, + befter[1] ); recognized.after = span_t(s, s + strlen(s)); } } @@ -255,14 +251,17 @@ recognize_replacements( filespan_t mfile, std::list<replace_t>& pending_replacem span_t found(mfile.eodata, mfile.eodata); - if( regex_search( mfile.ccur(), (const char *)mfile.eodata, cm, re) ) { + if( regex_search( mfile.ccur(), + const_cast<const char *>(mfile.eodata), + cm, re) ) { gcc_assert(cm[1].matched); found = span_t( cm[1].first, cm[1].second ); if( yy_flex_debug ) { size_t n = count_newlines(mfile.data, found.p); - dbgmsg("%s:%d first '%.*s' is on line %zu (offset %zu)", __func__, __LINE__, + dbgmsg("%s:%d first '%.*s' is on line " HOST_SIZE_T_PRINT_UNSIGNED + " (offset " HOST_SIZE_T_PRINT_UNSIGNED ")", __func__, __LINE__, directive.before.size(), directive.before.p, - ++n, found.p - mfile.data); + (fmt_size_t)++n, (fmt_size_t)(found.p - mfile.data)); } } else { dbgmsg("%s:%d not found: '%s' in \n'%.*s'", __func__, __LINE__, @@ -289,9 +288,12 @@ recognize_replacements( filespan_t mfile, std::list<replace_t>& pending_replacem bol = next.found.pend; if( yy_flex_debug ) { - size_t n = std::count((const char *)mfile.data, recognized.before.p, '\n'); - dbgmsg( "%s:%d: line %zu @ %zu: '%s'\n/%.*s/%.*s/", __func__, __LINE__, - ++n, next.found.p - mfile.data, + size_t n = std::count(const_cast<const char *>(mfile.data), + recognized.before.p, '\n'); + dbgmsg( "%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED + " @ " HOST_SIZE_T_PRINT_UNSIGNED ": '%s'\n/%.*s/%.*s/", + __func__, __LINE__, + (fmt_size_t)++n, (fmt_size_t)(next.found.p - mfile.data), next.directive.before.p, int(recognized.before.size()), recognized.before.p, int(recognized.after.size()), recognized.after.p ); @@ -303,21 +305,85 @@ recognize_replacements( filespan_t mfile, std::list<replace_t>& pending_replacem next.found = span_t(mfile.eodata, mfile.eodata); regex re(next.directive.before.p, extended_icase); - if( regex_search(bol, (const char *)mfile.eodata, cm, re) ) { + if( regex_search(bol, const_cast<const char *>(mfile.eodata), cm, re) ) { gcc_assert(cm[1].matched); next.found = span_t( cm[1].first, cm[1].second ); - size_t n = std::count((const char *)mfile.data, next.found.p, '\n'); + size_t n = std::count(const_cast<const char *>(mfile.data), + next.found.p, '\n'); if( false ) - dbgmsg("%s:%d next '%.*s' will be on line %zu (offset %zu)", __func__, __LINE__, + dbgmsg("%s:%d next '%.*s' will be on line " HOST_SIZE_T_PRINT_UNSIGNED + " (offset " HOST_SIZE_T_PRINT_UNSIGNED ")", __func__, __LINE__, next.directive.before.size(), next.directive.before.p, - ++n, next.found.p - mfile.data); + (fmt_size_t)++n, (fmt_size_t)(next.found.p - mfile.data)); } pnext = std::min_element(futures.begin(), futures.end()); } } static void +check_push_pop_directive( filespan_t& mfile ) { + char eol = '\0'; + const char *p = std::find(mfile.cur, mfile.eol, '>'); + if( ! (p < mfile.eol && p[1] == *p ) ) return; + + const char pattern[] = + ">>[[:blank:]]*(push|pop)[[:blank:]]+" + "(" + "all|" + "call-convention|" + "cobol-words|" + "define|" + "source[[:blank:]]+format|" + "turn" + ")"; + static regex re(pattern, extended_icase); + + // show contents of marked subexpressions within each match + cmatch cm; + + std::swap(*mfile.eol, eol); // see implementation for excuses + bool ok = regex_search(p, const_cast<const char *>(mfile.eol), cm, re); + std::swap(*mfile.eol, eol); + + if( ok ) { + gcc_assert(cm.size() > 1); + bool push = TOUPPER(cm[1].first[1]) == 'U'; + switch( TOUPPER(cm[2].first[0]) ) { + case 'A': // ALL + push? cdf_push() : cdf_pop(); + break; + case 'C': + switch( TOUPPER(cm[2].first[1]) ) { + case 'A': // CALL-CONVENTION + push? cdf_push_call_convention() : cdf_pop_call_convention(); + break; + case 'O': // COBOL-WORDS + push? cdf_push_current_tokens() : cdf_pop_current_tokens(); + break; + default: + gcc_unreachable(); + } + break; + case 'D': // DEFINE + push? cdf_push_dictionary() : cdf_pop_dictionary(); + break; + case 'S': // SOURCE FORMAT + push? cdf_push_source_format() : cdf_pop_source_format(); + break; + case 'T': // TURN + push? cdf_push_enabled_exceptions() : cdf_pop_enabled_exceptions(); + break; + default: + gcc_unreachable(); + } + erase_line(const_cast<char*>(cm[0].first), + const_cast<char*>(cm[0].second)); + } +} + +static void check_source_format_directive( filespan_t& mfile ) { + char eol = '\0'; const char *p = std::find(mfile.cur, mfile.eol, '>'); if( ! (p < mfile.eol && p[1] == *p ) ) return; @@ -330,7 +396,12 @@ check_source_format_directive( filespan_t& mfile ) { // show contents of marked subexpressions within each match cmatch cm; - if( regex_search(p, (const char *)mfile.eol, cm, re) ) { + + std::swap(*mfile.eol, eol); // see implementation for excuses + bool ok = regex_search(p, const_cast<const char *>(mfile.eol), cm, re); + std::swap(*mfile.eol, eol); + + if( ok ) { gcc_assert(cm.size() > 1); switch( cm[3].length() ) { case 4: @@ -343,11 +414,14 @@ check_source_format_directive( filespan_t& mfile ) { gcc_assert(cm[3].length() == 4 || cm[3].length() == 5); break; } - mfile.cur = const_cast<char*>(cm[0].second); - dbgmsg( "%s:%d: %s format set, on line %zu", __func__, __LINE__, - indicator.column == 7? "FIXED" : "FREE", mfile.lineno() ); - erase_line(const_cast<char*>(cm[0].first), - const_cast<char*>(cm[0].second)); + + dbgmsg( "%s:%d: %s format set, on line " HOST_SIZE_T_PRINT_UNSIGNED, + __func__, __LINE__, + cdf_source_format().description(), + (fmt_size_t)mfile.lineno() ); + char *bol = cdf_source_format().is_fixed()? mfile.cur : const_cast<char*>(cm[0].first); + gcc_assert(cm[0].second <= mfile.eol); + erase_line(bol, const_cast<char*>(cm[0].second)); } } @@ -380,21 +454,28 @@ struct buffer_t : public bytespan_t { dbgmsg("flex input buffer: '%.*s'\n[xelf]", int(pos - data), data); } void dump() const { +#ifdef GETENV_OK if( getenv("lexer_input") ) show(); +#endif } }; -static bool -valid_sequence_area( const char *p, const char *eodata ) { - const char *pend = p + 6; - if ( eodata < pend ) return false; +static inline bool is_p( char ch ) { return TOUPPER(ch) == 'P'; } - for( ; p < pend; p++ ) { - if( ! (ISDIGIT(*p) || *p == SPACE) ) { - return false; +static bool +is_program_id( const char *p, const char *eol ) { + static const std::string program_id("PROGRAM-ID"); + auto eop = p + program_id.size(); + if( eop < eol ) { + // PROGRAM-ID must be followed by a dot, perhaps with intervening whitespace. + for( const char *dot=eop; dot < eol && *dot != '.'; dot++ ) { + if( !ISSPACE(*dot) ) return false; } + std::string line (p, eop); + std::transform(line.begin(), line.end(), line.begin(), ::toupper); + return line == program_id; } - return true; // characters either digits or blanks + return false; } const char * esc( size_t len, const char input[] ); @@ -434,9 +515,9 @@ struct replacing_term_t { bool matched, done; span_t leading_trailing, term, stmt; - replacing_term_t(const char input[]) : matched(false), done(false) { - stmt = span_t(input, input); - } + explicit replacing_term_t(const char input[]) + : matched(false), done(false), stmt(span_t(input, input)) + {} }; extern YYLTYPE yylloc; @@ -457,11 +538,11 @@ update_yylloc( const csub_match& stmt, const csub_match& term ) { class dump_loc_on_exit { public: dump_loc_on_exit() { - if( getenv( "update_yylloc" ) ) + if( gcobol_getenv( "update_yylloc" ) ) location_dump( "update_yylloc", __LINE__, "begin", yylloc); } ~dump_loc_on_exit() { - if( getenv( "update_yylloc" ) ) + if( gcobol_getenv( "update_yylloc" ) ) location_dump( "update_yylloc", __LINE__, "end ", yylloc); } } dloe; @@ -506,7 +587,7 @@ update_yylloc( const csub_match& stmt, const csub_match& term ) { static replacing_term_t parse_replacing_term( const char *stmt, const char *estmt ) { - gcc_assert(stmt); gcc_assert(estmt); gcc_assert(stmt < estmt); + gcc_assert(stmt); gcc_assert(estmt); gcc_assert(stmt <= estmt); replacing_term_t output(stmt); static const char pattern[] = @@ -600,7 +681,8 @@ parse_replacing_term( const char *stmt, const char *estmt ) { } if( extraneous_replacing ) { update_yylloc( cm[0], cm[8] ); - yywarn("syntax error: invalid '%.*s'", cm[8].length(), cm[8].first); + cbl_message(LexReplaceE, "syntax error: invalid '%.*s'", + cm[8].length(), cm[8].first); output.matched = false; return output; } @@ -716,13 +798,13 @@ parse_replacing_pair( const char *stmt, const char *estmt ) { } } if( pair.stmt.p ) { - yywarn("CDF syntax error '%*s'", (int)pair.stmt.size(), pair.stmt.p); + cbl_message(LexReplaceE, "LEX syntax error '%.*s'", (int)pair.stmt.size(), pair.stmt.p); } else { // This eliminated a compiler warning about "format-overflow" - yywarn("CDF syntax error"); + cbl_message(LexReplaceE, "LEX syntax error"); } - pair.stmt = span_t(0UL, stmt); + pair.stmt = span_t(size_t(0), stmt); pair.replace = replace_t(); } return pair; @@ -762,9 +844,9 @@ parse_replace_pairs( const char *stmt, const char *estmt, bool is_copy_stmt ) { // Report findings. if( false && yy_flex_debug ) { for( size_t i=0; i < cm.size(); i++ ) { - dbgmsg("%s: %s %zu: '%.*s'", __func__, + dbgmsg("%s: %s " HOST_SIZE_T_PRINT_UNSIGNED ": '%.*s'", __func__, cm[i].matched? "Pair" : "pair", - i, + (fmt_size_t)i, cm[i].matched? int(cm[i].length()) : 0, cm[i].matched? cm[i].first : ""); } @@ -784,7 +866,7 @@ parse_replace_pairs( const char *stmt, const char *estmt, bool is_copy_stmt ) { } span_t& before(parsed.replace.before); - span_t& after(parsed.replace.after); + const span_t& after(parsed.replace.after); const char *befter[2] = { nonword_ch, nonword_ch }; gcc_assert(before.p < before.pend); @@ -823,9 +905,11 @@ parse_replace_pairs( const char *stmt, const char *estmt, bool is_copy_stmt ) { } if( yy_flex_debug ) { - dbgmsg( "%s:%d: %s: %zu pairs parsed from '%.*s'", __func__, __LINE__, - parsed.done()? "done" : "not done", - pairs.size(), parsed.stmt.size(), parsed.stmt.p ); + dbgmsg( "%s:%d: %s: " HOST_SIZE_T_PRINT_UNSIGNED " pairs parsed from '%.*s'", + __func__, __LINE__, + parsed.done() ? "done" : "not done", + (fmt_size_t)pairs.size(), parsed.stmt.size(), + parsed.stmt.size() ? parsed.stmt.p : "" ); int i = 0; for( const auto& replace : pairs ) { dbgmsg("%s:%d:%4d: '%s' => '%s'", __func__, __LINE__, @@ -851,7 +935,7 @@ struct copy_descr_t { }; static YYLTYPE -location_in( const filespan_t& mfile, const csub_match cm ) { +location_in( const filespan_t& mfile, const csub_match& cm ) { YYLTYPE loc { int(mfile.lineno() + 1), int(mfile.colno() + 1), int(mfile.lineno() + 1), int(mfile.colno() + 1) @@ -859,7 +943,7 @@ location_in( const filespan_t& mfile, const csub_match cm ) { gcc_assert(mfile.cur <= cm.first && cm.second <= mfile.eodata); auto nline = std::count(cm.first, cm.second, '\n'); if( nline ) { - gcc_assert(loc.first_line < nline); + gcc_assert(nline < loc.first_line); loc.first_line -= nline; auto p = static_cast<const char*>(memrchr(cm.first, '\n', cm.length())); loc.last_column = (cm.second) - p; @@ -902,14 +986,15 @@ parse_copy_directive( filespan_t& mfile ) { copy_stmt.p = mfile.eodata; if( regex_search(mfile.ccur(), - (const char *)mfile.eodata, cm, re) ) { + const_cast<const char *>(mfile.eodata), cm, re) ) { copy_stmt = span_t( cm[0].first, cm[0].second ); if( yy_flex_debug ) { size_t nnl = 1 + count_newlines(mfile.data, copy_stmt.p); size_t nst = 1 + count_newlines(copy_stmt.p, copy_stmt.pend); - dbgmsg("%s:%d: line %zu: COPY directive is %zu lines '%.*s'", + dbgmsg("%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED + ": COPY directive is " HOST_SIZE_T_PRINT_UNSIGNED " lines '%.*s'", __func__, __LINE__, - nnl, nst, copy_stmt.size(), copy_stmt.p); + (fmt_size_t)nnl, (fmt_size_t)nst, copy_stmt.size(), copy_stmt.p); } } } @@ -922,7 +1007,8 @@ parse_copy_directive( filespan_t& mfile ) { outcome.partial_line = span_t(mfile.cur, copy_stmt.p); if( yy_flex_debug ) { - dbgmsg("%zu expressions", std::count(pattern, pattern + sizeof(pattern), '(')); + dbgmsg(HOST_SIZE_T_PRINT_UNSIGNED " expressions", + (fmt_size_t)std::count(pattern, pattern + sizeof(pattern), '(')); int i = 0; for( const auto& m : cm ) { if( m.matched ) @@ -953,7 +1039,7 @@ parse_copy_directive( filespan_t& mfile ) { std::pair<std::list<replace_t>, char*> result = parse_replace_pairs( cm[0].second, mfile.eodata, true ); - std::list<replace_t>& replacements(result.first); + const std::list<replace_t>& replacements(result.first); outcome.parsed = (outcome.nreplace = replacements.size()) > 0; if( outcome.parsed ) { replace_directives.push(replacements); @@ -980,7 +1066,7 @@ parse_copy_directive( filespan_t& mfile ) { } static char * -parse_replace_last_off( filespan_t& mfile ) { +parse_replace_last_off( const filespan_t& mfile ) { static const char pattern[] = "REPLACE" "[[:space:]]+" "(LAST[[:space:]]+)?OFF[[:space:]]*[.]" @@ -990,7 +1076,7 @@ parse_replace_last_off( filespan_t& mfile ) { // REPLACE [LAST] OFF? bool found = regex_search(mfile.ccur(), - (const char *)mfile.eodata, cm, re); + const_cast<const char *>(mfile.eodata), cm, re); gcc_assert(found); // caller ensures gcc_assert(cm.size() == 2); @@ -1006,8 +1092,9 @@ parse_replace_last_off( filespan_t& mfile ) { } } - dbgmsg( "%s:%d: line %zu: parsed '%.*s', ", __func__, __LINE__, - mfile.lineno(), int(cm[0].length()), cm[0].first ); + dbgmsg( "%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED ": parsed '%.*s', ", + __func__, __LINE__, + (fmt_size_t)mfile.lineno(), int(cm[0].length()), cm[0].first ); // Remove statement from input erase_line(const_cast<char*>(cm[0].first), @@ -1039,20 +1126,23 @@ parse_replace_text( filespan_t& mfile ) { gcc_assert(mfile.line_length() > 2); if( pend[-1] == '\n' ) pend -= 2; auto len = int(pend - mfile.cur); - dbgmsg("%s:%d: line %zu: parsing '%.*s", __func__, __LINE__, - current_lineno, len, mfile.cur); + dbgmsg("%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED ": parsing '%.*s", + __func__, __LINE__, + (fmt_size_t)current_lineno, len, mfile.cur); } - if( ! regex_search(mfile.ccur(), (const char *)mfile.eodata, cm, re) ) { - dbgmsg( "%s:%d: line %zu: not a REPLACE statement:\n'%.*s'", - __func__, __LINE__, current_lineno, + if( ! regex_search(mfile.ccur(), mfile.eodata, cm, re) ) { + dbgmsg( "%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED + ": not a REPLACE statement:\n'%.*s'", + __func__, __LINE__, (fmt_size_t)current_lineno, int(mfile.line_length()), mfile.cur ); return span_t(); } // Report findings. if( yy_flex_debug ) { - dbgmsg("%zu expressions", std::count(pattern, pattern + sizeof(pattern), '(')); + dbgmsg(HOST_SIZE_T_PRINT_UNSIGNED " expressions", + (fmt_size_t)std::count(pattern, pattern + sizeof(pattern), '(')); int i = 0; for( const auto& m : cm ) { if( m.matched ) @@ -1077,12 +1167,14 @@ parse_replace_text( filespan_t& mfile ) { std::pair<std::list<replace_t>, char*> result = parse_replace_pairs(replace_stmt.p, replace_stmt.pend, false); - std::list<replace_t>& replacements(result.first); + const std::list<replace_t>& replacements(result.first); replace_directives.push( replacements ); if( yy_flex_debug ) { - dbgmsg( "%s:%d: line %zu: %zu pairs parsed from '%.*s'", __func__, __LINE__, - current_lineno, replacements.size(), int(replace_stmt.size()), replace_stmt.p ); + dbgmsg( "%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED ": " HOST_SIZE_T_PRINT_UNSIGNED + " pairs parsed from '%.*s'", __func__, __LINE__, + (fmt_size_t)current_lineno, (fmt_size_t)replacements.size(), + int(replace_stmt.size()), replace_stmt.p ); for( const auto& replace : replacements ) { int i = 0; dbgmsg("%s:%d:%4d: '%s' => '%s'", __func__, __LINE__, @@ -1113,7 +1205,7 @@ parse_replace_directive( filespan_t& mfile ) { next_directive = mfile.eodata; if( regex_search(mfile.ccur(), - (const char *)mfile.eodata, cm, re) ) { + const_cast<const char *>(mfile.eodata), cm, re) ) { gcc_assert(cm[1].matched); next_directive = cm[0].first; @@ -1162,8 +1254,9 @@ bytespan_t::append( const char *input, const char *eoinput ) { #if LEXIO auto nq = std::count_if(data, eodata, isquote); dbgmsg("%s:%3d: input ------ '%.*s'", __func__, __LINE__, int(eoinput - input), input); - dbgmsg("%s:%3d: precondition '%.*s' (%zu: %s)", __func__, __LINE__, - int(size()), data, nq, in_string()? "in string" : "not in string"); + dbgmsg("%s:%3d: precondition '%.*s' (" HOST_SIZE_T_PRINT_UNSIGNED ": %s)", + __func__, __LINE__, + int(size()), data, (fmt_size_t)nq, in_string()? "in string" : "not in string"); #endif if( !in_string() ) { // Remove trailing space unless it's part of a literal. while(data < eodata && ISSPACE(eodata[-1])) eodata--; @@ -1288,13 +1381,13 @@ lexer_input( char buf[], int max_size, FILE *input ) { for( auto p = mfile.cur; p < next; *output.pos++ = *p++ ) { static bool at_bol = false; if( at_bol ) { - auto nonblank = std::find_if( p, next, + auto nonblank_l = std::find_if( p, next, []( char ch ) { return !isblank(ch); } ); - if( nonblank + 1 < next ) { - if( *nonblank == '\r' ) nonblank++; // Windows - if( *nonblank == '\n' ) { - p = nonblank; + if( nonblank_l + 1 < next ) { + if( *nonblank_l == '\r' ) nonblank_l++; // Windows + if( *nonblank_l == '\n' ) { + p = nonblank_l; continue; } } @@ -1374,7 +1467,8 @@ preprocess_filter_add( const char input[] ) { auto filename = find_filter(filter.c_str()); if( !filename ) { - yywarn("preprocessor '%s/%s' not found", getcwd(NULL, 0), filter); + cbl_message(LexPreprocessE, "preprocessor '%s/%s' not found", + getcwd(NULL, 0), filter.c_str()); return false; } preprocessor_filters.push_back( std::make_pair(xstrdup(filename), options) ); @@ -1385,22 +1479,22 @@ void cdftext::echo_input( int input, const char filename[] ) { int fd; if( -1 == (fd = dup(input)) ) { - yywarn( "could not open preprocessed file %s to echo to standard output", - filename ); + cbl_message(LexPreprocessE, "could not open preprocessed file " + "%s to echo to standard output", filename ); return; } auto mfile = map_file(fd); if( -1 == write(STDOUT_FILENO, mfile.data, mfile.size()) ) { - yywarn( "could not write preprocessed file %s to standard output", + cbl_message(LexPreprocessE, "could not write preprocessed file %s to standard output", filename ); } if( -1 == munmap(mfile.data, mfile.size()) ) { - yywarn( "could not release mapped file" ); + cbl_message(LexPreprocessE, "could not release mapped file" ); } if( -1 == close(fd) ) { - yywarn( "could not close mapped file" ); + cbl_message(LexPreprocessE, "could not close mapped file" ); } } @@ -1420,18 +1514,20 @@ cdftext::lex_open( const char filename[] ) { int output = open_output(); - // Process any files supplied by the -include comamnd-line option. + // Process any files supplied by the -include command-line option. for( auto name : included_files ) { - int input; if( -1 == (input = open(name, O_RDONLY)) ) { - yyerrorvl(1, "", "cannot open -include file %s", name); + cbl_message(LexIncludeE, "cannot open %<-include%> file %qs", name); continue; } cobol_filename(name, inode_of(input)); filespan_t mfile( free_form_reference_format( input ) ); process_file( mfile, output ); + + cobol_filename_restore(); // process_file restores only for COPY } + included_files.clear(); cobol_filename(filename, inode_of(input)); filespan_t mfile( free_form_reference_format( input ) ); @@ -1453,7 +1549,7 @@ cdftext::lex_open( const char filename[] ) { argv[0] = filter; auto last_argv = std::transform( options.begin(), options.end(), argv.begin() + 1, - []( std::string& opt ) { + []( const std::string& opt ) { return xstrdup(opt.c_str()); } ); *last_argv = NULL; @@ -1475,18 +1571,18 @@ cdftext::lex_open( const char filename[] ) { } int erc; if( -1 == (erc = execv(filter, argv.data())) ) { - yywarn("could not execute %s", filter); + cbl_message(LexPreprocessE, "could not execute %s", filter); } _exit(erc); } int status; auto kid = wait(&status); gcc_assert(pid == kid); - if( kid == -1 ) cbl_err( "failed waiting for pid %d", pid); + if( kid == -1 ) cbl_err( "failed waiting for pid %ld", static_cast<long>(pid)); if( WIFSIGNALED(status) ) { - cbl_errx( "%s pid %d terminated by %s", - filter, kid, strsignal(WTERMSIG(status)) ); + cbl_errx( "%s pid %ld terminated by %s", + filter, static_cast<long>(kid), strsignal(WTERMSIG(status)) ); } if( WIFEXITED(status) ) { if( (status = WEXITSTATUS(status)) != 0 ) { @@ -1494,7 +1590,7 @@ cdftext::lex_open( const char filename[] ) { filter, status); } } - yywarn( "applied %s", filter ); + cbl_message(LexIncludeOkN, "applied %s", filter ); } return fdopen( output, "r"); @@ -1504,13 +1600,13 @@ int cdftext::open_input( const char filename[] ) { int fd = open(filename, O_RDONLY); if( fd == -1 ) { - dbgmsg( "could not open '%s': %m", filename ); + dbgmsg( "could not open '%s': %s", filename, xstrerror(errno) ); } verbose_file_reader = NULL != getenv("GCOBOL_TEMPDIR"); if( verbose_file_reader ) { - yywarn("verbose: opening %s for input", filename); + cbl_message(LexInputN, "verbose: opening %s for input", filename); } return fd; } @@ -1518,9 +1614,9 @@ cdftext::open_input( const char filename[] ) { int cdftext::open_output() { char *name = getenv("GCOBOL_TEMPDIR"); - int fd; if( name && 0 != strcmp(name, "/") ) { + int fd; char * stem = xasprintf("%sXXXXXX", name); if( -1 == (fd = mkstemp(stem)) ) { cbl_err( "could not open temporary file '%s' (%s)", @@ -1561,8 +1657,8 @@ cdftext::map_file( int fd ) { cbl_err( "%s: could not prepare map file from FIFO %d", __func__, input); } - if( false ) dbgmsg("%s: copied %ld bytes from FIFO", - __func__, nout); + if( false ) dbgmsg("%s: copied " HOST_SIZE_T_PRINT_DEC " bytes from FIFO", + __func__, (fmt_size_t)nout); } } } while( S_ISFIFO(sb.st_mode) ); @@ -1585,6 +1681,54 @@ cdftext::map_file( int fd ) { bool lexio_dialect_mf(); +/* + * A valid sequence area is 6 digits or blanks at the begining of the line that + * contains PROGRAM-ID. Return NULL if no valid sequence area, else return + * pointer to BOL. + */ +static const char * +valid_sequence_area( const char *data, const char *eodata ) { + + for( const char *p = data; + (p = std::find_if(p, eodata, is_p)) != eodata; + p++ ) + { + auto eol = std::find(p, eodata, '\n'); + if( p == data || ISSPACE(p[-1]) ) { + if( is_program_id(p, eol) ) { // found program-id token + const char *bol = p; + for( ; data <= bol-1 && bol[-1] != '\n'; --bol ) + ; + if( 6 < p - bol ) { + if( std::all_of(bol, bol+6, ::isdigit) ) { + return bol; + } + if( std::all_of(bol, bol+6, ::isblank) ) { + return bol; + } + break; + } + } + } + } + return nullptr; +} + +/* + * Reference Format -- valid COBOL between columns 8 and 72 -- has data after + * column 72 on the PROGRAM-ID line. Extended Reference Format (that allows + * longer lines) has no reason to follow the PROGRAM-ID with more stuff. + */ +static bool +infer_reference_format( const char *bol, const char *eodata ) { + assert(bol); + auto eol = std::find(bol, eodata, '\n'); + if( 72 < eol - bol ) { + return ! std::all_of(bol + 72, eol, ::isspace); + } + return false; +} + filespan_t cdftext::free_form_reference_format( int input ) { filespan_t source_buffer = map_file(input); @@ -1599,32 +1743,21 @@ cdftext::free_form_reference_format( int input ) { size_t lineno; bytespan_t line; // construct with length zero - current_line_t( char data[] ) : lineno(0), line(data, data) {} + explicit current_line_t( char data[] ) : lineno(0), line(data, data) {} } current( mfile.data ); /* - * If the format is not explicitly set on the command line, test the - * first 6 bytes of the first file to determine the format - * heuristically. If the first 6 characters are only digits or - * blanks, then the file is in fixed format. + * Infer source code format. */ - - if( indicator.inference_pending() ) { - const char *p = mfile.data; - while( p < mfile.eodata ) { - const char * pend = - std::find(p, const_cast<const char *>(mfile.eodata), '\n'); - if( 6 < pend - p ) break; - p = pend; - if( p < mfile.eodata) p++; + if( cdf_source_format().inference_pending() ) { + const char *bol = valid_sequence_area(mfile.data, mfile.eodata); + if( bol ) { + cdf_source_format().infer( bol, infer_reference_format(bol, mfile.eodata) ); } - if( valid_sequence_area(p, mfile.eodata) ) indicator.column = 7; - - dbgmsg("%s:%d: %s format detected", __func__, __LINE__, - indicator.column == 7? "FIXED" : "FREE"); } while( mfile.next_line() ) { + check_push_pop_directive(mfile); check_source_format_directive(mfile); remove_inline_comment(mfile.cur, mfile.eol); @@ -1745,15 +1878,15 @@ cdftext::free_form_reference_format( int input ) { void cdftext::process_file( filespan_t mfile, int output, bool second_pass ) { static size_t nfiles = 0; - std::list<replace_t> replacements; - __gnu_cxx::stdio_filebuf<char> outbuf(fdopen(output, "w"), std::ios::out); + __gnu_cxx::stdio_filebuf<char> outbuf(fdopen(output, "a"), std::ios::out); std::ostream out(&outbuf); std::ostream_iterator<char> ofs(out); // indicate current file static const char file_push[] = "\f#FILE PUSH ", file_pop[] = "\f#FILE POP\f"; + if( !included_files.empty() ) { ++nfiles; }; // force push/pop of included filename if( !second_pass && nfiles++ ) { static const char delimiter[] = "\f"; const char *filename = cobol_filename(); @@ -1772,8 +1905,8 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) { std::copy_if(copied.erased_lines.p, copied.erased_lines.pend, ofs, []( char ch ) { return ch == '\n'; } ); struct { int in, out; filespan_t mfile; } copy; - dbgmsg("%s:%d: line %zu, opening %s on fd %d", __func__, __LINE__, - mfile.lineno(), + dbgmsg("%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED ", opening %s on fd %d", + __func__, __LINE__, (fmt_size_t)mfile.lineno(), copybook.source(), copybook.current()->fd); copy.in = copybook.current()->fd; copy.mfile = free_form_reference_format( copy.in ); @@ -1809,31 +1942,12 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) { continue; // No active REPLACE directive. } - std::list<span_t> segments = segment_line(mfile); // no replace yields - // // 1 segment + std::list<span_t> segments = segment_line(mfile); for( const auto& segment : segments ) { std::copy(segment.p, segment.pend, ofs); } - if( segments.size() == 2 ) { - struct { - size_t before, after; - int delta() const { return before - after; } } nlines; - nlines.before = std::count(segments.front().p, - segments.front().pend, '\n'); - nlines.after = std::count(segments.back().p, segments.back().pend, '\n'); - if( nlines.delta() < 0 ) { - yywarn("line %zu: REPLACED %zu lines with %zu lines, " - "line count off by %d", mfile.lineno(), - nlines.before, nlines.after, nlines.delta()); - } - int nnl = nlines.delta(); - while( nnl-- > 0 ) { - static const char nl[] = "\n"; - std::copy(nl, nl + 1, ofs); - } - } out.flush(); } // end of file @@ -1841,6 +1955,7 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) { std::copy(file_pop, file_pop + strlen(file_pop), ofs); out.flush(); } + if( !included_files.empty() ) { --nfiles; }; } std::list<span_t> @@ -1856,12 +1971,30 @@ cdftext::segment_line( filespan_t& mfile ) { return output; } + /* + * If the replacement changes the number of lines in the replaced text, we + * need to reset the line number, because the next statement is on a + * different line in the manipulated text than in the original. Before each + * replacement, set the original line number. After each replacement, set + * the line number after the elided text on the next line. + */ for( const replace_t& segment : pending ) { gcc_assert(mfile.cur <= segment.before.p); gcc_assert(segment.before.pend <= mfile.eodata); + struct { unsigned long ante, post; } lineno = { + gb4(mfile.lineno()), gb4(mfile.lineno() + segment.after.nlines()) + }; + const char *directive = lineno.ante == lineno.post? + nullptr : xasprintf("\n#line %lu \"%s\"\n", + lineno.ante, cobol_filename()); + + if( directive ) + output.push_back( span_t(strlen(directive), directive) ); output.push_back( span_t(mfile.cur, segment.before.p) ); output.push_back( span_t(segment.after.p, segment.after.pend ) ); + if( directive ) + output.push_back( span_t(strlen(directive), directive) ); mfile.cur = const_cast<char*>(segment.before.pend); } @@ -1877,5 +2010,3 @@ cdftext::segment_line( filespan_t& mfile ) { return output; } - -//////// End of the cdf_text.h file diff --git a/gcc/cobol/lexio.h b/gcc/cobol/lexio.h index cf7f53a..ba4ef0a 100644 --- a/gcc/cobol/lexio.h +++ b/gcc/cobol/lexio.h @@ -43,7 +43,6 @@ #define SPACE ' ' bool lexer_echo(); - bool is_reference_format(); static inline bool isquote( char ch ) { @@ -70,7 +69,9 @@ erase_source( char *src, char *esrc ) { struct bytespan_t { char *data, *eodata; - bytespan_t( char *data = NULL, char *eodata = NULL ) + bytespan_t() : data( nullptr), eodata(nullptr) {} + + bytespan_t( char *data, char *eodata ) : data(data), eodata(eodata) { if( eodata < data ) { @@ -111,19 +112,7 @@ struct bytespan_t { } }; -/* Location type. Borrowed from parse.h as generated by Bison. */ -#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED -typedef struct YYLTYPE YYLTYPE; -struct YYLTYPE -{ - int first_line; - int first_column; - int last_line; - int last_column; -}; -# define YYLTYPE_IS_DECLARED 1 -# define YYLTYPE_IS_TRIVIAL 1 -#endif +// YYLTYPE supplied by cbldiag.h. Borrowed from parse.h as generated by Bison. struct filespan_t : public bytespan_t { char *cur, *eol, *quote; @@ -137,7 +126,7 @@ struct filespan_t : public bytespan_t { {} filespan_t(void *p, size_t len) : bytespan_t( static_cast<char*>(p), static_cast<char*>(p) + len ) - , cur(data), eol(data), quote(NULL), iline(0), line_quote72(0) + , cur(data), eol(data), quote(NULL), iline(0), icol(0), line_quote72(0) {} size_t lineno() const { return iline; } @@ -237,6 +226,7 @@ struct span_t { span_t( const char *data, const char *eodata ) : p(data), pend(eodata) { verify(); } + // cppcheck-suppress operatorEqRetRefThis span_t& operator=( const csub_match& cm ) { p = cm.first; pend = cm.second; @@ -245,6 +235,8 @@ struct span_t { int size() const { return pend - p; } + size_t nlines() const { return p && pend? std::count(p, pend, '\n') : 0; } + span_t dup() const { auto output = new char[size() + 1]; auto eout = std::copy(p, pend, output); @@ -252,15 +244,22 @@ struct span_t { return span_t(output, eout); } const char * has_nul() const { - auto p = std::find(this->p, pend, '\0'); - return p != pend? p : NULL; + auto p_l = std::find(this->p, pend, '\0'); + return p_l != pend? p_l : NULL; + } + + bool at_eol() const { + return p < pend && '\n' == pend[-1]; + } + const char * optional_eol() const { + return at_eol() ? "" : "\n"; } }; struct replace_t { struct span_t before, after; - replace_t( span_t before = span_t(), - span_t after = span_t() ) + replace_t() : before(span_t()), after(span_t()) {} + replace_t( span_t before, span_t after ) : before(before), after(after) {} replace_t& reset() { diff --git a/gcc/cobol/messages.cc b/gcc/cobol/messages.cc new file mode 100644 index 0000000..423b53a --- /dev/null +++ b/gcc/cobol/messages.cc @@ -0,0 +1,388 @@ +/* + * 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. + */ + +/* + * Define a table of diagnositic messages, each uniquely identified and + * grouped into dialects. The user can select on the command line which + * ones are in effect. + */ + +#include <cobol-system.h> +#include <coretypes.h> +#include <tree.h> +#undef yy_flex_debug + +#include <langinfo.h> + +#include <coretypes.h> +#include <version.h> +#include <demangle.h> +#include <intl.h> +#include <backtrace.h> +#include <diagnostic.h> +#include <opts.h> +#include "util.h" + +#include "cbldiag.h" +#include "cdfval.h" +#include "lexio.h" + +#include "../../libgcobol/ec.h" +#include "../../libgcobol/common-defs.h" +#include "symbols.h" +#include "inspect.h" +#include "../../libgcobol/io.h" +#include "genapi.h" +#include "genutil.h" +#include "../../libgcobol/charmaps.h" + + + +/* + * As of now, every diagnositc has one id, one message, one kind, and is + * associated with "one" dialect. The dialect could be ORed. If it is, that + * means among the dialects it belongs to, it is always of the same kind. + * + * The diagnositic mask in force during compilation may include/exclude + * features based on their associated dialect and/or by id. It may stipulate + * that a warning is treated as an error, too, but that's up the diagnostic + * framework. If a feature requires a dialect and is not specifically enabled, + * gcobol emits of message of the associated kind, and names the dialect + * required. + */ +struct cbl_diag_t { + cbl_diag_id_t id; + cbl_name_t option; + diagnostics::kind kind; + cbl_dialect_t dialect; + + explicit cbl_diag_t( cbl_diag_id_t id ) + : id(id), option(""), kind(diagnostics::kind::ignored), dialect(dialect_gcc_e) + {} + + cbl_diag_t( cbl_diag_id_t id, + const char option[], + diagnostics::kind kind, + cbl_dialect_t dialect = dialect_iso_e ) + : id(id), option(""), kind(kind), dialect(dialect) + { + gcc_assert(strlen(option) < sizeof(this->option)); + strcpy(this->option, option); + } + + bool operator<( const cbl_diag_t& that ) const { + return id < that.id; + } +}; + +/* + * Initially, errors and warnings are set per the default, dialect gcc. If the + * user chooses dialect iso, all dialect-enabled features are turned into + * errors. If the user selects a more generous dialect, features associated + * with it are set to be ignored. + * + * Individual features may also be suppressed, and all warnings may be elevated + * to errors. + */ +const static auto dialect_mf_gnu = cbl_dialect_t(dialect_mf_e | dialect_gnu_e); +const static auto dialect_ibm_mf_gnu = cbl_dialect_t(dialect_ibm_e | + dialect_mf_e | + dialect_gnu_e); + +std::set<cbl_diag_t> cbl_diagnostics { + { CdfNotFoundW, "-Wcdf-name-not-found", diagnostics::kind::warning }, + { CdfParameterW, "-Wcdf-invalid-parameter", diagnostics::kind::warning }, + + { EcUnknownW, "-Wec-unknown", diagnostics::kind::warning }, + + { IbmEjectE, "-Wcobol-eject", diagnostics::kind::error, dialect_ibm_e }, + { IbmLengthOf, "-Wlength-of", diagnostics::kind::error, dialect_ibm_mf_gnu }, + { IbmEqualAssignE, "-Wequal-assign", diagnostics::kind::error, dialect_ibm_e }, + { IbmProcedurePointer, "-Wprocedure-pointer", diagnostics::kind::error, dialect_ibm_mf_gnu }, + { IbmSectionNegE, "-Wsegment-negative", diagnostics::kind::error, dialect_ibm_e }, + { IbmSectionRangeE, "-Wsegment-error", diagnostics::kind::error, dialect_ibm_e }, + { IbmSectionSegmentW, "-Wsegment", diagnostics::kind::warning, dialect_ibm_e }, + { IbmStopNumber, "-Wstop-number", diagnostics::kind::error, dialect_ibm_e }, + { IbmVolatileE, "-Wcobol-volatile", diagnostics::kind::error, dialect_ibm_e }, + { IbmVolatileW, "-Wcobol-volatile", diagnostics::kind::warning, dialect_ibm_e }, + + // RESUME not supported by IBM + { IsoResume, "-Wcobol-resume", diagnostics::kind::error, dialect_ibm_e }, + + { MfBinaryLongLong, "-Wbinary-long-long", diagnostics::kind::error, dialect_mf_gnu }, + { MfCallGiving, "-Wcall-giving", diagnostics::kind::error, dialect_mf_gnu }, + { MfCdfDollar, "-Wcdf-dollar", diagnostics::kind::error, dialect_mf_gnu }, + { MfComp6, "-Wcomp-6", diagnostics::kind::error, dialect_mf_gnu }, + { MfCompX, "-Wcomp-x", diagnostics::kind::error, dialect_mf_gnu }, + { MfLevel_1_Occurs, "Wlevel-1-occurs", diagnostics::kind::error, dialect_mf_gnu }, + { MfLevel78, "-Wlevel-78", diagnostics::kind::error, dialect_mf_gnu }, + { MfMovePointer, "-Wmove-pointer", diagnostics::kind::error, dialect_mf_gnu }, + { MfReturningNum, "-Wreturning-number", diagnostics::kind::error, dialect_mf_gnu }, + { MfUsageTypename, "-Wusage-typename", diagnostics::kind::error, dialect_mf_gnu }, + { MfTrailing, "-Winspect-trailing", diagnostics::kind::error, dialect_mf_gnu }, + + { LexIncludeE, "-Winclude-file-not-found", diagnostics::kind::error }, + { LexIncludeOkN, "-Winclude-file-found", diagnostics::kind::note }, + { LexIndicatorE, "-Wstray-indicator", diagnostics::kind::error }, + { LexInputN, "-Wcopybook-found", diagnostics::kind::note }, + { LexLineE, "-Wbad-line-directive", diagnostics::kind::error }, + { LexPreprocessE, "-Wpreprocessor-error", diagnostics::kind::error }, + { LexReplaceE, "-Wreplace-error", diagnostics::kind::error }, + // mf and gnu do not require whitespace after relational operators + { LexSeparatorE, "-Woperator-space", diagnostics::kind::error, dialect_mf_gnu }, + + { Par78CdfDefinedW, "-Wlevel-78-defined", diagnostics::kind::warning }, + { ParIconvE, "-Wiconv-error", diagnostics::kind::note }, + { ParInfoI, "-Wentry-convention", diagnostics::kind::note }, + { ParLangInfoW, "-Wnllanginfo-error", diagnostics::kind::warning }, + { ParLiteral2W, "-Wliteral-concat", diagnostics::kind::warning }, + { ParLocaleW, "-Wlocale-error", diagnostics::kind::warning }, + { ParNoCorrespondingW, "-Wmove-corresponding", diagnostics::kind::warning }, + { ParNumstrW, "-Wbad-numeric", diagnostics::kind::warning }, + { ParUnresolvedProcE, "-Wprocedure-not-found", diagnostics::kind::error }, + + // unimplmeneted syntax warnings + { SynApplyCommit, "-Wapply-commit", diagnostics::kind::warning }, + { SynFileCodeSet, "-Wfile-code-set", diagnostics::kind::warning }, + { SynHighOrderBit, "-Whigh-order-bit", diagnostics::kind::warning }, + { SynRecordingMode, "-Wrecording-mode", diagnostics::kind::warning }, + { SynSetLocaleTo, "-Wset-locale-to", diagnostics::kind::warning }, + { SynSetToLocale, "-Wset-to-locale", diagnostics::kind::warning }, + +}; + +static struct set_verify { + set_verify() { + gcc_assert(cbl_diagnostics.size() == DiagDiagDiag); + auto p = std::find_if(cbl_diagnostics.begin(), cbl_diagnostics.end(), + []( const auto& diag ) { + return '?' == cbl_dialect_str(diag.dialect)[0]; + } ); + if( p != cbl_diagnostics.end() ) { + fprintf(stderr, "unregconized dialect '%04x (~%04x)'", p->dialect, ~p->dialect); + } + gcc_assert( std::none_of(cbl_diagnostics.begin(), cbl_diagnostics.end(), + []( const auto& diag ) { + return '?' == cbl_dialect_str(diag.dialect)[0]; + } ) ); + } +} verify_consistent_message_count; + +static inline diagnostics::kind +kind_of( cbl_diag_id_t id ) { + auto diag = cbl_diagnostics.find(cbl_diag_t(id)); + if( diag != cbl_diagnostics.end() ) { + return diag->kind; + } + return diagnostics::kind::ice; +} + +diagnostics::kind +cbl_diagnostic_kind( cbl_diag_id_t id ) { + return kind_of(id); +} + +bool +cbl_diagnostic_kind( cbl_diag_id_t id, diagnostics::kind kind ) { + auto p = cbl_diagnostics.find( cbl_diag_t{id} ); + if( p != cbl_diagnostics.end() ) { + auto diag(*p); + diag.kind = kind; + cbl_diagnostics.erase(p); + return cbl_diagnostics.insert(diag).second; + } + return false; +} + +bool +cbl_diagnostic_kind( cbl_dialect_t dialect, diagnostics::kind kind ) { + bool ok = true; + for( auto diag : cbl_diagnostics ) { + if( diag.dialect == dialect ) { + if( ! cbl_diagnostic_kind(diag.id, kind) ) ok = false; + } + } + return ok; +} + +void +cobol_warning( cbl_diag_id_t id, int yn, bool warning_as_error ) { + gcc_assert( 0 <= yn && yn <= 1 ); + + diagnostics::kind kind = yn? + diagnostics::kind::warning : diagnostics::kind::ignored; + + if( warning_as_error ) { + kind = diagnostics::kind::error; + } + + cbl_diagnostic_kind(id, kind); +} + +static inline const char * +option_of( cbl_diag_id_t id ) { + auto diag = cbl_diagnostics.find(cbl_diag_t(id)); + if( diag != cbl_diagnostics.end() && diag->option[0] ) { + return diag->option; + } + return nullptr; +} + +const char * +cbl_diagnostic_option( cbl_diag_id_t id ) { + return option_of(id); +} + +/* + * This is the general message looker-upper. It determines whether the + * diagnositic is in force, at what level, and the message text, and invokes + * the framework. + */ +extern int yychar; +extern YYLTYPE yylloc; + +static const diagnostics::option_id option_zero; + +location_t current_token_location(); +location_t current_token_location(const location_t& loc); + +bool +cbl_message( cbl_diag_id_t id, const char gmsgid[], ... ) { + auto_diagnostic_group d; + const char *option; + char *msg = nullptr; + + diagnostics::kind kind = kind_of(id); + if( kind == diagnostics::kind::ignored ) return false; + + if( (option = option_of(id)) != nullptr ) { + msg = xasprintf("%s [%s]", gmsgid, option); + gmsgid = msg; + } + + va_list ap; + + va_start (ap, gmsgid); + auto ret = emit_diagnostic_valist( kind, current_token_location(), + option_zero, gmsgid, &ap ); + va_end (ap); + free(msg); + + return ret; +} + +bool cbl_message( cbl_loc_t loc, cbl_diag_id_t id, const char gmsgid[], ... ) { + class temp_loc_t { // copied from util.cc + location_t orig; + public: + temp_loc_t() : orig(current_token_location()) { + if( yychar < 3 ) return; + + gcc_location_set(yylloc); // use lookahead location + } + explicit temp_loc_t( const YYLTYPE& loc) : orig(current_token_location()) { + gcc_location_set(loc); + } + explicit temp_loc_t( const YDFLTYPE& loc) : orig(current_token_location()) { + gcc_location_set(loc); + } + ~temp_loc_t() { + if( orig != current_token_location() ) { + current_token_location(orig); + } + } + }; + + auto_diagnostic_group d; + const char *option; + char *msg = nullptr; + + diagnostics::kind kind = kind_of(id); + if( kind == diagnostics::kind::ignored ) return false; + + if( (option = option_of(id)) != nullptr ) { + msg = xasprintf("%s [%s]", gmsgid, option); + gmsgid = msg; + } + + temp_loc_t looker(loc); + va_list ap; + + va_start (ap, gmsgid); + rich_location richloc (line_table, current_token_location()); + auto ret = emit_diagnostic_valist( kind, + current_token_location(), + option_zero, gmsgid, &ap ); + va_end (ap); + free(msg); + + return ret; +} + +/* + * Verify the dialect associated with the id (and thus term) is covered by the + * dialects currently in effect. If not, issue a standard message of the kind + * defined by the id. Possible combinations: + * dialect required: ok, dialect matches feature dialect + * dialect prohibits not_ok, dialect matches feature ~dialect + * + * If ok is false, then a match means the dialect prohibits the feature. + */ +bool +dialect_ok( const cbl_loc_t& loc, cbl_diag_id_t id, const char term[], bool ok ) { + auto diag = cbl_diagnostics.find(cbl_diag_t(id)); + + const char *verb = "requires"; + + if( diag == cbl_diagnostics.end() ) { + gcc_unreachable(); + } + + if( diag->kind == diagnostics::kind::ignored ) return true; + + if( dialect_has(diag->dialect) ) { + if( ok ) { + return true; + } else { + verb = "prohibits"; + } + } else { + if( !ok ) return true; // current dialect correctly does not match the feature + } + + cbl_message(loc, id, "%qs %s %<-dialect %s%>", + term, verb, cbl_dialect_str(diag->dialect)); + return false; +} + + + + + diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y index c45dc33..ad292b9 100644 --- a/gcc/cobol/parse.y +++ b/gcc/cobol/parse.y @@ -28,10 +28,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ %code requires { + #include "config.h" #include <fstream> // Before cobol-system because it uses poisoned functions #include "cobol-system.h" + #include "coretypes.h" #include "../../libgcobol/io.h" #include "../../libgcobol/ec.h" + #include "tree.h" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" @@ -42,23 +45,62 @@ }; enum accept_func_t { + accept_e, accept_done_e, accept_command_line_e, accept_envar_e, }; + struct coll_alphanat_t { + const char *alpha, *national; + }; + + struct label_pair_t { + cbl_label_t *from, *to; + }; + +class locale_tgt_t { + char user_system_default; + std::vector<int> categories; + public: + locale_tgt_t() : user_system_default('\0') {} + locale_tgt_t( int category ) + : user_system_default('\0') + , categories(1, category) + {} + locale_tgt_t operator=( int ch ) { + assert(categories.empty()); + switch(ch) { + case 'S': case 'U': + user_system_default = ch; + return *this; + } + gcc_unreachable(); + } + locale_tgt_t push_back( int token ) { + categories.push_back(token); + return *this; + } + + bool is_default() const { return 0 < user_system_default; } + char default_of() const { + assert(categories.empty()); + return user_system_default; + } + const std::vector<int>& lc_categories() const { return categories; } +}; + class literal_t { size_t isym; public: + cbl_encoding_t encoding; char prefix[3]; size_t len; char *data; bool empty() const { return data == NULL; } size_t isymbol() const { return isym; } - const char * symbol_name() const { - return isym? cbl_field_of(symbol_at(isym))->name : ""; - } + const char * symbol_name() const; literal_t& set( size_t len, char *data, const char prefix[] ) { @@ -67,17 +109,8 @@ return *this; } - literal_t& - set( const cbl_field_t * field ) { - assert(field->has_attr(constant_e)); - assert(is_literal(field)); - - set_prefix( "", 0 ); - set_data( field->data.capacity, - const_cast<char*>(field->data.initial), - field_index(field) ); - return *this; - } + literal_t& set( const cbl_field_t * field ); + literal_t& set_data( size_t len, char *data, size_t isym = 0 ) { this->isym = isym; @@ -90,13 +123,8 @@ } return *this; } - literal_t& - set_prefix( const char *input, size_t len ) { - assert(len < sizeof(prefix)); - std::fill(prefix, prefix + sizeof(prefix), '\0'); - std::transform(input, input + len, prefix, toupper); - return *this; - } + literal_t& set_prefix( const char *input, size_t len ); + bool compatible_prefix( const literal_t& that ) const { if( prefix[0] != that.prefix[0] ) { @@ -185,14 +213,14 @@ data_category_t category; category_map_t replacement; - init_statement_t( category_map_t replacement ) + explicit init_statement_t( const category_map_t& replacement ) : to_value(false) , category(data_category_none) , replacement(replacement) {} - init_statement_t( bool to_value = false ) + explicit init_statement_t( bool to_value = false ) : to_value(to_value) , category(data_category_none) , replacement(category_map_t()) @@ -205,7 +233,7 @@ static data_category_t data_category_of( const cbl_refer_t& refer ); - static _Float128 + static REAL_VALUE_TYPE numstr2i( const char input[], radix_t radix ); struct cbl_field_t; @@ -239,7 +267,7 @@ struct Elem_list_t { std::list<E> elems; Elem_list_t() {} - Elem_list_t( E elem ) { + explicit Elem_list_t( E elem ) { elems.push_back(elem); } Elem_list_t * push_back( E elem ) { @@ -277,8 +305,14 @@ } %{ +#include "config.h" #include <fstream> // Before cobol-system because it uses poisoned functions #include "cobol-system.h" +#include "coretypes.h" +#include "tree.h" +#undef cobol_dialect +#undef cobol_exceptions +#undef yy_flex_debug #include "cdfval.h" #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" @@ -290,6 +324,7 @@ #include "genapi.h" #include "../../libgcobol/exceptl.h" #include "exceptg.h" +#include "../../libgcobol/charmaps.h" #include "parse_ante.h" %} @@ -323,16 +358,16 @@ NUMED "NUMERIC-EDITED picture" NUMED_CR "NUMERIC-EDITED CR picture" NUMED_DB "NUMERIC-EDITED DB picture" -%token <number> NINEDOT NINES NINEV PIC_P -%token <string> SPACES +%token <number> NINEDOT NINES NINEV PIC_P ONES +%token <string> SPACES EQ "EQUAL" %token <literal> LITERAL %token <number> END EOP %token <string> FILENAME %token <number> INVALID %token <number> NUMBER NEGATIVE %token <numstr> NUMSTR "numeric literal" -%token <number> OVERFLOW -%token <computational> COMPUTATIONAL +%token <number> OVERFLOW_kw "OVERFLOW" +%token <computational> BINARY_INTEGER COMPUTATIONAL %token <boolean> PERFORM BACKWARD %token <number> POSITIVE @@ -340,7 +375,7 @@ %token <string> SECTION %token <number> STANDARD_ALPHABET "STANDARD ALPHABET" %token <string> SWITCH -%token <string> UPSI +%token <string> UPSI %token <number> ZERO /* environment names */ @@ -354,6 +389,7 @@ %token <number> MIGHT_BE "IS or IS NOT" FUNCTION_UDF "UDF name" FUNCTION_UDF_0 "UDF" + DEFAULT %token <string> DATE_FMT "date format" TIME_FMT "time format" @@ -366,7 +402,7 @@ LSUB "(" PARAMETER_kw "PARAMETER" OVERRIDE READY RESET - RSUB ")" + RSUB")" SERVICE_RELOAD "SERVICE RELOAD" STAR_CBL "*CBL" SUBSCRIPT SUPPRESS TITLE TRACE USE @@ -376,7 +412,10 @@ CDF_EVALUATE ">>EVALUATE" CDF_WHEN ">>WHEN" CDF_END_EVALUATE ">>END-EVALUATE" + CALL_CONVENTION ">>CALL-CONVENTION" CALL_COBOL "CALL" CALL_VERBATIM "CALL (as C)" + CDF_PUSH ">>PUSH" CDF_POP ">>POP" + SOURCE_FORMAT ">>SOURCE FORMAT" IF THEN ELSE SENTENCE @@ -390,7 +429,10 @@ STRING_kw "STRING" STOP SUBTRACT START UNSTRING WRITE WHEN - ABS ACCESS ACOS ACTUAL ADVANCING AFTER ALL + ARGUMENT_NUMBER ARGUMENT_VALUE + ENVIRONMENT_NAME ENVIRONMENT_VALUE + + ABS ACCESS ACOS ACTUAL ADVANCING AFTER ALL ALLOCATE ALPHABET ALPHABETIC ALPHABETIC_LOWER "ALPHABETIC-LOWER" ALPHABETIC_UPPER "ALPHABETIC-UPPER" @@ -402,7 +444,7 @@ BASED BASECONVERT BEFORE BINARY BIT BIT_OF "BIT-OF" BIT_TO_CHAR "BIT-TO-CHAR" - BLANK BLOCK + BLANK BLOCK_kw BOOLEAN_OF_INTEGER "BOOLEAN-OF-INTEGER" BOTTOM BY BYTE BYTE_LENGTH "BYTE-LENGTH" @@ -410,7 +452,7 @@ CF CH CHANGED CHAR CHAR_NATIONAL "CHAR-NATIONAL" CHARACTER CHARACTERS CHECKING CLASS - COBOL CODE CODESET COLLATING + COBOL CODE CODESET "CODE-SET" COLLATING COLUMN COMBINED_DATETIME "COMBINED-DATETIME" COMMA COMMAND_LINE "COMMAND-LINE" COMMAND_LINE_COUNT "COMMAND-LINE-COUNT" @@ -429,14 +471,14 @@ DAY_OF_WEEK "DAY-OF-WEEK" DAY_TO_YYYYDDD "DAY-TO-YYYYDDD" DBCS DE DEBUGGING DECIMAL_POINT - DECLARATIVES DEFAULT DELIMITED DELIMITER DEPENDING + DECLARATIVES DELIMITED DELIMITER DEPENDING DESCENDING DETAIL DIRECT DIRECT_ACCESS "DIRECT-ACCESS" DOWN DUPLICATES DYNAMIC - E EBCDIC EC EGCS ENTRY ENVIRONMENT EQUAL EVERY - EXAMINE EXHIBIT EXP EXP10 EXTEND EXTERNAL + E EBCDIC EC EGCS ENCODING ENTRY ENVIRONMENT + EVERY EXAMINE EXHIBIT EXP EXP10 EXTEND EXTERNAL EXCEPTION_FILE "EXCEPTION-FILE" EXCEPTION_FILE_N "EXCEPTION-FILE-N" @@ -478,7 +520,7 @@ INTEGER_OF_DAY "INTEGER-OF-DAY" INTEGER_OF_FORMATTED_DATE "INTEGER-OF-FORMATTED-DATE" INTEGER_PART "INTEGER-PART" - INTO INTRINSIC INVOKE IO IO_CONTROL "IO-CONTROL" + INTO INTRINSIC INVOKE IO "I-O" IO_CONTROL "I-O-CONTROL" IS ISNT "IS NOT" KANJI KEY @@ -523,7 +565,7 @@ PAGE_COUNTER "PAGE-COUNTER" PF PH PI PIC PICTURE PLUS PRESENT_VALUE PRINT_SWITCH - PROCEDURE PROCEDURES PROCEED PROCESS + PROCEDURE PROCEDURES PROCEED PROCESS PROCESSING PROGRAM_ID "PROGRAM-ID" PROGRAM_kw "Program" PROPERTY PROTOTYPE PSEUDOTEXT @@ -533,7 +575,7 @@ RD RECORD RECORDING RECORDS RECURSIVE REDEFINES REEL REFERENCE RELATIVE REM REMAINDER REMARKS REMOVAL RENAMES REPLACE REPLACING REPORT REPORTING REPORTS - REPOSITORY RERUN RESERVE RESTRICTED RESUME + REPOSITORY RERUN RESERVE RESTRICTED RESUME RETRY REVERSE REVERSED REWIND RF RH RIGHT ROUNDED RUN SAME SCREEN SD @@ -554,7 +596,7 @@ STATUS STRONG SUBSTITUTE SUM SYMBOL SYMBOLIC SYNCHRONIZED - TALLY TALLYING TAN TERMINATE TEST + TALLYING TAN TERMINATE TEST TEST_DATE_YYYYMMDD "TEST-DATE-YYYYMMDD" TEST_DAY_YYYYDDD "TEST-DAY-YYYYDDD" TEST_FORMATTED_DATETIME "TEST-FORMATTED-DATETIME" @@ -564,17 +606,16 @@ THAN TIME TIMES TO TOP TOP_LEVEL - TRACKS TRACK_AREA TRAILING TRANSFORM TRIM TRUE_kw "True" TRY - TURN TYPE TYPEDEF + TRACKS TRACK_AREA TRAILING TRANSFORM TRIM TRUE_kw "True" + TRY TURN TYPE TYPEDEF - ULENGTH UNBOUNDED UNIT UNITS UNIT_RECORD UNTIL UP UPON - UPOS UPPER_CASE USAGE USING USUBSTR USUPPLEMENTARY - UTILITY UUID4 UVALID UWIDTH + ULENGTH UNBOUNDED UNIT UNITS UNIT_RECORD UNTIL + UP UPON UPOS UPPER_CASE USAGE USING + USUBSTR USUPPLEMENTARY UTILITY UUID4 UVALID UWIDTH - VALUE VARIANCE VARYING VOLATILE + VALIDATING VALUE VARIANCE VARYING VOLATILE WHEN_COMPILED WITH WORKING_STORAGE - XML XMLGENERATE XMLPARSE YEAR_TO_YYYY YYYYDDD YYYYMMDD /* unused Context Words */ @@ -607,7 +648,7 @@ NONE NORMAL NUMBERS PREFIXED PREVIOUS PROHIBITED RELATION REQUIRED REVERSE_VIDEO ROUNDING - SECONDS SECURE SHORT SIGNED + SECONDS SECURE SHORT SIGNED_kw STANDARD_BINARY "STANDARD-BINARY" STANDARD_DECIMAL "STANDARD-DECIMAL" STATEMENT STEP STRUCTURE @@ -615,9 +656,11 @@ TOWARD_LESSER "TOWARD-LESSER" TRUNCATION UCS_4 "UCS-4" - UNDERLINE UNSIGNED + UNDERLINE UNSIGNED_kw UTF_16 "UTF-16" UTF_8 "UTF-8" + XMLGENERATE "XML GENERATE" + XMLPARSE "XML PARSE" ADDRESS END_ACCEPT "END-ACCEPT" @@ -639,6 +682,7 @@ END_SUBTRACT "END-SUBTRACT" END_UNSTRING "END-UNSTRING" END_WRITE "END-WRITE" + END_XML "END-XML" END_IF "END-IF" /* end tokens without semantic value */ @@ -649,15 +693,17 @@ %type <number> sentence statements statement %type <number> star_cbl_opt close_how -%type <number> test_before usage_clause1 might_be -%type <boolean> all optional sign_leading on_off initialized strong +%type <number> test_before usage_clause1 might_be alphanational +%type <boolean> all optional sign_leading on_off initialized strong is_signed %type <number> count data_clauses data_clause %type <number> nine nines nps relop spaces_etc reserved_value signed -%type <number> variable_type +%type <number> variable_type binary_type %type <number> true_false posneg eval_posneg %type <number> open_io alphabet_etc %type <special_type> device_name -%type <string> numed collating_sequence context_word ctx_name locale_spec +%type <string> numed context_word ctx_name locale_spec +%type <char_class_locales> char_class_locales coll_alphanats +%type <collating_name> coll_alphanat %type <literal> namestr alphabet_lit program_as repo_as %type <field> perform_cond kind_of_name %type <refer> alloc_ret @@ -683,7 +729,7 @@ %type <string> fd_name picture_sym name66 paragraph_name %type <literal> literalism %type <number> bound advance_when org_clause1 read_next -%type <number> access_mode multiple lock_how lock_mode +%type <number> access_mode multiple lock_how lock_mode org_is %type <select_clauses> select_clauses %type <select_clause> select_clause access_clause alt_key_clause assign_clause collate_clause status_clause @@ -692,6 +738,10 @@ relative_key_clause reserve_clause sharing_clause %type <file> filename read_body write_body delete_body +%type <label> delete_file_body +%type <error> delete_error delete_except delete_excepts + +%type <file> start_impl start_cond start_body %type <rewrite_t> rewrite_body %type <min_max> record_vary rec_contains from_to record_desc %type <file_op> read_file rewrite1 write_file @@ -705,7 +755,7 @@ %type <refer> move_tgt selected_name read_key read_into vary_by %type <refer> accept_refer num_operand envar search_expr any_arg %type <accept_func> accept_body -%type <refers> expr_list subscripts arg_list free_tgts +%type <refers> subscript_exprs subscripts arg_list free_tgts %type <targets> move_tgts set_tgts %type <field> search_varying %type <field> search_term search_terms @@ -722,7 +772,7 @@ %type <refer> inspected %type <insp_qual> insp_qual -%type <insp_match> insp_quals insp_mtquals tally_match +%type <insp_match> insp_quals insp_mtqual tally_match %type <insp_replace> x_by_y %type <insp_oper> replace_oper x_by_ys %type <insp_oper> tally_forth tally_matches @@ -765,6 +815,7 @@ %type <error> on_overflow on_overflows %type <error> arith_err arith_errs %type <error> accept_except accept_excepts call_except call_excepts + %type <compute_body_t> compute_body %type <refer> ffi_name set_operand set_tgt scalar_arg unstring_src @@ -783,15 +834,24 @@ %type <error_clauses> io_invalids read_eofs write_eops %type <boolean> io_invalid read_eof write_eop global is_global anycase backward + end_display + exh_changed exh_named + override %type <number> mistake globally first_last %type <io_mode> io_mode +%type <label_pair> xmlprocs +%type <error> xmlexcept xmlexcepts +%type <field> xmlencoding xmlvalidating +%type <number> xmlreturning +%type <label> xmlparse_body + %type <labels> labels %type <label> label_1 section_name %type <switches> upsi_entry -%type <special> acceptable disp_target +%type <special> acceptable disp_upon %type <display> disp_body %type <false_domain> domains domain @@ -817,24 +877,38 @@ %type <opt_init_sects> opt_init_sects %type <opt_init_sect> opt_init_sect %type <number> opt_init_value +%type <number> locale_current loc_category user_default +%type <string> locale_name +%type <token_list> loc_categories locale_tgt %type <opt_round> rounded round_between rounded_type rounded_mode %type <opt_arith> opt_arith_type %type <module_type> module_type +%type <nameloc> repo_func_name +%type <namelocs> repo_func_names +%type <codeset> codeset_name +%type <locale_phrase> locale_phrase + %union { bool boolean; int number; char *string; - _Float128 float128; // Hope springs eternal: 28 Mar 2023 + REAL_VALUE_TYPE float128; literal_t literal; cbl_field_attr_t field_attr; ec_type_t ec_type; ec_list_t* ec_list; + cbl_nameloc_t *nameloc; + cbl_namelocs_t *namelocs; declarative_list_t* dcl_list_t; isym_list_t* isym_list; struct { radix_t radix; char *string; } numstr; - struct { int token; literal_t name; } prog_end; + struct { YYLTYPE loc; int token; literal_t name; } prog_end; struct { int token; special_name_t id; } special_type; + struct { char locale_type; const char * name; } locale_phrase; + coll_alphanat_t char_class_locales; + struct collating_name_t { int token; const char *name; } collating_name; + struct { size_t isym; cbl_encoding_t encoding; } codeset; struct { cbl_field_type_t type; uint32_t capacity; bool signable; } computational; struct cbl_special_name_t *special; @@ -846,7 +920,7 @@ struct { cbl_file_t *file; file_status_t handled; } file_op; struct cbl_label_t *label; struct { cbl_label_t *label; int token; } exception; - struct cbl_field_data_t *field_data; + struct { cbl_encoding_t encoding; cbl_field_data_t *data; } field_data; struct cbl_field_t *field; struct { bool tf; cbl_field_t *field; } bool_field; struct { int token; cbl_field_t *cond; } cond_field; @@ -870,9 +944,9 @@ struct arith_t *arith; struct { size_t ntgt; cbl_num_result_t *tgts; cbl_refer_t *expr; } compute_body_t; - struct ast_inspect_t *insp_one; - struct ast_inspect_list_t *insp_all; - struct ast_inspect_oper_t *insp_oper; + struct cbl_inspect_t *insp_one; + cbl_inspect_opers_t *insp_all; + struct cbl_inspect_oper_t *insp_oper; struct { bool before; cbl_inspect_qual_t *qual; } insp_qual; cbl_inspect_t *inspect; cbl_inspect_match_t *insp_match; @@ -882,12 +956,14 @@ struct { cbl_refer_t *input, *delimiter; } delimited_1; struct { cbl_refer_t *from, *len; } refmod_parts; struct refer_collection_t *delimiteds; + struct { cbl_label_t *on_error, *not_error; } error; + label_pair_t label_pair; struct { unsigned int nclause; bool tf; } error_clauses; struct refer_pair_t { cbl_refer_t *first, *second; } refer2; struct { refer_collection_t *inputs; refer_pair_t into; } str_body; - struct { accept_func_t func; cbl_refer_t *into, *from; } accept_func; + struct { accept_func_t func; cbl_refer_t *into, *from; special_name_t special;} accept_func; struct unstring_into_t *uns_into; struct unstring_tgt_list_t *uns_tgts; struct unstring_tgt_t *uns_tgt; @@ -915,6 +991,7 @@ substitution_t substitution; substitutions_t *substitutions; struct { bool is_locale; cbl_refer_t *arg2; } numval_locale_t; + locale_tgt_t *token_list; cbl_options_t::arith_t opt_arith; cbl_round_t opt_round; @@ -924,7 +1001,10 @@ } %printer { fprintf(yyo, "clauses: 0x%04x", $$); } data_clauses -%printer { fprintf(yyo, "%s %s", refer_type_str($$), $$? $$->name() : "<none>"); } <refer> +%printer { fprintf(yyo, "%s %s %s", + refer_type_str($$), + $$? $$->name() : "<none>", + $$ && $$->field? $$->field->codeset.name() : "<none>"); } <refer> %printer { fprintf(yyo, "%s", $$->field? name_of($$->field) : "[omitted]"); } alloc_ret %printer { fprintf(yyo, "%s %s '%s' (%s)", $$? cbl_field_type_str($$->type) : "<%empty>", @@ -938,18 +1018,20 @@ %printer { fprintf(yyo, "%s (token %d)", keyword_str($$), $$ ); } relop %printer { fprintf(yyo, "'%s'", $$? $$ : "" ); } NAME <string> -%printer { fprintf(yyo, "%s'%.*s'{%zu} %s", $$.prefix, int($$.len), $$.data, $$.len, +%printer { fprintf(yyo, "%s'%.*s'{" HOST_SIZE_T_PRINT_UNSIGNED "} %s", + $$.prefix, int($$.len), $$.data, (fmt_size_t)$$.len, $$.symbol_name()); } <literal> -%printer { fprintf(yyo, "%s (1st of %zu)", +%printer { fprintf(yyo,"%s (1st of" HOST_SIZE_T_PRINT_UNSIGNED")", $$->targets.empty()? "" : $$->targets.front().refer.field->name, - $$->targets.size() ); } <targets> -%printer { fprintf(yyo, "#%zu: %s", - is_temporary($$)? 0 : field_index($$), + (fmt_size_t)$$->targets.size() ); } <targets> +%printer { fprintf(yyo, "#" HOST_SIZE_T_PRINT_UNSIGNED ": %s", + is_temporary($$)? 0 : (fmt_size_t)field_index($$), $$? name_of($$) : "<nil>" ); } name -%printer { fprintf(yyo, "{%zu-%zu}", $$.min, $$.max ); } <min_max> +%printer { fprintf(yyo, "{" HOST_SIZE_T_PRINT_UNSIGNED "-" HOST_SIZE_T_PRINT_UNSIGNED "}", + (fmt_size_t)$$.min, (fmt_size_t)$$.max ); } <min_max> %printer { fprintf(yyo, "{%s}", $$? "+/-" : "" ); } signed -%printer { fprintf(yyo, "{%s of %zu}", - teed_up_names().front(), teed_up_names().size() ); } qname +%printer { fprintf(yyo, "{%s of " HOST_SIZE_T_PRINT_UNSIGNED "}", + teed_up_names().front(), (fmt_size_t) teed_up_names().size() ); } qname %printer { fprintf(yyo, "{%d}", $$ ); } <number> %printer { fprintf(yyo, "'%s'", $$.string ); } <numstr> %printer { const char *s = string_of($$); @@ -961,9 +1043,9 @@ $$.low? (const char*) $$.low : "", $$.high? (const char*) $$.high : "", $$.also? "+" : "" ); } <colseq> -%printer { fprintf(yyo, "{%s, %zu parameters}", +%printer { fprintf(yyo, "{%s, " HOST_SIZE_T_PRINT_UNSIGNED " parameters}", name_of($$.ffi_name->field), !$$.using_params? 0 : - $$.using_params->elems.size()); } call_body + (fmt_size_t)$$.using_params->elems.size()); } call_body %printer { fprintf(yyo, "%s <- %s", data_category_str($$.category), name_of($$.replacement->field)); } init_by @@ -987,14 +1069,17 @@ %right IF THEN ELSE SENTENCE ACCEPT ADD ALTER CALL CANCEL CLOSE COMPUTE CONTINUE - DELETE DISPLAY DIVIDE EVALUATE END EOP EXIT FILLER_kw + DELETE DISPLAY DIVIDE + EVALUATE END EOP EXIT + FILLER_kw GOBACK GOTO INITIALIZE INSPECT - MERGE MOVE MULTIPLY OPEN OVERFLOW PARAGRAPH PERFORM + MERGE MOVE MULTIPLY OPEN OVERFLOW_kw PARAGRAPH PERFORM READ RELEASE RETURN REWRITE SEARCH SET SELECT SORT SORT_MERGE STRING_kw STOP SUBTRACT START UNSTRING WRITE WHEN INVALID + XMLGENERATE XMLPARSE %left ABS ACCESS ACOS ACTUAL ADVANCING AFP_5A AFTER ALL ALLOCATE @@ -1003,12 +1088,12 @@ ALPHANUMERIC ALPHANUMERIC_EDITED ALPHED ALSO ALTERNATE ANNUITY ANUM ANY ANYCASE APPLY ARE - AREA AREAS AS + AREA AREAS AS ATTRIBUTES ASCENDING ACTIVATING ASIN ASSIGN AT ATAN BACKWARD BASED BASECONVERT BEFORE BINARY BIT BIT_OF BIT_TO_CHAR - BLANK BLOCK + BLANK BLOCK_kw BOOLEAN_OF_INTEGER BOTTOM BY BYTE BYTE_LENGTH @@ -1044,7 +1129,8 @@ DOWN DUPLICATES DYNAMIC - E EBCDIC EC EGCS ENTRY ENVIRONMENT EQUAL ERROR EVERY + E EBCDIC EC EGCS ELEMENT + ENTRY ENVIRONMENT ERROR EVERY EXAMINE EXCEPTION EXHIBIT EXP EXP10 EXTEND EXTERNAL EXCEPTION_FILE @@ -1115,12 +1201,13 @@ MIGHT_BE MINN MULTIPLE MOD MODE MODULE_NAME - NAMED NAT NATIONAL + NAMED NAMESPACE NAMESPACE_PREFIX "NAMESPACE-PREFIX" + NAT NATIONAL NATIONAL_EDITED NATIONAL_OF NATIVE NEGATIVE NESTED NEXT NINEDOT NINES NINEV NO NOTE NO_CONDITION - NULLS NULLPTR NUMBER + NONNUMERIC NULLS NULLPTR NUMBER NUME NUMED NUMED_CR NUMED_DB NUMERIC NUMERIC_EDITED NUMSTR NUMVAL NUMVAL_C @@ -1168,7 +1255,7 @@ LITERAL SUBSTITUTE SUM SWITCH SYMBOL SYMBOLIC SYNCHRONIZED SYSIN SYSIPT SYSLST SYSOUT SYSPCH SYSPUNCH - TALLY TALLYING TAN TERMINATE TEST + TALLYING TAN TERMINATE TEST TEST_DATE_YYYYMMDD TEST_DAY_YYYYDDD TEST_FORMATTED_DATETIME @@ -1188,7 +1275,7 @@ VALUE VARIANCE VARYING VOLATILE WHEN_COMPILED WITH WORKING_STORAGE - XML XMLGENERATE XMLPARSE + XML_DECLARATION "XML-DECLARATION" YEAR_TO_YYYY YYYYDDD YYYYMMDD ZERO @@ -1222,7 +1309,7 @@ NONE NORMAL NUMBERS PREFIXED PREVIOUS PROHIBITED RELATION REQUIRED REVERSE_VIDEO ROUNDING - SECONDS SECURE SHORT SIGNED + SECONDS SECURE SHORT SIGNED_kw STANDARD_BINARY STANDARD_DECIMAL STATEMENT STEP STRUCTURE @@ -1230,7 +1317,7 @@ TOWARD_LESSER TRUNCATION UCS_4 - UNDERLINE UNSIGNED + UNDERLINE UNSIGNED_kw UTF_16 UTF_8 @@ -1241,7 +1328,7 @@ END_EVALUATE END_MULTIPLY END_PERFORM END_READ END_RETURN END_REWRITE END_SEARCH END_START END_STRING END_SUBTRACT - END_UNSTRING END_WRITE + END_UNSTRING END_WRITE END_XML error END_IF @@ -1249,7 +1336,7 @@ %left OR %left AND %right NOT -%left '<' '>' '=' NE LE GE +%left '<' '>' EQ NE LE GE %left '-' '+' %left '*' '/' %right POW @@ -1308,7 +1395,7 @@ return ok; } - static void initialize_allocated( cbl_refer_t input ); + static void initialize_allocated( const cbl_refer_t& input ); static void initialize_statement( std::list<cbl_num_result_t>& tgts, bool with_filler, @@ -1327,21 +1414,72 @@ return strlen(lit.data) == lit.len? lit.data : NULL; } - static inline char * string_of( _Float128 cce ) { - static const char empty[] = "", format[] = "%.32E"; - char output[64]; - int len = strfromf128 (output, sizeof(output), format, cce); - if( sizeof(output) < size_t(len) ) { - dbgmsg("string_of: value requires %d digits (of %zu)", - len, sizeof(output)); - return xstrdup(empty); + static inline void strip_trailing_zeroes(char * const psz) + { + if( yydebug) return; + // The idea here is to take the output of real_to_decimal and make it + // more integer friendly. Any integer value that can be expressed in 1 + // to MAX_FIXED_POINT_DIGITS digits is converted to a string without a + // decimal point and no exponent. + + char *pdot = strchr(psz, '.'); + gcc_assert(pdot); + char *pe = strchr(psz, 'e'); + if( !pe ) + { + // The most likely cause of this is a "0.0" result. + strcpy(psz, "0"); + return; } + char *pnz = pe-1; + while(*pnz == '0') + { + pnz--; + } + // pdot points to the decimal point. + // pe points to the 'e'. + // pnz points to the rightmost non-zero significand digit. + + // Put the exponent on top of the trailing zeroes: + memmove(pnz+1, pe, strlen(pe)+1); + pe = pnz+1; + int exp = atoi(pe+1); + // Compute the number digits to the right of the decimal point: + int non_zero_digits = pe - (pdot+1); + if( exp >= 1 && exp <= MAX_FIXED_POINT_DIGITS && non_zero_digits <= exp) + { + // Further simplification is possible, because the value does not actually + // need a decimal point. That's because we are dealing with something + // like 1.e+0, or 1.23e2 or 1.23e3 + + // Terminate the value where the 'e' is now: + *pe = '\0'; + // Figure out where the extra zeroes will go: + pe -= 1; + // Get rid of the decimal place: + memmove(pdot, pdot+1, strlen(pdot)+1); + // Tack on the additional zeroes: + for(int i=0; i<exp - non_zero_digits; i++) + { + *pe++ = '0'; + } + *pe++ = '\0'; + } + } + static inline char * string_of( const REAL_VALUE_TYPE &cce ) { + char output[64]; + real_to_decimal( output, &cce, sizeof(output), 32, 0 ); + strip_trailing_zeroes(output); char decimal = symbol_decimal_point(); std::replace(output, output + strlen(output), '.', decimal); return xstrdup(output); } + static inline char * string_of( tree cce ) { + return string_of (TREE_REAL_CST (cce)); + } + cbl_field_t * new_literal( const literal_t& lit, enum cbl_field_attr_t attr ); @@ -1390,21 +1528,22 @@ id_div: cdf_words IDENTIFICATION_DIV '.' program_id cdf_words: %empty | cobol_words + /* | error { error_msg(@1, "not a COBOL-WORD"); } */ ; cobol_words: cobol_words1 | cobol_words cobol_words1 ; cobol_words1: COBOL_WORDS EQUATE NAME[keyword] WITH NAME[name] { - if( ! tokens.equate(@keyword, $keyword, $name) ) { YYERROR; } + if( ! cdf_tokens.equate(@keyword, $keyword, $name) ) { YYERROR; } } | COBOL_WORDS UNDEFINE NAME[keyword] { - if( ! tokens.undefine(@keyword, $keyword) ) { YYERROR; } + if( ! cdf_tokens.undefine(@keyword, $keyword) ) { YYERROR; } } | COBOL_WORDS SUBSTITUTE NAME[keyword] BY NAME[name] { - if( ! tokens.substitute(@keyword, $keyword, $name) ) { YYERROR; } + if( ! cdf_tokens.substitute(@keyword, $keyword, $name) ) { YYERROR; } } | COBOL_WORDS RESERVE NAME[name] { - if( ! tokens.reserve(@name, $name) ) { YYERROR; } + if( ! cdf_tokens.reserve(@name, $name) ) { YYERROR; } } ; @@ -1418,7 +1557,7 @@ program_id: PROGRAM_ID dot namestr[name] program_as program_attrs[attr] dot const char *name = string_of($name); parser_enter_program( name, false, &main_error ); if( main_error ) { - error_msg(@name, "PROGRAM-ID 'main' is invalid with -main option"); + error_msg(@name, "PROGRAM-ID 'main' is invalid with %<-main%> option"); YYERROR; } @@ -1444,7 +1583,7 @@ program_as: %empty { static const literal_t empty {}; $$ = empty; } | AS LITERAL { $$ = $2; } ; -function_id: FUNCTION '.' NAME program_as program_attrs[attr] '.' +function_id: FUNCTION NAME program_as program_attrs[attr] '.' { internal_ebcdic_lock(); current_division = identification_div_e; @@ -1454,7 +1593,8 @@ function_id: FUNCTION '.' NAME program_as program_attrs[attr] '.' int main_error = 0; parser_enter_program( $NAME, true, &main_error ); if( main_error ) { - error_msg(@NAME, "FUNCTION-ID 'main' is invalid with -main option"); + error_msg(@NAME, "FUNCTION-ID %<main%> is invalid " + "with %<-main%> option"); YYERROR; } if( symbols_begin() == symbols_end() ) { @@ -1463,8 +1603,8 @@ function_id: FUNCTION '.' NAME program_as program_attrs[attr] '.' if( !current.new_program(@NAME, LblFunction, $NAME, $program_as.data, $attr.common, $attr.initial) ) { - auto L = symbol_program(current_program_index(), $NAME); - assert(L); + auto e = symbol_function(current_program_index(), $NAME); + auto L = cbl_label_of(e); error_msg(@NAME, "FUNCTION %s already defined on line %d", $NAME, L->line); YYERROR; @@ -1477,7 +1617,7 @@ function_id: FUNCTION '.' NAME program_as program_attrs[attr] '.' current.udf_add(current_program_index()); if( nparse_error > 0 ) YYABORT; } - | FUNCTION '.' NAME program_as is PROTOTYPE '.' + | FUNCTION NAME program_as is PROTOTYPE '.' { cbl_unimplemented("FUNCTION PROTOTYPE"); } @@ -1496,7 +1636,7 @@ opt_clause: opt_arith | opt_entry | opt_binary | opt_decimal { - cbl_unimplementedw("type FLOAT-DECIMAL was ignored"); + cbl_unimplemented("type FLOAT-DECIMAL"); } | opt_intermediate | opt_init @@ -1518,21 +1658,25 @@ opt_round: DEFAULT ROUNDED mode is rounded_type[type] { } ; opt_entry: ENTRY_CONVENTION is COBOL { - yywarn("ENTRY-CONVENTION IS COBOL, check"); + cbl_message(ParInfoI, "ENTRY-CONVENTION IS COBOL"); } ; opt_binary: FLOAT_BINARY default_kw is HIGH_ORDER_LEFT { - cbl_unimplementedw("HIGH-ORDER-LEFT was ignored"); + cbl_unimplementedw(SynHighOrderBit, + "HIGH-ORDER-LEFT was ignored"); if( ! current.option_binary(cbl_options_t::high_order_left_e) ) { - error_msg(@3, "unable to set HIGH_ORDER_LEFT"); + cbl_message(@3, SynHighOrderBit, + "unable to set %<HIGH_ORDER_LEFT%>"); } } | FLOAT_BINARY default_kw is HIGH_ORDER_RIGHT[opt] { - cbl_unimplementedw("HIGH-ORDER-RIGHT was ignored"); + cbl_unimplementedw(SynHighOrderBit, + "HIGH-ORDER-RIGHT was ignored"); if( ! current.option_binary(cbl_options_t::high_order_right_e) ) { - error_msg(@opt, "unable to set HIGH-ORDER-RIGHT"); + cbl_message(@opt, SynHighOrderBit, + "unable to set HIGH-ORDER-RIGHT"); } } ; @@ -1541,30 +1685,38 @@ default_kw: %empty ; opt_decimal: FLOAT_DECIMAL default_kw is HIGH_ORDER_LEFT[opt] { - cbl_unimplementedw("HIGH-ORDER-LEFT was ignored"); + cbl_unimplementedw(SynHighOrderBit, + "HIGH-ORDER-LEFT was ignored"); if( ! current.option_decimal(cbl_options_t::high_order_left_e) ) { - error_msg(@opt, "unable to set HIGH-ORDER-LEFT"); + cbl_message(@opt, SynHighOrderBit, + "unable to set HIGH-ORDER-LEFT"); } } | FLOAT_DECIMAL default_kw is HIGH_ORDER_RIGHT[opt] { - cbl_unimplementedw("HIGH-ORDER-RIGHT was ignored"); + cbl_unimplementedw(SynHighOrderBit, + "HIGH-ORDER-RIGHT was ignored"); if( ! current.option_decimal(cbl_options_t::high_order_right_e) ) { - error_msg(@opt, "unable to set HIGH-ORDER-RIGHT"); + cbl_message(@opt, SynHighOrderBit, + "unable to set HIGH-ORDER-RIGHT"); } } | FLOAT_DECIMAL default_kw is BINARY_ENCODING[opt] { - cbl_unimplementedw("BINARY-ENCODING was ignored"); + cbl_unimplementedw(SynHighOrderBit, + "BINARY-ENCODING was ignored"); if( ! current.option(cbl_options_t::binary_encoding_e) ) { - error_msg(@opt, "unable to set BINARY-ENCODING option"); + cbl_message(@opt, SynHighOrderBit, + "unable to set BINARY-ENCODING option"); } } | FLOAT_DECIMAL default_kw is DECIMAL_ENCODING[opt] { - cbl_unimplementedw("DECIMAL-ENCODING was ignored"); + cbl_unimplementedw(SynHighOrderBit, + "DECIMAL-ENCODING was ignored"); if( ! current.option(cbl_options_t::decimal_encoding_e) ) { - error_msg(@opt, "unable to set DECIMAL-ENCODING option"); + cbl_message(@opt, SynHighOrderBit, + "unable to set DECIMAL-ENCODING option"); } } ; @@ -1653,9 +1805,9 @@ namestr: ctx_name { $$.prefix); YYERROR; } - if( !is_cobol_word($$.data) ) { + if( !is_cobol_charset($$.data) ) { error_msg(@1, "literal '%s' must be a COBOL or C identifier", - $$.data); + $$.data); } } ; @@ -1748,7 +1900,8 @@ io_control_clause: } | APPLY COMMIT on field_list { - cbl_unimplementedw("I-O-CONTROL APPLY COMMIT"); + cbl_unimplementedw(SynApplyCommit, + "I-O-CONTROL APPLY COMMIT ignored"); } ; area: %empty @@ -1768,7 +1921,7 @@ select: SELECT optional NAME[name] select_clauses[clauses] '.' cbl_file_t *file = $clauses.file; file->optional = $optional; - file->line = yylineno; + file->line = @name.first_line; if( !namcpy(@clauses, file->name, $name) ) YYERROR; if( ! ($clauses.clauses & assign_clause_e) ) { @@ -1841,7 +1994,7 @@ select: SELECT optional NAME[name] select_clauses[clauses] '.' cbl_file_t file = protofile; file.optional = $optional; - file.line = yylineno; + file.line = @name.first_line; if( !namcpy(@name, file.name, $name) ) YYERROR; if( file_add(@name, &file) == NULL ) YYERROR; @@ -1856,11 +2009,12 @@ selected_name: external scalar { $$ = $2; } YYERROR; } uint32_t len = $name.len; - cbl_field_t field { - 0, FldLiteralA, FldInvalid, quoted_e | constant_e, - 0, 0, 0, nonarray, 0, "", 0, cbl_field_t::linkage_t(), - {len,len,0,0, $name.data}, NULL }; + // Pretend hex-encoded because that means use verbatim. + cbl_field_t field { FldLiteralA, + hex_encoded_e | quoted_e | constant_e, + {len,len,0,0, $name.data} }; field.attr |= literal_attr($name.prefix); + field.codeset.set(); $$ = new cbl_refer_t( field_add(@name, &field) ); } ; @@ -1885,7 +2039,7 @@ select_clauses: select_clause { $$.clauses = $1.clause; $$.file = $1.file; } if( $$.file->nkey++ == 0 ) { // If no key yet exists, create room for it and the // present alternate. - assert($$.file->keys == &cbl_file_t::no_key); + assert($$.file->keys == nullptr); $$.file->keys = new cbl_file_key_t[++$$.file->nkey]; } { @@ -1897,8 +2051,7 @@ select_clauses: select_clause { $$.clauses = $1.clause; $$.file = $1.file; } // Assign the alternate key to the last element, // and update the pointer. *alt = $part.file->keys[0]; - delete[] $$.file->keys; - $$.file->keys = keys; + $$.file->keys_update(keys); } break; case assign_clause_e: @@ -1967,11 +2120,11 @@ select_clauses: select_clause { $$.clauses = $1.clause; $$.file = $1.file; } YYERROR; } if( $$.file->nkey == 0 ) { + assert( 1 == $part.file->nkey ); $$.file->nkey = $part.file->nkey; - $$.file->keys = $part.file->keys; - } else { - $$.file->keys[0] = $part.file->keys[0]; - } + $$.file->keys = new cbl_file_key_t[1]; + } + $$.file->keys[0] = $part.file->keys[0]; break; /* case password_clause_e: */ case file_status_clause_e: @@ -2129,14 +2282,28 @@ org_clause: org_clause1[org] $$.file->org = static_cast<cbl_file_org_t>($org); } ; -org_is: %empty - | ORGANIZATION is +org_is: %empty { $$ = 0; } + | ORGANIZATION is { $$ = 0; } + | ORGANIZATION is RECORD { $$ = RECORD; } + | RECORD { $$ = RECORD; } ; // file_sequential is the proper default -org_clause1: org_is SEQUENTIAL { $$ = file_sequential_e; } - | org_is LINE SEQUENTIAL { $$ = file_line_sequential_e; } - | org_is RELATIVE { $$ = file_relative_e; } - | org_is INDEXED { $$ = file_indexed_e; } +org_clause1: org_is SEQUENTIAL { + $$ = $1 == RECORD? file_line_sequential_e : file_sequential_e; + } + | org_is LINE SEQUENTIAL + { + if( $1 ) error_msg(@2, "syntax error: invalid %<RECORD%>"); + $$ = file_line_sequential_e; + } + | org_is RELATIVE { + if( $1 ) error_msg(@2, "syntax error: invalid %<RECORD%>"); + $$ = file_relative_e; + } + | org_is INDEXED { + if( $1 ) error_msg(@2, "syntax error: invalid %<RECORD%>"); + $$ = file_indexed_e; + } ; /* @@ -2221,20 +2388,31 @@ config_paragraphs: config_paragraph config_paragraph: SPECIAL_NAMES '.' - | SPECIAL_NAMES '.' specials '.' - | SOURCE_COMPUTER '.' NAME with_debug '.' - | OBJECT_COMPUTER '.' NAME collating_sequence[name] '.' - { - if( $name ) { - if( !current.collating_sequence($name) ) { - error_msg(@name, "collating sequence already defined as '%s'", - current.collating_sequence()); - YYERROR; + | SPECIAL_NAMES '.' special_names '.' + { + std::reverse_iterator<symbol_elem_t *> + p(symbols_end()), + pend(symbols_begin(PROGRAM)); + for( ++p; p != pend; p++ ) { + if( p->type == SymAlphabet ) { + const auto& alphabet = *cbl_alphabet_of(&*p); + if( alphabet.encoding == no_encoding_e ) { + assert(alphabet.locale != 0 ); + const auto& missing = *cbl_locale_of(symbol_at(alphabet.locale)); + error_msg(alphabet.loc, + "ALPHABET %qs references LOCALE %qs, which is not defined", + alphabet.name, missing.name); + } } } } - | REPOSITORY '.' - | REPOSITORY '.' repo_members '.' + | SOURCE_COMPUTER '.' + | SOURCE_COMPUTER '.' NAME '.' + | SOURCE_COMPUTER '.' NAME with_debug '.' + | OBJECT_COMPUTER '.' + | OBJECT_COMPUTER '.' NAME[computer] object_computer '.' + | REPOSITORY dot + | REPOSITORY dot repo_members '.' ; repo_members: repo_member @@ -2262,38 +2440,61 @@ repo_expands: %empty repo_interface: INTERFACE NAME repo_as repo_expands ; -repo_func: FUNCTION repo_func_names INTRINSIC - { - auto namelocs( name_queue.pop() ); - for( const auto& nameloc : namelocs ) { - current.repository_add(nameloc.name); +repo_func: FUNCTION repo_func_names[namelocs] INTRINSIC { + for( const auto& nameloc : *$namelocs ) { + if( 0 == intrinsic_token_of(nameloc.name) ) { + error_msg(nameloc.loc, + "no such intrinsic function: %qs", + nameloc.name); + continue; + } + current.repository_add(nameloc.name); } } | FUNCTION ALL INTRINSIC { current.repository_add_all(); } - | FUNCTION repo_func_names - ; -repo_func_names: - repo_func_name - | repo_func_names repo_func_name - ; -repo_func_name: NAME { - if( ! current.repository_add($NAME) ) { // add intrinsic by name - auto token = current.udf_in($NAME); + | FUNCTION repo_func_names[namelocs] { + // We allow multiple names because GnuCOBOL does. ISO says 1. + for( const auto& nameloc : *$namelocs ) { + if( 0 != intrinsic_token_of(nameloc.name) ) { + error_msg(nameloc.loc, + "intrinsic function %qs requires INTRINSIC", + nameloc.name); + continue; + } + auto token = current.udf_in(nameloc.name); if( !token ) { - error_msg(@NAME, "%s is not defined here as a user-defined function", - $NAME); - current.udf_dump(); - YYERROR; + error_msg(nameloc.loc, + "%s is not defined here as a user-defined function", + nameloc.name); + continue; } - auto e = symbol_function(0, $NAME); + auto e = symbol_function(0, nameloc.name); assert(e); current.repository_add(symbol_index(e)); // add UDF to repository } } ; +repo_func_names: + repo_func_name[name] { + $$ = new cbl_namelocs_t(1, *$name); + delete $name; + } + | repo_func_names repo_func_name[name] { + $$ = $1; + $$->push_back(*$name); + delete $name; + } + ; +repo_func_name: NAME repo_as { + if( ! $repo_as.empty() ) { + cbl_unimplemented_at(@repo_as, "%qs", $repo_as.data); + } + $$ = new cbl_nameloc_t(@NAME, $NAME); + } + ; repo_program: PROGRAM_kw NAME repo_as { @@ -2325,7 +2526,7 @@ repo_program: PROGRAM_kw NAME repo_as assert(program); prog.data.initial = program->name; } - auto e = symbol_field_add(PROGRAM, &prog); + const auto e = symbol_field_add(PROGRAM, &prog); symbol_field_location(symbol_index(e), @NAME); } ; @@ -2333,23 +2534,100 @@ repo_program: PROGRAM_kw NAME repo_as repo_property: PROPERTY NAME repo_as ; -with_debug: %empty - | with DEBUGGING MODE { +with_debug: with DEBUGGING MODE { if( ! set_debug(true) ) { error_msg(@2, "DEBUGGING MODE valid only in fixed format"); } } ; -collating_sequence: %empty { $$ = NULL; } - | PROGRAM_kw COLLATING SEQUENCE is NAME[name] { $$ = $name; } - | PROGRAM_kw SEQUENCE is NAME[name] { $$ = $name; } - | COLLATING SEQUENCE is NAME[name] { $$ = $name; } - | SEQUENCE is NAME[name] { $$ = $name; } +object_computer: %empty + | char_classification + | collating_sequence + | char_classification collating_sequence + | collating_sequence char_classification + ; +char_classification: + character CLASSIFICATION char_class_locales[seq] + { + if( $seq.alpha ) { + auto e = symbol_locale(PROGRAM, $seq.alpha); + if( !e ) { + error_msg(@seq, "no LOCALE defined as %qs", $seq.alpha); + } else { + auto& encoding = cbl_locale_of(e)->encoding; + current.alpha_encoding(symbol_index(e), encoding); + } + } + if( $seq.national ) { + auto e = symbol_locale(PROGRAM, $seq.national); + if( !e ) { + error_msg(@seq, "no LOCALE defined as %qs", $seq.national); + } else { + auto& encoding = cbl_locale_of(e)->encoding; + current.national_encoding(symbol_index(e), encoding); + } + } + } + ; +collating_sequence: + program_kw collating SEQUENCE char_class_locales[seq] + { + if( !current.collating_sequence($seq.alpha) ) { + error_msg(@seq, "collating sequence already defined as '%s'", + current.collating_sequence()); + YYERROR; + } + } ; -specials: special_names +char_class_locales: + is NAME[name] { + $$.alpha = $name; + $$.national = nullptr; + } + | coll_alphanats { $$ = $1; } + ; +coll_alphanats: coll_alphanat[encoding] { + $$ = coll_alphanat_t(); + const char **pname = + $encoding.token == ALPHANUMERIC? &$$.alpha : &$$.national; + *pname = $encoding.name; + } + | coll_alphanats coll_alphanat[encoding] + { + const char **pname = + $encoding.token == ALPHANUMERIC? &$$.alpha : &$$.national; + if( *pname ) { + error_msg(@encoding, + "%qs is repeated", keyword_str($encoding.token)); + } + *pname = $encoding.name; + } ; +coll_alphanat: for alphanational is locale_phrase[locale] { + $$.token = $alphanational; + $$.name = $locale.name; + if( ! $locale.name ) { + const char *locale_name = "???"; + switch($locale.locale_type) { + case 'L': locale_name = "LOCALE"; break; + case 'D': locale_name = "DEFAULT"; break; + case 'S': locale_name = "SYSTEM-DEFAULT"; break; + case 'U': locale_name = "USER-DEFAULT"; break; + } + cbl_unimplemented("FOR %s IS %s", + keyword_str($$.token), + locale_name); + } + } + ; + +locale_phrase: NAME { $$.name = $1; $$.locale_type = '\0'; } + | LOCALE { $$.name = nullptr; $$.locale_type = 'L'; } + | DEFAULT { $$.name = nullptr; $$.locale_type = $1; } + ; + special_names: special_name | special_names special_name ; @@ -2362,12 +2640,26 @@ special_name: dev_mnemonic if( !namcpy(@name, $abc->name, $name) ) YYERROR; if( yydebug ) $abc->dump(); } + | ALPHABET NAME[name] for alphanational is alphabet_name[abc] + { + if( !$abc ) YYERROR; + assert($abc); // already in symbol table + if( !namcpy(@name, $abc->name, $name) ) YYERROR; + if( yydebug ) $abc->dump(); + const size_t isym = symbol_index(symbol_elem_of($abc)); + switch($alphanational) { + case ALPHANUMERIC: + current.alpha_encoding(isym, $abc->encoding); + break; + case NATIONAL: + current.national_encoding(isym, $abc->encoding); + break; + default: gcc_unreachable(); + } + } | CLASS NAME is domains { - struct cbl_field_t field = { 0, - FldClass, FldInvalid, 0, 0, 0, 0, nonarray, yylineno, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; + struct cbl_field_t field = { FldClass, 0, {}, 0, $NAME }; if( !namcpy(@NAME, field.name, $2) ) YYERROR; struct cbl_domain_t *domain = @@ -2377,6 +2669,7 @@ special_name: dev_mnemonic field.data.false_value_as($domains); field.data.domain_as(domain); + field.codeset.set(); domains.clear(); if( field_add(@2, &field) == NULL ) { @@ -2401,10 +2694,20 @@ special_name: dev_mnemonic { symbol_decimal_point_set(','); } - | LOCALE NAME is locale_spec + | LOCALE NAME is locale_spec[spec] { - current.locale($NAME, $locale_spec); - cbl_unimplemented("LOCALE syntax"); + cbl_locale_t locale($NAME, $spec); + if( locale.encoding == no_encoding_e ) { + error_msg(@NAME, "invalid iconv LOCALE name %qs", $spec); + YYERROR; + } + if( locale.encoding == UTF8_e ) { + cbl_unimplemented("UTF-8"); + YYERROR; + } + if( ! current.locale_add(locale) ) { + error_msg(@NAME, "%qs already defined as LOCALE name", $NAME); + } } ; | upsi @@ -2414,6 +2717,8 @@ special_name: dev_mnemonic } ; locale_spec: NAME { $$ = $1; } + | UTF_8 { static char s[] ="UTF-8"; $$ = s; } + | UTF_16 { static char s[] ="UTF-16"; $$ = s; } | LITERAL { $$ = string_of($1); } ; @@ -2453,23 +2758,14 @@ dev_mnemonic: device_name is NAME } | NAME[device] is NAME[name] { - static const std::map< std::string, special_name_t > fujitsus - { // Fujitsu calls these "function names", not device names - { "ARGUMENT-NUMBER", ARG_NUM_e }, - { "ARGUMENT-VALUE", ARG_VALUE_e } , - { "ENVIRONMENT-NAME", ENV_NAME_e }, - { "ENVIRONMENT-VALUE", ENV_VALUE_e }, - }; - std::string device($device); - std::transform($device, $device + strlen($device), - device.begin(), toupper); - auto p = fujitsus.find(device.c_str()); - if( p == fujitsus.end() ) { - error_msg(@device, "%s is not a device name"); + auto p = cmd_or_env_special_of($device); + if( !p ) { + error_msg(@device, "%s is not a device name", $device); + YYERROR; } - cbl_special_name_t special = { 0, p->second }; - if( !namcpy(@name, special.name, $name) ) YYERROR; + cbl_special_name_t special = { 0, *p }; + namcpy(@name, special.name, $name); symbol_special_add(PROGRAM, &special); } @@ -2505,13 +2801,29 @@ device_name: SYSIN { $$.token = SYSIN; $$.id = SYSIN_e; } | STDIN { $$.token = STDIN; $$.id = STDIN_e; } | STDOUT { $$.token = STDOUT; $$.id = STDOUT_e; } | STDERR { $$.token = STDERR; $$.id = STDERR_e; } + /* These cannot be both ctx_name and here. * + /* ARGUMENT_NUMBER { $$.token=0; $$.id = ARG_NUM_e; } */ + /* ENVIRONMENT_NAME { $$.token=0; $$.id = ENV_NAME_e; } */ + /* ENVIRONMENT_VALUE { $$.token=0; $$.id = ENV_VALUE_e; } */ ; -alphabet_name: STANDARD_ALPHABET { $$ = alphabet_add(@1, ASCII_e); } +alphabet_name: STANDARD_ALPHABET { $$ = alphabet_add(@1, CP1252_e); } | NATIVE { $$ = alphabet_add(@1, EBCDIC_e); } | EBCDIC { $$ = alphabet_add(@1, EBCDIC_e); } + | LOCALE locale_name[name] + { + auto e = symbol_locale(PROGRAM, $name); + if( !e ) { + dbgmsg("no such LOCALE yet %s", $name); + cbl_locale_t locale($name); // locale is named but not defined + e = symbol_locale_add(PROGRAM, &locale); + } + cbl_alphabet_t alphabet( @name, symbol_index(e), $name); + $$ = alphabet_add(alphabet); + } | alphabet_seqs { + $1->reencode(); $$ = cbl_alphabet_of(symbol_alphabet_add(PROGRAM, $1)); } | error @@ -2536,7 +2848,8 @@ alphabet_seqs: alphabet_seq[seq] YYERROR; } $$->add_sequence(@seq, $seq.low); - size_t len = $seq.low == nul_string()? 1 : strlen((const char*)$seq.low); + size_t len = $seq.low == nul_string()? + 1 : strlen((const char*)$seq.low); assert(len > 0); $$->add_interval(@seq, $seq.low[--len], $seq.high[0]); $$->add_sequence(@seq, $seq.high); @@ -2589,17 +2902,19 @@ alphabet_seq: alphabet_lit[low] alphabet_etc: alphabet_lit { if( $1.len > 1 ) { - error_msg(@1, "'%c' can be only a single letter", $1.data); + error_msg(@1, "%qs can be only a single letter", $1.data); YYERROR; } $$ = (unsigned char)$1.data[0]; } | spaces_etc { - // For figurative constants, pass the synmbol table index, + // For figurative constants, pass the symbol table index, // marked with the high bit. static const auto bits = sizeof($$) * 8 - 1; - $$ = 1; - $$ = $$ << bits; + unsigned int high_bit = 1L << bits; + static_assert(sizeof($$) == sizeof(high_bit), + "adjust high_bit to match size of nonterminal target"); + memcpy(&$$, &high_bit, sizeof($$)); $$ |= constant_index($1); } ; @@ -2708,22 +3023,12 @@ domains: domain domain: all LITERAL[a] { - if( ! string_of($a) ) { - gcc_location_set(@a); - yywarn("'%s' has embedded NUL", $a.data); - } $$ = NULL; cbl_domain_t domain(@a, $all, $a.len, $a.data); domains.push_back(domain); } | all[a_all] LITERAL[a] THRU all[z_all] LITERAL[z] { - if( ! string_of($a) ) { - yywarn("'%s' has embedded NUL", $a.data); - } - if( ! string_of($z) ) { - yywarn("'%s' has embedded NUL", $z.data); - } $$ = NULL; cbl_domain_elem_t first(@a, $a_all, $a.len, $a.data), last(@z, $z_all, $z.len, $z.data); @@ -2750,9 +3055,6 @@ domain: all LITERAL[a] domains.push_back(domain); } | all[a_all] reserved_value[a] THRU all[z_all] LITERAL[z] { - if( ! string_of($z) ) { - yywarn("'%s' has embedded NUL", $z.data); - } $$ = NULL; if( $a == NULLS ) YYERROR; auto value = constant_of(constant_index($a))->data.initial; @@ -2770,10 +3072,7 @@ domain: all LITERAL[a] } | when_set_to FALSE_kw is LITERAL[value] { - if( ! string_of($value) ) { - yywarn("'%s' has embedded NUL", $value.data); - } - char *dom = $value.data; + const char *dom = $value.data; $$ = new cbl_domain_t(@value, false, $value.len, dom); } | when_set_to FALSE_kw is reserved_value @@ -2853,7 +3152,7 @@ fd_clause: record_desc f->varying_size.explicitly = f->varies(); if( f->varying_size.max != 0 ) { if( !(f->varying_size.min <= f->varying_size.max) ) { - error_msg(@1, "%zu must be <= %zu", + error_msg(@1, "%zu must be less than or equal to %zu", f->varying_size.min, f->varying_size.max); YYERROR; } @@ -2874,10 +3173,42 @@ fd_clause: record_desc error_msg(@NAME, "invalid RECORDING MODE '%s'", $NAME); YYERROR; } - cbl_unimplementedw("RECORDING MODE was ignored, not defined by ISO 2023"); + cbl_unimplementedw(SynRecordingMode, + "RECORDING MODE ignored"); } | VALUE OF fd_values - | CODESET is NAME + | CODESET is codeset_name[codeset] { + auto f = cbl_file_of(symbol_at(file_section_fd)); + f->codeset = cbl_file_t::codeset_t($codeset.encoding, + $codeset.isym); + cbl_unimplementedw(SynFileCodeSet, + "sorry, unimplemented CODE-SET"); + } + | CODESET for alphanational is codeset_name[codeset] + { + auto f = cbl_file_of(symbol_at(file_section_fd)); + f->codeset = cbl_file_t::codeset_t($codeset.encoding, + $codeset.isym); + if( $codeset.isym == 0 ) { + switch( $alphanational) { + case ALPHANUMERIC: + if( $codeset.encoding != ASCII_e ) { + error_msg(@alphanational, + "FOR ALPHANUMERIC: invalid codeset"); + } + break; + case NATIONAL: + if( $codeset.encoding != EBCDIC_e ) { + error_msg(@alphanational, + "FOR ALPHANUMERIC: invalid codeset"); + } + break; + default: + gcc_unreachable(); + } + } + cbl_unimplemented("CODE-SET"); + } | is GLOBAL { auto f = cbl_file_of(symbol_at(file_section_fd)); @@ -2892,40 +3223,62 @@ fd_clause: record_desc { auto f = cbl_file_of(symbol_at(file_section_fd)); f->attr |= external_e; - cbl_unimplemented("AS LITERAL "); + cbl_unimplemented("AS LITERAL"); } - | fd_linage + | fd_linage { cbl_unimplemented("LINAGE"); } | fd_report { cbl_unimplemented("REPORT WRITER"); YYERROR; } ; -block_desc: BLOCK contains rec_contains chars_recs +alphanational: ALPHANUMERIC { $$ = ALPHANUMERIC; } + | NATIONAL { $$ = NATIONAL; } + ; +codeset_name: STANDARD_ALPHABET { $$.isym = 0; $$.encoding = ASCII_e; } + | NATIVE { $$.isym = 0; $$.encoding = EBCDIC_e; } + | EBCDIC { $$.isym = 0; $$.encoding = EBCDIC_e; } + | NAME + { + auto e = symbol_alphabet(PROGRAM, $NAME); + if( !e ) { + error_msg(@NAME, "invalid CODE-SET: %qs", $NAME); + YYERROR; + } + $$.isym = symbol_index(e); + $$.encoding = custom_encoding_e; + } + ; + +block_desc: BLOCK_kw contains rec_contains chars_recs ; rec_contains: NUMSTR[min] { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = $$.max = n; // fixed length } | NUMSTR[min] TO NUMSTR[max] { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + rn = numstr2i($max.string, $max.radix); + n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@max, "size %s cannot be negative", $max.string); YYERROR; } $$.max = n; if( !($$.min < $$.max) ) { - error_msg(@max, "FROM (%xz) must be less than TO (%zu)", + error_msg(@max, "FROM (%zu) must be less than TO (%zu)", $$.min, $$.max); YYERROR; } @@ -2978,26 +3331,32 @@ in_size: IN SIZE ; from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + rn = numstr2i($max.string, $max.radix); + n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $max.string); YYERROR; } $$.max = n; } | NUMSTR[min] TO NUMSTR[max] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + rn = numstr2i($max.string, $max.radix); + n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@max, "size %s cannot be negative", $max.string); YYERROR; } @@ -3005,8 +3364,9 @@ from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { } | TO NUMSTR[max] characters { - ssize_t n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($max.string, $max.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@max, "size %s cannot be negative", $max.string); YYERROR; } @@ -3015,8 +3375,9 @@ from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { } | FROM NUMSTR[min] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } @@ -3024,8 +3385,9 @@ from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { $$.max = size_t(-1); } | NUMSTR[min] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } @@ -3049,7 +3411,7 @@ depending: %empty assert(e->type == SymField); odo = symbol_index(e); } else { - e = symbol_field_forward_add(PROGRAM, 0, $NAME, yylineno); + e = symbol_field_forward_add(PROGRAM, 0, $NAME, @NAME.first_line); if( !e ) YYERROR; symbol_field_location( symbol_index(e), @NAME ); odo = field_index(cbl_field_of(e)); @@ -3098,7 +3460,7 @@ field: cdf // Format data.initial per picture if( 0 == pristine_values.count(field.data.initial) ) { - if( field.data.digits > 0 && field.data.value_of() != 0.0 ) { + if( field.data.digits > 0 && !field.is_zero() ) { char *initial; int rdigits = field.data.rdigits < 0? 1 : field.data.rdigits + 1; @@ -3112,7 +3474,7 @@ field: cdf } initial = string_of(field.data.value_of()); if( !initial ) { - error_msg(@1, xstrerror(errno)); + error_msg(@1, "could not convert value to string"); YYERROR; } char decimal = symbol_decimal_point(); @@ -3145,7 +3507,7 @@ occurs_clause: OCCURS cardinal_lb indexed } cbl_occurs_t *occurs = ¤t_field()->occurs; occurs->bounds.lower = - occurs->bounds.upper = $name->data.value_of(); + occurs->bounds.upper = $name->as_integer(); } ; cardinal_lb: cardinal times { @@ -3156,7 +3518,8 @@ cardinal_lb: cardinal times { cardinal: NUMSTR[input] { - $$ = numstr2i( $input.string, $input.radix ); + REAL_VALUE_TYPE rn = numstr2i($input.string, $input.radix); + $$ = real_to_integer (&rn); } ; @@ -3217,11 +3580,11 @@ index_field1: ctx_name[name] field.data = data; if( !namcpy(@name, field.name, $name) ) YYERROR; - auto symbol = symbol_field(PROGRAM, 0, $name); + auto symbol = symbol_field(PROGRAM, field.parent, $name); if( symbol ) { - auto field( cbl_field_of(symbol) ); + auto f( cbl_field_of(symbol) ); error_msg(@name, "'%s' already defined on line %d", - field->name, field->line ); + f->name, f->line ); YYERROR; } @@ -3246,11 +3609,8 @@ level_name: LEVEL ctx_name error_msg(@LEVEL, "LEVEL %d not supported", $LEVEL); YYERROR; } - struct cbl_field_t field = { 0, - FldInvalid, FldInvalid, 0, 0, 0, capacity_cast($1), - nonarray, yylineno, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; + cbl_field_t field = { FldInvalid, capacity_cast($LEVEL), + @ctx_name.first_line }; if( !namcpy(@ctx_name, field.name, $2) ) YYERROR; $$ = field_add(@$, &field); @@ -3271,10 +3631,9 @@ level_name: LEVEL ctx_name error_msg(@LEVEL, "LEVEL %d not supported", $LEVEL); YYERROR; } - struct cbl_field_t field = { 0, - FldInvalid, FldInvalid, 0, 0, 0, capacity_cast($1), - nonarray, yylineno, "", - 0, {}, {}, NULL }; + struct cbl_field_t field = { FldInvalid, + capacity_cast($LEVEL), + @LEVEL.first_line }; $$ = field_add(@1, &field); if( !$$ ) { @@ -3287,36 +3646,40 @@ level_name: LEVEL ctx_name data_descr: data_descr1 { $$ = current_field($1); // make available for occurs, etc. - char *env = getenv("symbols_update"); - if( env && env[0] == 'P' ) { - dbgmsg("parse.y:%d: %-15s %s (%s)", __LINE__, - cbl_field_type_str($$->type) + 3, - field_str($$), - cbl_field_type_str($$->usage) + 3); - } } | error { static cbl_field_t none = {}; $$ = &none; } ; const_value: cce_expr - | BYTE_LENGTH of name { $$ = $name->data.capacity; } - | LENGTH of name { $$ = $name->data.capacity; } - | LENGTH_OF of name { $$ = $name->data.capacity; } + | BYTE_LENGTH of name { set_real_from_capacity(@name, $name, &$$); } + | LENGTH of name { set_real_from_capacity(@name, $name, &$$); } + | LENGTH_OF of name { set_real_from_capacity(@name, $name, &$$); } + | LENGTH_OF of binary_type[type] { + real_from_integer(&$$, VOIDmode, $type, SIGNED); } ; value78: literalism { - cbl_field_data_t data = {}; - data.capacity = capacity_cast(strlen($1.data)); - data.initial = $1.data; - $$ = new cbl_field_data_t(data); + cbl_field_data_t data; + data.capacity = capacity_cast(strlen($1.data)); + data.initial = $1.data; + $$.encoding = $1.encoding; + $$.data = new cbl_field_data_t(data); } | const_value { - cbl_field_data_t data = {}; - data = $1; - $$ = new cbl_field_data_t(data); + cbl_field_data_t data; + data = build_real (float128_type_node, $1); + $$.encoding = current_encoding('A'); + $$.data = new cbl_field_data_t(data); } + | reserved_value[value] + { + const auto field = constant_of(constant_index($value)); + $$.encoding = current_encoding('A'); + $$.data = new cbl_field_data_t(field->data); + } + | true_false { cbl_unimplemented("Boolean constant"); @@ -3343,13 +3706,29 @@ data_descr1: level_name field.attr |= constant_e; if( $is_global ) field.attr |= global_e; field.type = FldLiteralN; - field.data = $const_value; + field.data = build_real (float128_type_node, $const_value); field.data.initial = string_of($const_value); + field.codeset.set(); - if( !cdf_value(field.name, static_cast<int64_t>($const_value)) ) { + if( !cdf_value(field.name, cdfval_t($const_value)) ) { error_msg(@1, "%s was defined by CDF", field.name); } } + + | level_name CONSTANT is_global as reserved_value[value] + { + cbl_field_t& field = *$1; + if( field.level != 1 ) { + error_msg(@1, "%s must be an 01-level data item", field.name); + YYERROR; + } + field.attr |= constant_e; + if( $is_global ) field.attr |= global_e; + field.type = FldLiteralA; + auto fig = constant_of(constant_index($value)); + field.data = fig->data; + } + | level_name CONSTANT is_global as literalism[lit] { cbl_field_t& field = *$1; @@ -3366,6 +3745,11 @@ data_descr1: level_name if( !cdf_value(field.name, $lit.data) ) { error_msg(@1, "%s was defined by CDF", field.name); } + if( ! field.codeset.set() ) { + error_msg(@lit, "CONSTANT inconsistent with encoding %s", + cbl_alphabet_t::encoding_str(field.codeset.encoding)); + } + value_encoding_check(@lit, $1); } | level_name CONSTANT is_global FROM NAME @@ -3389,25 +3773,23 @@ data_descr1: level_name | LEVEL78 NAME[name] VALUE is value78[data] { - if( ! dialect_mf() ) { - dialect_error(@1, "level 78", "mf"); - YYERROR; - } - struct cbl_field_t field = { 0, FldLiteralA, FldInvalid, - constant_e, 0, 0, 78, nonarray, - yylineno, "", 0, {}, *$data, NULL }; - if( !namcpy(@name, field.name, $name) ) YYERROR; + dialect_ok(@1, MfLevel78, "LEVEL 78"); + cbl_field_t field = { FldLiteralA, constant_e, *$data.data, + 78, $name, @name.first_line }; if( field.data.initial ) { field.attr |= quoted_e; + field.codeset.set($data.encoding); if( !cdf_value(field.name, field.data.initial) ) { - yywarn("%s was defined by CDF", field.name); + cbl_message(Par78CdfDefinedW, + "%s was defined by CDF", field.name); } } else { field.type = FldLiteralN; field.data.initial = string_of(field.data.value_of()); - if( !cdf_value(field.name, - static_cast<int64_t>(field.data.value_of())) ) { - yywarn("%s was defined by CDF", field.name); + field.codeset.set($data.encoding); + if( !cdf_value(field.name, field.as_integer()) ) { + cbl_message(Par78CdfDefinedW, + "%s was defined by CDF", field.name); } } if( ($$ = field_add(@name, &field)) == NULL ) { @@ -3418,10 +3800,8 @@ data_descr1: level_name | LEVEL88 NAME /* VALUE */ NULLPTR { - struct cbl_field_t field = { 0, - FldClass, FldInvalid, 0, 0, 0, 88, nonarray, yylineno, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; + struct cbl_field_t field = {FldClass, 0, {}, + 88, $NAME, @NAME.first_line}; if( !namcpy(@NAME, field.name, $2) ) YYERROR; auto fig = constant_of(constant_index(NULLS))->data.initial; @@ -3444,19 +3824,16 @@ data_descr1: level_name } | LEVEL88 NAME VALUE domains { - struct cbl_field_t field = { 0, - FldClass, FldInvalid, 0, 0, 0, 88, nonarray, yylineno, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; - if( !namcpy(@NAME, field.name, $2) ) YYERROR; - - struct cbl_domain_t *domain = + cbl_field_t field = { + FldClass, 0, {}, 88, $NAME, @NAME.first_line}; + cbl_domain_t *domain = new cbl_domain_t[ domains.size() + 1]; std::copy(domains.begin(), domains.end(), domain); field.data.domain_as(domain); field.data.false_value_as($domains); + field.codeset.set(); domains.clear(); if( ($$ = field_add(@2, &field)) == NULL ) { @@ -3550,7 +3927,7 @@ data_descr1: level_name } if( field_index($thru) <= field_index($orig) ) { error_msg(@orig, "cannot RENAME %s %s THRU %s %s " - "because they're in the wrong order", + "because they are in the wrong order", $orig->level_str(), name_of($orig), $thru->level_str(), name_of($thru)); YYERROR; @@ -3592,7 +3969,7 @@ data_descr1: level_name case FldNumericEdited: if( $field->has_attr(signable_e) ) { error_msg(@2, "%s has 'S' in PICTURE, cannot be BLANK WHEN ZERO", - $field->name, cbl_field_type_str($field->type) ); + $field->name ); } break; default: @@ -3605,12 +3982,12 @@ data_descr1: level_name // SIGN clause valid only with "S" in picture if( $field->type == FldNumericDisplay && !is_signable($field) ) { - static const size_t sign_attrs = leading_e | separate_e; + static const uint64_t sign_attrs = leading_e | separate_e; static_assert(sizeof(sign_attrs) == sizeof($field->attr), "size matters"); // remove inapplicable inherited sign attributes - size_t group_sign = group_attr($field) & sign_attrs; + uint64_t group_sign = group_attr($field) & sign_attrs; $field->attr &= ~group_sign; if( $field->attr & sign_attrs ) { @@ -3653,15 +4030,14 @@ data_descr1: level_name } // Ensure signed initial VALUE is for signed numeric type - if( is_numeric($field) && - $field->data.initial && - $field->type != FldFloat ) - { - switch( $field->data.initial[0] ) { - case '-': - if( !$field->has_attr(signable_e) ) { - error_msg(@field, "%s is unsigned but has signed VALUE '%s'", - $field->name, $field->data.initial); + if( is_numeric($field) ) { + if( $field->data.initial && $field->type != FldFloat ) { + switch( $field->data.initial[0] ) { + case '-': + if( !$field->has_attr(signable_e) ) { + error_msg(@field, "%s is unsigned but has signed VALUE '%s'", + $field->name, $field->data.initial); + } } } } @@ -3669,8 +4045,17 @@ data_descr1: level_name // Verify VALUE $field->report_invalid_initial_value(@data_clauses); + bool numerical = + $field->type == FldNumericDisplay || is_numeric($field); + + if( $field->data.initial && ! numerical ) { + if( normal_value_e == cbl_figconst_of($field->data.initial) ) { + value_encoding_check(@data_clauses, $field); + } + } + // verify REDEFINES - auto parent = parent_of($field); + const auto parent = parent_of($field); if( parent && $field->level == parent->level ) { valid_redefine(@field, $field, parent); // calls yyerror } @@ -3691,8 +4076,9 @@ literalism: LITERAL { $$ = $1; } if( $second.prefix[0] ) { strcpy(output.prefix, $second.prefix); } if( ! $first.compatible_prefix($second) ) { - yywarn("dissimilar literals, '%s' prevails", - output.prefix); + cbl_message(@$, ParLiteral2W, + "dissimilar literals, '%s' prevails", + output.prefix); } } ; @@ -3766,7 +4152,7 @@ data_clauses: data_clause // If any implied TYPE bits are on in addition to // type_clause_e, they're in conflict. - static const size_t type_implies = + static const uint64_t type_implies = // ALIGNED clause not implemented blank_zero_clause_e | justified_clause_e | picture_clause_e | sign_clause_e | synched_clause_e | usage_clause_e; @@ -3802,12 +4188,11 @@ data_clauses: data_clause if( field->is_binary_integer() && field->data.capacity == 4) { auto redefined = symbol_redefines(field); if( redefined && redefined->type == FldPointer ) { - if( yydebug ) { - yywarn("expanding %s size from %u bytes to %zu " - "because it redefines %s with USAGE POINTER", - field->name, field->size(), sizeof(void*), - redefined->name); - } + dbgmsg("expanding %s size from %u bytes to %lu " + "because it redefines %s with USAGE POINTER", + field->name, field->size(), + int_size_in_bytes(ptr_type_node), + redefined->name); field->embiggen(); } } @@ -3841,7 +4226,7 @@ data_clause: any_length { $$ = any_length_e; } cbl_field_t *field = current_field(); switch( field->level ) { case 1: - if( dialect_mf() ) break; + if( dialect_ok(@$, MfLevel_1_Occurs, "LEVEL 01 for OCCURS") ) break; __attribute__((fallthrough)); case 77: case 88: @@ -3896,7 +4281,7 @@ picture_clause: PIC signed nps[fore] nines nps[aft] field->data.capacity = type_capacity(field->type, $4); field->data.digits = $4; if( long(field->data.digits) != $4 ) { - error_msg(@2, "indicated size would be %ld bytes, " + error_msg(@2, "indicated size would be %d bytes, " "maximum data item size is %u", $4, UINT32_MAX); } @@ -3919,16 +4304,17 @@ picture_clause: PIC signed nps[fore] nines nps[aft] { cbl_field_t *field = current_field(); field->data.digits = $left + $rdigits; + field->attr |= $signed; if( field->is_binary_integer() ) { field->data.capacity = type_capacity(field->type, field->data.digits); + field->data.rdigits = $rdigits; } else { if( !field_type_update(field, FldNumericDisplay, @$) ) { YYERROR; } ERROR_IF_CAPACITY(@PIC, field); - field->attr |= $signed; field->data.capacity = field->data.digits; field->data.rdigits = $rdigits; } @@ -3962,15 +4348,16 @@ picture_clause: PIC signed nps[fore] nines nps[aft] cbl_field_t *field = current_field(); if( field->type == FldNumericBin5 && - field->data.capacity == 0 && - dialect_mf() ) + field->data.capacity == 0xFF && + dialect_ok(@2, MfCompX, "alphanumeric PICTURE with numeric USAGE") ) { // PIC X COMP-X or COMP-9 if( ! field->has_attr(all_x_e) ) { - error_msg(@2, "COMP PICTURE requires all X's or all 9's"); + error_msg(@2, "COMP PICTURE requires all X%'s or all 9%'s"); YYERROR; } } else { if( !field_type_update(field, FldAlphanumeric, @$) ) { + dbgmsg("alnum_pic: %s", field_str(field)); YYERROR; } } @@ -3978,14 +4365,16 @@ picture_clause: PIC signed nps[fore] nines nps[aft] if( field->data.initial != NULL ) { if( 0 < field->data.capacity && field->data.capacity < uint32_t($size) ) { - auto p = blank_pad_initial( field->data.initial, - field->data.capacity, $size ); + auto p = blank_pad_initial(field->data.initial, + field->data.capacity, $size ); if( !p ) YYERROR; field->data.initial = p; } } - field->data.capacity = $size; + charmap_t *charmap = + __gg__get_charmap(field->codeset.encoding); + field->data.capacity = $size * charmap->stride(); field->data.picture = NULL; if( false ) dbgmsg("PIC alphanum_pic[size]:%d: %s", @@ -4000,7 +4389,7 @@ picture_clause: PIC signed nps[fore] nines nps[aft] } ERROR_IF_CAPACITY(@PIC, field); if( !is_numeric_edited($picture) ) { - error_msg(@picture, numed_message); + error_msg(@picture, "%s", numed_message); YYERROR; } field->data.picture = $picture; @@ -4042,7 +4431,13 @@ picture_clause: PIC signed nps[fore] nines nps[aft] gcc_unreachable(); } } + | PIC ones ; +ones: ONES + { + cbl_unimplemented("Boolean type not implemented"); + } + ; alphanum_pic: alphanum_part { current_field()->set_attr($1.attr); @@ -4065,6 +4460,11 @@ alphanum_pic: alphanum_part { ; alphanum_part: ALNUM[picture] count { + auto field = current_field(); + if( ! field->codeset.set($picture) ) { + error_msg(@picture, "PICTURE inconsistent with encoding %s", + cbl_alphabet_t::encoding_str(field->codeset.encoding)); + } $$.attr = uniform_picture($picture); $$.nbyte = strlen($picture); auto count($count); @@ -4073,7 +4473,7 @@ alphanum_part: ALNUM[picture] count $$.nbyte += count; // AX9(3) has count 5 } if( count < 0 ) { - error_msg(@2, "PICTURE count '(%d)' is negative", count ); + error_msg(@2, "PICTURE count %<(%d)%> is negative", count ); YYERROR; } } @@ -4092,7 +4492,7 @@ nine: %empty { $$ = 0; } { $$ = $1; if( $$ == 0 ) { - error_msg(@1, "'(0)' invalid in PICTURE (ISO 2023 13.18.40.3)"); + error_msg(@1, "%<(0)%> invalid in PICTURE (ISO 2023 13.18.40.3)"); } } ; @@ -4103,16 +4503,17 @@ nines: NINES count: %empty { $$ = 0; } | '(' NUMSTR ')' { - $$ = numstr2i( $NUMSTR.string, $NUMSTR.radix ); + REAL_VALUE_TYPE rn = numstr2i($NUMSTR.string, $NUMSTR.radix); + $$ = real_to_integer (&rn); if( $$ == 0 ) { - error_msg(@2, "'(0)' invalid in PICTURE (ISO 2023 13.18.40.3)"); + error_msg(@2, "%<0%> invalid in PICTURE (ISO 2023 13.18.40.3)"); } } | '(' NAME ')' { auto value = cdf_value($NAME); if( ! (value && value->is_numeric()) ) { - error_msg(@NAME, "PICTURE '(%s)' requires a CONSTANT value", $NAME ); + error_msg(@NAME, "PICTURE %qs requires a CONSTANT value", $NAME ); YYERROR; } int nmsg = 0; @@ -4120,15 +4521,18 @@ count: %empty { $$ = 0; } if( e ) { // verify not floating point with nonzero fraction auto field = cbl_field_of(e); assert(is_literal(field)); - if( field->data.value_of() != size_t(field->data.value_of()) ) { + REAL_VALUE_TYPE vi; + real_from_integer (&vi, VOIDmode, field->as_integer(), SIGNED); + if( !real_identical (TREE_REAL_CST_PTR (field->data.value_of()), + &vi) ) { nmsg++; - error_msg(@NAME, "invalid PICTURE count '(%s)'", + error_msg(@NAME, "invalid PICTURE count %<(%s)%>", field->data.initial ); } } $$ = value->as_number(); if( $$ <= 0 && !nmsg) { - error_msg(@NAME, "invalid PICTURE count '(%s)'", $NAME ); + error_msg(@NAME, "invalid PICTURE count %<(%s)%>", $NAME ); } } ; @@ -4147,11 +4551,21 @@ usage_clause: usage_clause1[type] } } ; -usage_clause1: usage COMPUTATIONAL[comp] native +usage_clause1: usage BIT + { + cbl_unimplemented("Boolean type not implemented"); + } + | usage BINARY_INTEGER [comp] is_signed { + // action for BINARY_INTEGER is repeated for COMPUTATIONAL, below. + // If it changes, consolidate in a function. bool infer = true; cbl_field_t *field = current_field(); + if( ! $is_signed ) { + $comp.signable = false; + } + // Some binary types have defined capacity; switch($comp.type) { // COMPUTATIONAL and COMP-5 rely on PICTURE. @@ -4167,21 +4581,15 @@ usage_clause1: usage COMPUTATIONAL[comp] native case FldAlphanumeric: // PIC X COMP-5 or COMP-X assert( field->data.digits == 0 ); assert( field->data.rdigits == 0 ); - if( dialect_mf() ) { - field->type = $comp.type; - field->clear_attr(signable_e); - } else { - error_msg(@comp, "numeric USAGE invalid " - "with Alpnanumeric PICTURE"); - YYERROR; - } + dialect_ok(@2, MfCompX, "alphanumeric PICTURE with numeric USAGE"); + + field->type = $comp.type; + field->clear_attr(signable_e); break; case FldNumericDisplay: // PIC 9 COMP-5 or COMP-X if( $comp.capacity == 0xFF ) { // comp-x is a bit like comp-5 assert( field->data.digits == field->data.capacity ); - if( ! dialect_mf() ) { - dialect_error(@1, "COMP-X", "mf"); - } + dialect_ok(@2, MfCompX, "alphanumeric PICTURE with numeric USAGE"); } field->type = $comp.type; field->data.capacity = type_capacity(field->type, @@ -4194,9 +4602,78 @@ usage_clause1: usage COMPUTATIONAL[comp] native case FldPacked: // comp-6 is unsigned comp-3 assert(! $comp.signable); // else PACKED_DECIMAL from scanner field->attr |= separate_e; - if( ! dialect_mf() ) { - dialect_error(@1, "COMP-6", "mf"); + dialect_ok(@2, MfComp6, "COMP-6"); + if( field->type == FldNumericDisplay ) {// PICTURE before USAGE + infer = false; + assert(field->data.capacity > 0); + field->type = $comp.type; + field->data.capacity = type_capacity(field->type, + field->data.digits); + } + break; + default: + break; + } + + if( infer ) { + if( $comp.capacity > 0 ) { + if( field->data.capacity > 0 ) { + error_msg(@comp, "%s is BINARY type, incompatible with PICTURE", + field->name); + YYERROR; + } + field->data.capacity = $comp.capacity; + field->type = $comp.type; + if( $comp.signable ) { + field->attr = (field->attr | signable_e); + } + } + } + $$ = $comp.type; + } + | usage COMPUTATIONAL[comp] native + { + // logic below duplicates BINARY_INTEGER, above. + // If it changes, consolidate in a function. + bool infer = true; + cbl_field_t *field = current_field(); + + // Some binary types have defined capacity; + switch($comp.type) { + // COMPUTATIONAL and COMP-5 rely on PICTURE. + case FldNumericBinary: + field->attr |= big_endian_e; + __attribute__((fallthrough)); + case FldNumericBin5: + // If no capacity yet, then no picture, infer $comp.capacity. + // If field has capacity, ensure USAGE is compatible. + if( field->data.capacity > 0 ) { // PICTURE before USAGE + infer = false; + switch( field->type ) { + case FldAlphanumeric: // PIC X COMP-5 or COMP-X + assert( field->data.digits == 0 ); + assert( field->data.rdigits == 0 ); + dialect_ok(@2, MfCompX, "alphanumeric PICTURE with numeric USAGE"); + field->type = $comp.type; + field->clear_attr(signable_e); + break; + case FldNumericDisplay: // PIC 9 COMP-5 or COMP-X + if( $comp.capacity == 0xFF ) { // comp-x is a bit like comp-5 + assert( field->data.digits == field->data.capacity ); + dialect_ok(@2, MfCompX, "alphanumeric PICTURE with numeric USAGE"); + } + field->type = $comp.type; + field->data.capacity = type_capacity(field->type, + field->data.digits); + break; + default: break; + } } + break; + case FldPacked: // comp-6 is unsigned comp-3 + assert(! $comp.signable); // else PACKED_DECIMAL from scanner + field->attr |= separate_e; + dialect_ok(@2, MfComp6, "COMP-6"); if( field->type == FldNumericDisplay ) {// PICTURE before USAGE infer = false; assert(field->data.capacity > 0); @@ -4248,6 +4725,13 @@ usage_clause1: usage COMPUTATIONAL[comp] native | usage INDEX { $$ = symbol_field_index_set( current_field() )->type; } + | usage NATIONAL { + auto field = current_field(); + if( ! field->codeset.set(EBCDIC_e) ) { + error_msg(@2, "usage NATIONAL conflicts with PICTURE"); + } + $$ = FldInvalid; + } // We should enforce data/code pointers with a different type. | usage POINTER { @@ -4261,9 +4745,10 @@ usage_clause1: usage COMPUTATIONAL[comp] native if( gcobol_feature_embiggen() && redefined && is_numeric(redefined->type) && redefined->size() == 4) { // For now, we allow POINTER to expand a 32-bit item to 64 bits. - field->data.capacity = sizeof(void *); - dbgmsg("%s: expanding #%zu %s capacity %u => %u", __func__, - field_index(redefined), redefined->name, + field->data.capacity = int_size_in_bytes(ptr_type_node); + dbgmsg("%s: expanding #" HOST_SIZE_T_PRINT_UNSIGNED + " %s capacity %u => %u", __func__, + (fmt_size_t)field_index(redefined), redefined->name, redefined->data.capacity, field->data.capacity); redefined->embiggen(); @@ -4284,10 +4769,23 @@ usage_clause1: usage COMPUTATIONAL[comp] native value_clause: VALUE all LITERAL[lit] { cbl_field_t *field = current_field(); + + if( $lit.prefix[0] ) { // not the default encoding + if( ! field->codeset.set($lit.encoding) ) { + error_msg(@lit, "VALUE inconsistent with encoding %s", + cbl_alphabet_t::encoding_str(field->codeset.encoding)); + } + } else { + field->codeset.set(); + } + + if( field->codeset.encoding != $lit.encoding ) { + error_msg(@lit, "PICTURE inconsistent with VALUE %s'%s'", + $lit.prefix, $lit.data); + } + field->data.initial = $lit.data; field->attr |= literal_attr($lit.prefix); - // The __gg__initialize_data routine needs to know that VALUE is a - // quoted literal. This is critical for NumericEdited variables field->attr |= quoted_e; if( field->data.capacity == 0 ) { @@ -4304,15 +4802,16 @@ value_clause: VALUE all LITERAL[lit] { } } } - value_encoding_check(@lit, field); } | VALUE all cce_expr[value] { cbl_field_t *field = current_field(); auto orig_str = original_number(); - auto orig_val = numstr2i(orig_str, decimal_e); + REAL_VALUE_TYPE orig_val; + real_from_string3 (&orig_val, orig_str, + TYPE_MODE (float128_type_node)); char *initial = NULL; - if( orig_val == $value ) { + if( real_identical (&orig_val, &$value) ) { initial = orig_str; pristine_values.insert(initial); } else { @@ -4324,21 +4823,28 @@ value_clause: VALUE all LITERAL[lit] { std::replace(initial, initial + strlen(initial), '.', decimal); field->data.initial = initial; - field->data = $value; + field->data = build_real (float128_type_node, $value); if( $all ) field_value_all(field); } | VALUE all reserved_value[value] { + cbl_field_t *field = current_field(); + if( ! field->codeset.set() ) { + error_msg(@value, "VALUE inconsistent with encoding %s", + cbl_alphabet_t::encoding_str(field->codeset.encoding)); + } if( $value != NULLS ) { auto fig = constant_of(constant_index($value)); - current_field()->data.initial = fig->data.initial; + cbl_field_t *field = current_field(); + field->data.initial = fig->data.initial; } } | /* VALUE is */ NULLPTR { auto fig = constant_of(constant_index(NULLS)); - current_field()->data.initial = fig->data.initial; + cbl_field_t *field = current_field(); + field->data.initial = fig->data.initial; } | VALUE error { @@ -4368,7 +4874,7 @@ justified_clause: is JUSTIFIED redefines_clause: REDEFINES NAME[orig] { - struct symbol_elem_t *e = field_of($orig); + struct symbol_elem_t *e = symbol_field(PROGRAM, 0, $orig); if( !e ) { error_msg(@2, "REDEFINES target not defined"); YYERROR; @@ -4428,10 +4934,13 @@ any_length: ANY LENGTH if( field->attr & any_length_e ) { error_msg(@1, "ANY LENGTH already set"); } + const char *prog_name = current.program()->name; + bool is_compat = 0 < compat_programs.count(prog_name); if( ! (field->level == 1 && current_data_section == linkage_datasect_e && (1 < current.program_level() || - current.program()->is_function())) ) { + current.program()->is_function() || + is_compat)) ) { error_msg(@1, "ANY LENGTH valid only for 01 " "in LINKAGE SECTION of a function or contained program"); YYERROR; @@ -4504,7 +5013,7 @@ same_clause: SAME AS name YYERROR; } - auto e = symbol_field_same_as( field, other ); + const auto e = symbol_field_same_as( field, other ); symbol_field_location( symbol_index(e), @name ); } ; @@ -4515,7 +5024,7 @@ sign_clause: sign_is sign_leading sign_separate if( $sign_leading ) { field->attr |= leading_e; } else { - field->attr &= ~size_t(leading_e); // turn off in case inherited + field->attr &= ~uint64_t(leading_e); // turn off in case inherited field->attr |= signable_e; } if( $sign_separate ) field->attr |= separate_e; @@ -4553,19 +5062,16 @@ type_clause: TYPE to typename { cbl_field_t *field = current_field(); if( $typename ) { - auto e = symbol_field_same_as(field, $typename); + const auto e = symbol_field_same_as(field, $typename); symbol_field_location( symbol_index(e), @typename ); } } | USAGE is typename { - if( ! dialect_mf() ) { - dialect_error(@typename, "USAGE TYPENAME", "mf"); - YYERROR; - } + dialect_ok(@typename, MfUsageTypename, "USAGE TYPENAME"); cbl_field_t *field = current_field(); if( $typename ) { - auto e = symbol_field_same_as(field, $typename); + const auto e = symbol_field_same_as(field, $typename); symbol_field_location( symbol_index(e), @typename ); } } @@ -4580,6 +5086,7 @@ typedef_clause: is TYPEDEF strong error_msg(@2, "%s %s IS TYPEDEF must be level 01", field->level_str(), field->name); } + field->codeset.set(); field->attr |= typedef_e; if( $strong ) field->attr |= strongdef_e; if( ! current.typedef_add(field) ) { @@ -4596,10 +5103,8 @@ typedef_clause: is TYPEDEF strong volatile_clause: VOLATILE { - if( dialect_ibm() ) { - yywarn("VOLATILE has no effect"); - } else { - dialect_error(@1, "VOLATILE", "ibm"); + if( dialect_ok(@1, IbmVolatileE, "VOLATILE") ) { + cbl_message(@1, IbmVolatileW, "VOLATILE has no effect"); } } ; @@ -4668,6 +5173,7 @@ by_value_arg: scalar declaratives: %empty | DECLARATIVES '.' <label>{ + cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); current.enabled_exception_cache = enabled_exceptions; enabled_exceptions.clear(); current.doing_declaratives(true); @@ -4686,6 +5192,7 @@ declaratives: %empty * forward reference, because we haven't yet begun to parse * nondeclarative procedures. */ + cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); parser_label_label($label); enabled_exceptions = current.enabled_exception_cache; current.enabled_exception_cache.clear(); @@ -4754,10 +5261,9 @@ sentence: statements '.' std::set<std::string> externals = current.end_program(); if( !externals.empty() ) { for( const auto& name : externals ) { - yywarn("%s calls external symbol '%s'", + dbgmsg("%s calls external symbol '%s'", prog->name, name.c_str()); } - YYERROR; } // pointer still valid because name is in symbol table ast_end_program(prog->name); @@ -4778,12 +5284,11 @@ statements: statement { $$ = $1; } statement: error { if( current.declarative_section_name() ) { - error_msg(@1, "missing END DECLARATIVES or SECTION name", - nparse_error); + error_msg(@1, "missing END DECLARATIVES or SECTION name"); YYABORT; } if( max_errors_exceeded(nparse_error) ) { - error_msg(@1, "max errors %d reached", nparse_error); + error_msg(@1, "max errors %zu reached", nparse_error); YYABORT; } } @@ -4801,6 +5306,7 @@ statement: error { | divide { $$ = DIVIDE; } | entry { $$ = ENTRY; } | evaluate { $$ = EVALUATE; } + | exhibit_stmt { $$ = EXHIBIT; } | exit { $$ = EXIT; } | free { $$ = FREE; } | go_to { $$ = GOTO; } @@ -4827,13 +5333,14 @@ statement: error { | subtract { $$ = SUBTRACT; } | unstring { $$ = UNSTRING; } | write { $$ = WRITE; } - ; + | xmlgenerate { $$ = XMLGENERATE; } + | xmlparse { $$ = XMLPARSE; } + ; /* * ISO defines ON EXCEPTION only for Format 3 (screen). We - * implement extensions defined by MF and Fujitsu (and us) to - * use ACCEPT to interact with the command line and the - * environment. + * implement extensions defined by MF and Fujitsu to use ACCEPT + * to interact with the command line and the environment. * * ISO ACCEPT and some others are implemented in accept_body, * before the parser sees any ON EXCEPTION. In those cases @@ -4848,6 +5355,9 @@ accept: accept_body end_accept { switch( $accept_body.func ) { case accept_done_e: break; + case accept_e: + parser_accept(*$1.into, $1.special, nullptr, nullptr); + break; case accept_command_line_e: if( $1.from->field == NULL ) { // take next command-line arg parser_accept_command_line(*$1.into, argi, NULL, NULL); @@ -4869,7 +5379,10 @@ accept: accept_body end_accept { switch( $accept_body.func ) { case accept_done_e: error_msg(@ec, "ON EXCEPTION valid only " - "with ENVIRONMENT or COMAMND-LINE(n)"); + "with ENVIRONMENT or COMMAND-LINE(n)"); + break; + case accept_e: + parser_accept(*$1.into, $1.special, $ec.on_error, $ec.not_error); break; case accept_command_line_e: if( $1.from->field == NULL ) { // take next command-line arg @@ -4881,7 +5394,7 @@ accept: accept_body end_accept { parser_move(*$1.into, *$1.from); if( $ec.on_error || $ec.not_error ) { error_msg(@ec, "ON EXCEPTION valid only " - "with ENVIRONMENT or COMAMND-LINE(n)"); + "with ENVIRONMENT or COMMAND-LINE(n)"); } } else { parser_accept_command_line(*$1.into, *$1.from, @@ -4902,7 +5415,7 @@ end_accept: %empty %prec ACCEPT accept_body: accept_refer { $$.func = accept_done_e; - parser_accept(*$1, CONSOLE_e); + parser_accept(*$1, CONSOLE_e, nullptr, nullptr); } | accept_refer FROM DATE { @@ -4961,29 +5474,15 @@ accept_body: accept_refer } | accept_refer FROM acceptable { - cbl_field_t *argc = register_find("_ARGI"); - switch( $acceptable->id ) { - case ARG_NUM_e: - $$.func = accept_command_line_e; - $$.into = $1; - $$.from = new_reference(argc); - break; - case ARG_VALUE_e: - $$.func = accept_command_line_e; - $$.into = $1; - $$.from = cbl_refer_t::empty(); - break; - default: - $$.func = accept_done_e; - parser_accept( *$1, $acceptable->id ); - } + $$.func = accept_e; + $$.into = $1; + $$.special = $acceptable->id; } | accept_refer FROM ENVIRONMENT envar { $$.func = accept_envar_e; $$.into = $1; $$.from = $envar; - //// parser_accept_envar( *$1, *$envar ); } | accept_refer FROM COMMAND_LINE { @@ -4995,7 +5494,6 @@ accept_body: accept_refer $$.func = accept_command_line_e; $$.into = $1; $$.from = $expr; - //// parser_accept_command_line(*$1, $expr->field ); } | accept_refer FROM COMMAND_LINE_COUNT { $$.func = accept_done_e; @@ -5048,7 +5546,7 @@ accept_except: EXCEPTION { $$.not_error = NULL; $$.on_error = label_add(LblArith, - uniq_label("accept"), yylineno); + uniq_label("accept"), @1.first_line); if( !$$.on_error ) YYERROR; parser_accept_exception( $$.on_error ); @@ -5078,9 +5576,58 @@ acceptable: device_name { $$ = special_of($1); if( !$$ ) { - error_msg(@NAME, "no such environment mnemonic name: %s", $NAME); - YYERROR; - } + const special_name_t *special_type = cmd_or_env_special_of($NAME); + if( !special_type ) { + error_msg(@NAME, "no such special name '%s'", $NAME); + YYERROR; + } + if( ENV_NAME_e == *special_type ) { + error_msg(@NAME, "cannot ACCEPT FROM %qs", $NAME); + YYERROR; + } + // Add the name now, as a convenience. + int token = 0; + switch(*special_type) { + case ARG_NUM_e: token = ARGUMENT_NUMBER; break; + case ARG_VALUE_e: token = ARGUMENT_VALUE; break; + case ENV_VALUE_e: token = ENVIRONMENT_VALUE; break; + + case ENV_NAME_e: + default: + error_msg(@NAME, "cannot ACCEPT FROM %qs", $NAME); + YYERROR; + break; + } + cbl_special_name_t special = { token, *special_type }; + namcpy(@NAME, special.name, $NAME); + + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); + cbl_special_name_t& unused(*$$); + assert(unused.id); + } + assert($$); + } + | ENVIRONMENT_VALUE { + // Add the name now, as a convenience. + cbl_special_name_t special = + { ENVIRONMENT_VALUE, ENV_VALUE_e, "ENVIRONMENT-VALUE" }; + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); + } + | ARGUMENT_NUMBER { + // Add the name now, as a convenience. + cbl_special_name_t special = + { ARGUMENT_NUMBER, ARG_NUM_e, "ARGUMENT-NUMBER" }; + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); + } + | ARGUMENT_VALUE { + // Add the name now, as a convenience. + cbl_special_name_t special = + { ARGUMENT_VALUE, ARG_VALUE_e, "ARGUMENT-VALUE" }; + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); } ; @@ -5133,9 +5680,10 @@ add_body: sum TO rnames corresponding_arith_fields( $sum->refers.front().field, rhs.front().refer.field ); if( pairs.empty() ) { - yywarn( "%s and %s have no corresponding fields", - $sum->refers.front().field->name, - rhs.front().refer.field->name ); + cbl_message( @$, ParNoCorrespondingW, + "%s and %s have no corresponding fields", + $sum->refers.front().field->name, + rhs.front().refer.field->name ); } // First src/tgt elements are templates. // Their subscripts apply to the correspondents. @@ -5197,16 +5745,13 @@ name88: NAME88 { scalar88: name88 subscripts[subs] refmod[ref] { - size_t n = $subs->size(); - auto subscripts = new cbl_refer_t[n]; - $subs->use_list(subscripts); if( $ref.from->is_reference() || $ref.len->is_reference() ) { error_msg(@subs, "subscripts on start:len refmod " "parameters are unsupported"); YYERROR; } cbl_span_t span( $ref.from, $ref.len ); - $$ = new cbl_refer_t($1, n, subscripts, span); + $$ = new cbl_refer_t($1, $subs->vectorize(), span); } | name88 refmod[ref] { @@ -5235,7 +5780,8 @@ allocate: ALLOCATE expr[size] CHARACTERS initialized RETURNING scalar[retu { statement_begin(@1, ALLOCATE); if( $size->field->type == FldLiteralN ) { - if( $size->field->data.value_of() <= 0 ) { + const auto size = TREE_REAL_CST_PTR ($size->field->data.value_of()); + if( real_isneg(size) || real_iszero(size) ) { error_msg(@size, "size must be greater than 0"); YYERROR; } @@ -5275,7 +5821,7 @@ compute_impl: COMPUTE compute_body[body] { parser_assign( $body.ntgt, $body.tgts, *$body.expr, NULL, NULL, current.compute_label() ); - current.declaratives_evaluate(ec_none_e); + current.declaratives_evaluate(); } ; compute_cond: COMPUTE compute_body[body] arith_errs[err] @@ -5283,7 +5829,7 @@ compute_cond: COMPUTE compute_body[body] arith_errs[err] parser_assign( $body.ntgt, $body.tgts, *$body.expr, $err.on_error, $err.not_error, current.compute_label() ); - current.declaratives_evaluate(ec_size_e); + current.declaratives_evaluate(); } ; end_compute: %empty %prec COMPUTE @@ -5297,69 +5843,36 @@ compute_body: rnames { statement_begin(@$, COMPUTE); } compute_expr[expr] { $$.expr = $expr; } ; -compute_expr: '=' { +compute_expr: EQ { + if( $1[0] == 'E' ) { // lexer found EQUALS keyword + dialect_ok(@1, IbmEqualAssignE, + "EQUAL as assignment operator" ); + } current.compute_begin(); } expr { $$ = $expr; } ; - | EQUAL { - if( ! dialect_ibm() ) { - dialect_error(@1, "EQUAL invalid as assignment operator", "ibm"); - } - current.compute_begin(); - } expr { - $$ = $expr; - } - ; -display: disp_body end_display - { - std::vector <cbl_refer_t> args($1.vargs->args.size()); - std::copy( $1.vargs->args.begin(), $1.vargs->args.end(), args.begin() ); - if( $1.special && $1.special->id == ARG_NUM_e ) { - if( $1.vargs->args.size() != 1 ) { - error_msg(@1, "ARGUMENT-NUMBER can be set to only one value"); - } - cbl_refer_t& src( $1.vargs->args.front() ); - cbl_field_t *dst = register_find("_ARGI"); - parser_move( dst, src ); - } else { - parser_display($1.special, - args.empty()? NULL : args.data(), args.size(), - DISPLAY_ADVANCE); - } - current.declaratives_evaluate(ec_none_e); - } - | disp_body NO ADVANCING end_display +display: disp_body end_display[advance] { - std::vector <cbl_refer_t> args($1.vargs->args.size()); - std::copy( $1.vargs->args.begin(), $1.vargs->args.end(), args.begin() ); - - if( $1.special && $1.special->id == ARG_NUM_e ) { - if( $1.vargs->args.size() != 1 ) { - error_msg(@1, "ARGUMENT-NUMBER can be set to only one value"); - } - cbl_refer_t& src( $1.vargs->args.front() ); - cbl_field_t *dst = register_find("_ARGI"); - parser_move( dst, src ); - } else { - parser_display($1.special, - args.empty()? NULL : args.data(), args.size(), - DISPLAY_NO_ADVANCE); - } - current.declaratives_evaluate(ec_none_e); + std::vector <cbl_refer_t> args($1.vargs->args.begin(), + $1.vargs->args.end()); + parser_display($1.special, args, $advance); + current.declaratives_evaluate(); } ; -end_display: %empty - | END_DISPLAY +end_display: %empty { $$ = DISPLAY_ADVANCE; } + | END_DISPLAY { $$ = DISPLAY_ADVANCE; } + | NO ADVANCING { $$ = DISPLAY_NO_ADVANCE; } + | NO ADVANCING END_DISPLAY { $$ = DISPLAY_NO_ADVANCE; } ; disp_body: disp_vargs[vargs] { $$.special = NULL; $$.vargs = $vargs; } - | disp_vargs[vargs] UPON disp_target[special] + | disp_vargs[vargs] UPON disp_upon[special] { $$.special = $special; $$.vargs = $vargs; @@ -5371,20 +5884,76 @@ disp_vargs: DISPLAY vargs { } ; -disp_target: device_name { +disp_upon: device_name { $$ = symbol_special($1.id); } | NAME { - symbol_elem_t *e = symbol_special(PROGRAM, $1); + symbol_elem_t *e = symbol_special(PROGRAM, $NAME); if( !e ) { - error_msg(@NAME, "no such special name '%s'", $NAME); - YYERROR; - } - $$ = cbl_special_name_of(e); + const special_name_t *special_type = cmd_or_env_special_of($NAME); + if( !special_type ) { + error_msg(@NAME, "no such special name '%s'", $NAME); + YYERROR; + } + // Add the name now, as a convenience. + // These may come through as a NAME, depending on how scanned. + int token = 0; + switch(*special_type) { + case ARG_NUM_e: token = ARGUMENT_NUMBER; break; + case ENV_NAME_e: token = ENVIRONMENT_NAME; break; + case ENV_VALUE_e: token = ENVIRONMENT_VALUE; break; + + case ARG_VALUE_e: + default: + error_msg(@NAME, "cannot DISPLAY UPON %qs", $NAME); + YYERROR; + break; + } + cbl_special_name_t special = { token, *special_type }; + namcpy(@NAME, special.name, $NAME); + + e = symbol_special_add(PROGRAM, &special); + } + $$ = cbl_special_name_of(e); + } + | ARGUMENT_NUMBER { + // Add the name now, as a convenience. + cbl_special_name_t special = + { ARGUMENT_NUMBER, ARG_NUM_e, "ARGUMENT-NUMBER" }; + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); + } + | ENVIRONMENT_NAME { + // Add the name now, as a convenience. + cbl_special_name_t special = + { ENVIRONMENT_NAME, ENV_NAME_e, "ENVIRONMENT-NAME" }; + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); + } + | ENVIRONMENT_VALUE { + // Add the name now, as a convenience. + cbl_special_name_t special = + { ENVIRONMENT_VALUE, ENV_VALUE_e, "ENVIRONMENT-VALUE" }; + symbol_elem_t *e = symbol_special_add(PROGRAM, &special); + $$ = cbl_special_name_of(e); } ; +exhibit_stmt: EXHIBIT exh_changed exh_named vargs { + statement_begin(@1, EXHIBIT); + std::vector<cbl_refer_t> args( $vargs->args.begin(), + $vargs->args.end() ); + parser_exhibit( $exh_changed, $exh_named, args ); + } + ; +exh_changed: %empty { $$ = false; } + | CHANGED { $$ = true; } + ; +exh_named: %empty { $$ = false; } + | NAMED { $$ = true; } + ; + divide: divide_impl end_divide { ast_divide($1); } | divide_cond end_divide { ast_divide($1); } ; @@ -5480,22 +6049,22 @@ end_program: end_program1[end] '.' gcc_unreachable(); } if( !matches ) { - error_msg(@end, "END %s %s' does not match IDENTIFICATION DIVISION '%s'", + error_msg($end.loc, "END %s %s does not match " + "%<IDENTIFICATION DIVISION %s%>", token_name, name, prog->name); YYERROR; } if( 0 != strcasecmp(prog->name, name) ) { - error_msg(@end, "END PROGRAM '%s' does not match PROGRAM-ID '%s'", + error_msg($end.loc, "END PROGRAM '%s' does not match PROGRAM-ID '%s'", name, prog->name); YYERROR; } std::set<std::string> externals = current.end_program(); if( !externals.empty() ) { - for( auto name : externals ) { - yywarn("%s calls external symbol '%s'", prog->name, name.c_str()); + for( const auto& name : externals ) { + dbgmsg("%s calls external symbol '%s'", prog->name, name.c_str()); } - YYERROR; } // pointer still valid because name is in symbol table ast_end_program(prog->name); @@ -5511,28 +6080,32 @@ end_program: end_program1[end] '.' token_name = "FUNCTION"; break; default: - cbl_internal_error( "END token invalid"); + cbl_internal_error( "%<END%> token invalid"); } - error_msg(@end, "END %s requires NAME before '.'", token_name); + error_msg(@end, "%<END%> %s requires %<NAME%> before %<.%>", token_name); YYERROR; } ; end_program1: END_PROGRAM namestr[name] { + $$.loc = @name; $$.token = END_PROGRAM; $$.name = $name; } | END_FUNCTION namestr[name] { + $$.loc = @name; $$.token = END_FUNCTION; $$.name = $name; } | END_PROGRAM '.' // error { + $$.loc = @1; $$.token = END_PROGRAM; } | END_FUNCTION '.' // error { + $$.loc = @1; $$.token = END_FUNCTION; } ; @@ -5587,7 +6160,7 @@ exit_with: %empty static cbl_refer_t status(rt); $$ = &status; } - auto prog = cbl_label_of(symbol_at(current_program_index())); + const auto prog = cbl_label_of(symbol_at(current_program_index())); if( prog->returning ) { $$ = new cbl_refer_t( cbl_field_of(symbol_at(prog->returning)) ); } @@ -5602,9 +6175,7 @@ exit_with: %empty } | RETURNING stop_status { - if( ! dialect_mf() ) { - dialect_error(@2, "RETURNING <number>", "mf"); - } + dialect_ok(@$, MfReturningNum, "RETURNING <number>"); $$ = $stop_status? $stop_status : new_reference(literally_one); } ; @@ -6171,17 +6742,17 @@ eval_abbrs: rel_term[a] { auto& ev( eval_stack.current() ); auto subj( ev.subject() ); if( !subj ) { - error_msg(@1, "WHEN %s phrase exceeds " + error_msg(@1, "WHEN %qs phrase exceeds " "subject set count of %zu", - $a.term->name(), ev.subject_count()); + nice_name_of($a.term->field), ev.subject_count()); YYERROR; } if( ! ev.compatible($a.term->field) ) { auto obj($a.term->field); error_msg(@1, "subject %s, type %s, " - "cannot be compared %s, type %s", - subj->name, 3 + cbl_field_type_str(subj->type), - obj->name, 3 + cbl_field_type_str(obj->type) ); + "cannot be compared %s, type %s", + subj->name, 3 + cbl_field_type_str(subj->type), + obj->name, 3 + cbl_field_type_str(obj->type) ); } auto result = ev.compare(*$a.term); if( ! result ) YYERROR; @@ -6276,7 +6847,7 @@ true_false: TRUE_kw { $$ = TRUE_kw; } scalar: tableref { // Check for missing subscript; others already checked. - if( $1->nsubscript == 0 && 0 < dimensions($1->field) ) { + if( $1->nsubscript() == 0 && 0 < dimensions($1->field) ) { subscript_dimension_error(@1, 0, $$); } } @@ -6287,8 +6858,8 @@ tableref: tableish { $$ = $1; $$->loc = @1; if( $$->is_table_reference() ) { - if( $$->nsubscript != dimensions($$->field) ) { - subscript_dimension_error(@1, $$->nsubscript, $$); + if( $$->nsubscript() != dimensions($$->field) ) { + subscript_dimension_error(@1, $$->nsubscript(), $$); YYERROR; } } @@ -6324,14 +6895,14 @@ tableish: name subscripts[subs] refmod[ref] %prec NAME refmod: LPAREN expr[from] ':' expr[len] ')' %prec NAME { - if( ! require_numeric(@from, *$from) ) YYERROR; - if( ! require_numeric(@len, *$len) ) YYERROR; + if( ! require_integer(@from, *$from) ) YYERROR; + if( ! require_integer(@len, *$len) ) YYERROR; $$.from = $from; $$.len = $len; } | LPAREN expr[from] ':' ')' %prec NAME { - if( ! require_numeric(@from, *$from) ) YYERROR; + if( ! require_integer(@from, *$from) ) YYERROR; $$.from = $from; $$.len = nullptr; } @@ -6351,13 +6922,13 @@ typename: NAME name: qname { build_symbol_map(); - auto namelocs( name_queue.pop() ); - auto names( name_queue.namelist_of(namelocs) ); - auto inner = namelocs.back(); + auto namelocs( name_queue.pop() ); + auto names( name_queue.namelist_of(namelocs) ); + auto inner = namelocs.back(); if( ($$ = field_find(names)) == NULL ) { if( procedure_div_e == current_division ) { - error_msg(inner.loc, - "DATA-ITEM '%s' not found", inner.name ); + error_msg(inner.loc, + "DATA-ITEM '%s' not found", inner.name ); YYERROR; } /* @@ -6368,9 +6939,9 @@ name: qname auto name = names.front(); names.pop_front(); auto e = symbol_field_forward_add(PROGRAM, parent, - name, yylineno); + name, @1.first_line); if( !e ) YYERROR; - symbol_field_location( symbol_index(e), @qname ); + symbol_field_location( symbol_index(e), @qname ); parent = symbol_index(e); $$ = cbl_field_of(e); } @@ -6398,10 +6969,16 @@ ctx_name: NAME context_word: APPLY { static char s[] ="APPLY"; $$ = s; } // screen description entry + | ARGUMENT_NUMBER { static char s[] ="ARGUMENT-NUMBER"; + $$ = s; } // Display Upon / Accept From + | ARGUMENT_VALUE { static char s[] ="ARGUMENT-VALUE"; + $$ = s; } // Accept From | ARITHMETIC { static char s[] ="ARITHMETIC"; $$ = s; } // OPTIONS paragraph | ATTRIBUTE { static char s[] ="ATTRIBUTE"; $$ = s; } // SET statement + | ATTRIBUTES { static char s[] ="ATTRIBUTES"; + $$ = s; } // XML GENERATE | AUTO { static char s[] ="AUTO"; $$ = s; } // screen description entry | AUTOMATIC { static char s[] ="AUTOMATIC"; @@ -6434,6 +7011,10 @@ context_word: APPLY { static char s[] ="APPLY"; $$ = s; } // ERASE clause in a screen description entry | ENTRY_CONVENTION { static char s[] ="ENTRY-CONVENTION"; $$ = s; } // OPTIONS paragraph + | ENVIRONMENT_NAME { static char s[] ="ENVIRONMENT-NAME"; + $$ = s; } // Display Upon + | ENVIRONMENT_VALUE { static char s[] ="ENVIRONMENT-VALUE"; + $$ = s; } // Display Upon / Accept From | ERASE { static char s[] ="ERASE"; $$ = s; } // screen description entry | EXPANDS { static char s[] ="EXPANDS"; @@ -6486,6 +7067,8 @@ context_word: APPLY { static char s[] ="APPLY"; $$ = s; } // LOCK MODE clause | MULTIPLE { static char s[] ="MULTIPLE"; $$ = s; } // LOCK ON phrase + | NAT { static char s[] ="NAT"; + $$ = s; } // CONVERT function | NEAREST_AWAY_FROM_ZERO { static char s[] ="NEAREST-AWAY-FROM-ZERO"; $$ = s; } // INTERMEDIATE ROUNDING clause and ROUNDED phrase | NEAREST_EVEN { static char s[] ="NEAREST-EVEN"; @@ -6522,7 +7105,7 @@ context_word: APPLY { static char s[] ="APPLY"; $$ = s; } // screen description entry | SHORT { static char s[] ="SHORT"; $$ = s; } // DYNAMIC LENGTH STRUCTURE clause - | SIGNED { static char s[] ="SIGNED"; + | SIGNED_kw { static char s[] ="SIGNED"; $$ = s; } // DYNAMIC LENGTH STRUCTURE clause and USAGE clause | STANDARD_BINARY { static char s[] ="STANDARD-BINARY"; $$ = s; } // ARITHMETIC clause @@ -6548,7 +7131,7 @@ context_word: APPLY { static char s[] ="APPLY"; $$ = s; } // ALPHABET clause | UNDERLINE { static char s[] ="UNDERLINE"; $$ = s; } // screen description entry and SET attribute statement - | UNSIGNED { static char s[] ="UNSIGNED"; + | UNSIGNED_kw { static char s[] ="UNSIGNED"; $$ = s; } // USAGE clause | UTF_8 { static char s[] ="UTF-8"; $$ = s; } // ALPHABET clause @@ -6564,7 +7147,7 @@ move: MOVE scalar TO move_tgts[tgts] { statement_begin(@1, MOVE); if( $scalar->field->type == FldIndex ) { - error_msg(@1, "'%s' cannot be MOVEd because it's an INDEX", + error_msg(@1, "%qs cannot be MOVEd because it is an %<INDEX%>", name_of($scalar->field) ); YYERROR; } @@ -6627,8 +7210,9 @@ move: MOVE scalar TO move_tgts[tgts] } if( !move_corresponding(*$to, *$from) ) { - yywarn( "%s and %s have no corresponding fields", - $from->field->name, $to->field->name ); + cbl_message( @$, ParNoCorrespondingW, + "%s and %s have no corresponding fields", + $from->field->name, $to->field->name ); } } ; @@ -6652,10 +7236,18 @@ move_tgt: scalar[tgt] { const auto& field(*$1); static char buf[32]; const char *value_str( name_of($literal) ); - if( is_numeric($1) && - float(field.data.value_of()) == int(field.data.value_of()) ) { - sprintf(buf, "%d", int(field.data.value_of())); - value_str = buf; + if( is_numeric($1) ) + { + REAL_VALUE_TYPE val = TREE_REAL_CST (field.data.value_of()); + int ival = (int)real_to_integer (&val); + val = real_value_truncate (TYPE_MODE (float_type_node), + val); + REAL_VALUE_TYPE rival; + real_from_integer (&rival, VOIDmode, ival, SIGNED); + if( real_identical (&val, &rival) ) { + sprintf(buf, "%d", ival); + value_str = buf; + } } auto litcon = field.name[0] == '_'? "literal" : "constant"; error_msg(@literal, "%s is a %s", value_str, litcon); @@ -6774,9 +7366,9 @@ arith_err: SIZE_ERROR *ptgt = $1 == NOT? current.compute_not_error() : current.compute_on_error(); } else { - *ptgt = label_add(LblArith, uniq_label("arith"), yylineno); + *ptgt = label_add(LblArith, uniq_label("arith"), @1.first_line); } - (*ptgt)->lain = yylineno; + (*ptgt)->lain = @1.first_line; parser_arith_error( *ptgt ); } ; @@ -6797,7 +7389,7 @@ arith_err: SIZE_ERROR relop: '<' { $$ = '<'; } | LE { $$ = LE; } - | '=' { $$ = '='; } + | EQ { $$ = EQ; } | NE { $$ = NE; } | GE { $$ = GE; } | '>' { $$ = '>'; } @@ -6829,22 +7421,22 @@ num_value: scalar // might actually be a string | num_literal { $$ = new_reference($1); } | ADDRESS OF scalar {$$ = $scalar; $$->addr_of = true; } | DETAIL OF scalar {$$ = $scalar; } + | LENGTH_OF binary_type[size] { + location_set(@1); + $$ = new cbl_refer_t( new_tempnumeric(none_e) ); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); + parser_set_numeric($$->field, $size); + } | LENGTH_OF name[val] { location_set(@1); - $$ = new cbl_refer_t( new_tempnumeric() ); - $$->field->clear_attr(signable_e); - if( dialect_gcc() ) { - dialect_error(@1, "LENGTH OF", "ibm"); - } + $$ = new cbl_refer_t( new_tempnumeric(none_e) ); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); parser_set_numeric($$->field, $val->data.capacity); } | LENGTH_OF name[val] subscripts[subs] { location_set(@1); - $$ = new cbl_refer_t( new_tempnumeric() ); - $$->field->clear_attr(signable_e); - if( dialect_gcc() ) { - dialect_error(@1, "LENGTH OF", "ibm"); - } + $$ = new cbl_refer_t( new_tempnumeric(none_e) ); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); if( 0 == dimensions($val) ) { cbl_refer_t r1($val); subscript_dimension_error( @subs, $subs->refers.size(), &r1 ); @@ -6872,34 +7464,42 @@ num_value: scalar // might actually be a string /* cce_relexpr: cce_expr */ /* | cce_relexpr '<' cce_expr { $$ = $1 < $3; } */ /* | cce_relexpr LE cce_expr { $$ = $1 <= $3; } */ -/* | cce_relexpr '=' cce_expr { $$ = $1 == $3; } */ +/* | cce_relexpr EQ cce_expr { $$ = $1 == $3; } */ /* | cce_relexpr NE cce_expr { $$ = $1 != $3; } */ /* | cce_relexpr GE cce_expr { $$ = $1 >= $3; } */ /* | cce_relexpr '>' cce_expr { $$ = $1 > $3; } */ /* ; */ cce_expr: cce_factor - | cce_expr '+' cce_expr { $$ = $1 + $3; } - | cce_expr '-' cce_expr { $$ = $1 - $3; } - | cce_expr '*' cce_expr { $$ = $1 * $3; } - | cce_expr '/' cce_expr { $$ = $1 / $3; } + | cce_expr '+' cce_expr { + real_arithmetic (&$$, PLUS_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } + | cce_expr '-' cce_expr { + real_arithmetic (&$$, MINUS_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } + | cce_expr '*' cce_expr { + real_arithmetic (&$$, MULT_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } + | cce_expr '/' cce_expr { + real_arithmetic (&$$, RDIV_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } | '+' cce_expr %prec NEG { $$ = $2; } - | '-' cce_expr %prec NEG { $$ = -$2; } + | '-' cce_expr %prec NEG { $$ = real_value_negate (&$2); } | '(' cce_expr ')' { $$ = $2; } ; cce_factor: NUMSTR { - /* - * As of March 2023, glibc printf does not deal with - * __int128_t. The below assertion is not required. It - * serves only remind us we're far short of the precision - * required by ISO. - */ - static_assert( sizeof($$) == sizeof(_Float128), - "quadmath?" ); - static_assert( sizeof($$) == 16, - "long doubles?" ); - $$ = numstr2i($1.string, $1.radix); + /* real_from_string does not allow arbitrary radix. */ + // When DECIMAL IS COMMA, commas act as decimal points. + gcc_assert($1.radix == decimal_e); + auto p = $1.string, pend = p + strlen(p); + std::replace(p, pend, ',', '.'); + real_from_string3( &$$, $1.string, + TYPE_MODE (float128_type_node) ); } ; @@ -6929,11 +7529,19 @@ section_name: NAME section_kw '.' section_kw: SECTION { - if( $1 ) { + if( $1 && dialect_ok(@1, IbmSectionSegmentW, "SECTION segment") ) { + cbl_message(@1, IbmSectionSegmentW, + "SECTION segment %qs was ignored", $1); if( *$1 == '-' ) { - error_msg(@1, "SECTION segment %s is negative", $1); + cbl_message(@1, IbmSectionNegE, + "SECTION segment %qs is negative", $1); } else { - cbl_unimplementedw("SECTION segment %s was ignored", $1); + int sectno; + sscanf($1, "%d", §no); + if( ! (0 <= sectno && sectno <= 99) ) { + cbl_message(@1, IbmSectionRangeE, + "SECTION segment %qs must be 0-99", $1); + } } } } @@ -6951,10 +7559,7 @@ stop: STOP RUN exit_with | STOP NUMSTR[status] // IBM syntax { statement_begin(@1, STOP); - if( ! dialect_ibm() ) { - dialect_error(@2, "STOP <number> is not ISO syntax,", "ibm"); - YYERROR; - } + dialect_ok(@2, IbmStopNumber, "STOP <number>"); cbl_refer_t status( new_literal($status.string, $status.radix) ); parser_see_stop_run( status, NULL ); } @@ -6976,7 +7581,7 @@ stop_status: status { $$ = NULL; } } ; -subscripts: LPAREN expr_list ')' { +subscripts: LPAREN subscript_exprs ')' { $$ = $2; const auto& exprs( $$->refers ); bool ok = std::all_of( exprs.begin(), exprs.end(), @@ -6996,18 +7601,18 @@ subscripts: LPAREN expr_list ')' { } } ; -expr_list: expr +subscript_exprs: expr { - if( ! require_numeric(@expr, *$expr) ) YYERROR; + if( ! require_integer(@expr, *$expr) ) YYERROR; $$ = new refer_list_t($expr); } - | expr_list expr { + | subscript_exprs expr { if( $1->size() == MAXIMUM_TABLE_DIMENSIONS ) { error_msg(@1, "table dimensions limited to %d", MAXIMUM_TABLE_DIMENSIONS); YYERROR; } - if( ! require_numeric(@expr, *$expr) ) YYERROR; + if( ! require_integer(@expr, *$expr) ) YYERROR; $1->push_back($2); $$ = $1; } | ALL { @@ -7035,22 +7640,22 @@ signed_literal: num_literal struct cbl_field_t *zero = constant_of(constant_index(ZERO)); parser_subtract( $$, zero, $2, current_rounded_mode() ); } + | LENGTH_OF binary_type[size] { + location_set(@1); + $$ = new_tempnumeric(none_e); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); + parser_set_numeric($$, $size); + } | LENGTH_OF name[val] { location_set(@1); - $$ = new_tempnumeric(); - $$->clear_attr(signable_e); - if( dialect_gcc() ) { - dialect_error(@1, "LENGTH OF", "ibm"); - } + $$ = new_tempnumeric(none_e); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); parser_set_numeric($$, $val->data.capacity); } | LENGTH_OF name[val] subscripts[subs] { location_set(@1); - $$ = new_tempnumeric(); - $$->clear_attr(signable_e); - if( dialect_gcc() ) { - dialect_error(@1, "LENGTH OF", "ibm"); - } + $$ = new_tempnumeric(none_e); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); if( 0 == dimensions($val) ) { cbl_refer_t r1($val); subscript_dimension_error( @subs, $subs->refers.size(), &r1 ); @@ -7275,6 +7880,7 @@ perform_cond: UNTIL { parser_perform_conditional( &perform_current()->tgt); } perform_inline: perform_start statements END_PERFORM { location_set(@END_PERFORM); + parser_sleep(*cbl_refer_t::empty()); $$ = perform_current(); if( $perform_start == LOCATION ) { error_msg(@1, "LOCATION not valid with PERFORM Format 2"); @@ -7283,12 +7889,14 @@ perform_inline: perform_start statements END_PERFORM | perform_start END_PERFORM { location_set(@END_PERFORM); + parser_sleep(*cbl_refer_t::empty()); $$ = perform_current(); if( $perform_start == LOCATION ) { error_msg(@1, "LOCATION not valid with PERFORM Format 2"); } } ; + perform_start: %empty %prec LOCATION { perform_ec_setup(); $$ = 0; @@ -7315,18 +7923,7 @@ perform_except: perform_start perform_ec_finally END_PERFORM { - auto perf = perform_current(); - // produce blob, jumped over by FINALLY paragraph - size_t iblob = symbol_declaratives_add( PROGRAM, perf->dcls ); - auto lave = perf->ec_labels.new_label(LblParagraph, "lave"); - auto handlers = cbl_field_of(symbol_at(iblob)); - - // install blob - parser_label_label(perf->ec_labels.init); - declarative_runtime_match(handlers, lave); - - // uninstall blob - parser_label_label(perf->ec_labels.fini); + cbl_unimplemented("PERFORM Format 3"); } ; @@ -7344,7 +7941,7 @@ perform_when1: WHEN perform_ec { std::transform( $perform_ec->elems.begin(), $perform_ec->elems.end(), std::back_inserter(perf->dcls), - []( cbl_declarative_t *p ) { + []( const cbl_declarative_t *p ) { return *p; } ); ast_enter_paragraph(when); @@ -7434,12 +8031,12 @@ except_files: except_name[ec] FILE_KW filenames { perform_ec_other: %empty %prec WHEN { - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); ast_enter_paragraph(ec_labels.other); parser_exit_paragraph(); } | WHEN OTHER { - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); ast_enter_paragraph(ec_labels.other); } exception statements %prec WHEN { @@ -7448,12 +8045,12 @@ perform_ec_other: ; perform_ec_common: %empty { - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); ast_enter_paragraph(ec_labels.common); parser_exit_paragraph(); } | WHEN COMMON { - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); ast_enter_paragraph(ec_labels.common); } exception statements { @@ -7462,18 +8059,18 @@ perform_ec_common: ; perform_ec_finally: %empty { - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); ast_enter_paragraph(ec_labels.finally); parser_exit_paragraph(); parser_label_goto(ec_labels.fini); } | FINALLY { - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); ast_enter_paragraph(ec_labels.finally); } exception statements { parser_exit_paragraph(); - auto& ec_labels( perform_current()->ec_labels ); + const auto& ec_labels( perform_current()->ec_labels ); parser_label_goto(ec_labels.fini); } ; @@ -7564,9 +8161,10 @@ subtract_body: sum FROM rnames corresponding_arith_fields( $sum->refers.front().field, rhs.front().refer.field ); if( pairs.empty() ) { - yywarn( "%s and %s have no corresponding fields", - $sum->refers.front().field->name, - rhs.front().refer.field->name ); + cbl_message(ParNoCorrespondingW, + "%s and %s have no corresponding fields", + $sum->refers.front().field->name, + rhs.front().refer.field->name ); } // First src/tgt elements are templates. // Their subscripts apply to the correspondents. @@ -7604,22 +8202,22 @@ varg1a: ADDRESS OF scalar { { $$ = new_reference(constant_of(constant_index($1))); } + | LENGTH_OF binary_type[size] { + location_set(@1); + $$ = new cbl_refer_t( new_tempnumeric(none_e) ); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); + parser_set_numeric($$->field, $size); + } | LENGTH_OF name[val] { location_set(@1); - $$ = new cbl_refer_t( new_tempnumeric() ); - $$->field->clear_attr(signable_e); - if( dialect_gcc() ) { - dialect_error(@1, "LENGTH OF", "ibm"); - } + $$ = new cbl_refer_t( new_tempnumeric(none_e) ); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); parser_set_numeric($$->field, $val->size()); } | LENGTH_OF name[val] subscripts[subs] { location_set(@1); - $$ = new cbl_refer_t( new_tempnumeric() ); - $$->field->clear_attr(signable_e); - if( dialect_gcc() ) { - dialect_error(@1, "LENGTH OF", "ibm"); - } + $$ = new cbl_refer_t( new_tempnumeric(none_e) ); + dialect_ok(@1, IbmLengthOf, "LENGTH OF"); if( 0 == dimensions($val) ) { cbl_refer_t r1($val); subscript_dimension_error( @subs, $subs->refers.size(), &r1 ); @@ -7628,6 +8226,10 @@ varg1a: ADDRESS OF scalar { } ; +binary_type: BINARY_INTEGER { $$ = $1.capacity; } + | COMPUTATIONAL { $$ = $1.capacity; } + ; + literal: literalism { $$ = $1.isymbol()? @@ -7671,14 +8273,14 @@ raise: RAISE EXCEPTION NAME "EXCEPTION CONDITION: %s", $NAME); YYERROR; } - cbl_unimplemented("RAISE <EXCEPTION OBJECT>"); + cbl_unimplemented("RAISE %<EXCEPTION OBJECT%>"); YYERROR; } ; read: read_file { - current.declaratives_evaluate($1.file, $1.handled); + current.declaratives_evaluate($1.file); } ; @@ -7738,10 +8340,6 @@ read_body: NAME read_next read_into read_key error_msg(@1, "syntax error? invalid file record name"); YYERROR; } - if( 0 && $$->access == file_access_dyn_e && $read_next >= 0 ) { - error_msg(@1, "sequential DYNAMIC access requires NEXT RECORD"); - YYERROR; - } if( $read_key->field && is_sequential($$) ) { error_msg(@1, "SEQUENTIAL file %s has no KEY", $$->name); YYERROR; @@ -7752,7 +8350,7 @@ read_body: NAME read_next read_into read_key YYERROR; } if( $read_key->field && $read_next < 0 ) { - error_msg(@1, "cannot read NEXT with KEY", $$->name); + error_msg(@1, "cannot read NEXT with KEY %qs", $$->name); YYERROR; } @@ -7865,7 +8463,7 @@ read_key: %empty { $$ = new cbl_refer_t(); } write: write_file { - current.declaratives_evaluate( $1.file, $1.handled ); + current.declaratives_evaluate($1.file ); } ; @@ -7985,7 +8583,7 @@ advance_by: scalar lines { $$ = $1; } /* BUG: should accept reference */ * number of lines is negative. So, we use the * negative Number Of The Beast as a PAGE flag. */ - $$ = new_reference( new_literal("-666") ); + $$ = new_reference( new_literal(xstrdup("-666")) ); } | device_name { $$ = new_reference(literally_one); } ; @@ -8042,7 +8640,33 @@ io_invalid: INVALID key { delete: delete_impl end_delete | delete_cond end_delete + | delete_file end_delete + ; +delete_file: DELETE delete_file_body[stmt] delete_error[err] { + if( ! $err.on_error ) parser_file_delete_on_exception($stmt); + if( ! $err.not_error ) parser_file_delete_not_exception($stmt); + parser_file_delete_end($stmt); + current.declaratives_evaluate(); + } +delete_file_body: + FILE_KW override filenames retry_phrase { + $$ = label_add(@$, LblXml, uniq_label("xfile")); + xml_statements.push($$); + statement_begin(@$, DELETE); + std::vector<cbl_file_t*> + filenames($filenames->files.begin(), + $filenames->files.end() ); + parser_file_delete_file( $$, filenames); + } ; +retry_phrase: %empty + | RETRY expr TIMES + | FOR expr SECONDS + | FOREVER { + cbl_unimplemented("DELETE FILE RETRY"); + } + ; + delete_impl: DELETE delete_body[file] { file_delete_args.call_parser_file_delete(true); @@ -8075,13 +8699,70 @@ delete_body: filename[file] record $$ = $file; } ; + +delete_error: %empty %prec DELETE { + $$.on_error = $$.not_error = nullptr; + } + | delete_excepts %prec DELETE + ; +delete_excepts: delete_except[a] statements %prec DELETE + { + assert( $a.on_error || $a.not_error ); + assert( ! ($a.on_error && $a.not_error) ); + $$ = $a; + } + | delete_excepts[a] delete_except[b] statements %prec DELETE + { + if( $a.on_error && $a.not_error ) { + error_msg(@1, "too many ON ERROR clauses"); + YYERROR; + } + // "ON" and "NOT ON" could be reversed, but not duplicated. + if( $a.on_error && $b.on_error ) { + error_msg(@1, "duplicate ON ERROR clauses"); + YYERROR; + } + if( $a.not_error && $b.not_error ) { + error_msg(@1, "duplicate NOT ON ERROR clauses"); + YYERROR; + } + $$ = $a; + if( $$.on_error ) { + assert($b.not_error); + $$.not_error = $b.not_error; + } else { + assert($b.on_error); + $$.on_error = $b.on_error; + } + } + ; +delete_except: EXCEPTION + { + auto xml_stmt = xml_statements.top(); + // The value of the pointer no longer matters, only NULL or not. + $$.on_error = $$.not_error = nullptr; + switch($1) { + case EXCEPTION: + $$.on_error = xml_stmt; + parser_file_delete_on_exception(xml_stmt); + break; + case NOT: + $$.not_error = xml_stmt; + parser_file_delete_not_exception(xml_stmt); + break; + default: + gcc_unreachable(); + } + } + ; + end_delete: %empty %prec DELETE | END_DELETE ; rewrite: rewrite1 { - current.declaratives_evaluate($1.file, $1.handled); + current.declaratives_evaluate($1.file); } ; @@ -8122,12 +8803,21 @@ end_rewrite: %empty %prec REWRITE ; start: start_impl end_start + { + current.declaratives_evaluate($1); + } | start_cond end_start + { + current.declaratives_evaluate($1); + } ; -start_impl: START start_body +start_impl: START start_body { + $$ = $2; + } ; start_cond: START start_body io_invalids { parser_fi(); + $$ = $2; } ; end_start: %empty %prec START @@ -8137,7 +8827,7 @@ end_start: %empty %prec START start_body: filename[file] { statement_begin(@$, START); - file_start_args.init(@file, $file); + $$ = file_start_args.init(@file, $file); parser_file_start( $file, lt_op, 0 ); } | filename[file] KEY relop name[key] @@ -8147,30 +8837,27 @@ start_body: filename[file] int size = key == 0 ? 0 : $file->keys[key - 1].size(); auto ksize = new_tempnumeric(); parser_set_numeric(ksize, size); - if( yydebug ) { - yywarn("START: key #%d '%s' has size %d", - key, $key->name, size); - } - file_start_args.init(@file, $file); + dbgmsg("START: key #%d '%s' has size %d", key, $key->name, size); + $$ = file_start_args.init(@file, $file); parser_file_start( $file, relop_of($relop), key, ksize ); } | filename[file] KEY relop name[key] with LENGTH expr { // lexer swallows IS, although relop allows it. statement_begin(@$, START); int key = $file->key_one($key); - file_start_args.init(@file, $file); + $$ = file_start_args.init(@file, $file); parser_file_start( $file, relop_of($relop), key, *$expr ); } | filename[file] FIRST { statement_begin(@$, START); - file_start_args.init(@file, $file); + $$ = file_start_args.init(@file, $file); parser_file_start( $file, lt_op, -1 ); } | filename[file] LAST { statement_begin(@$, START); - file_start_args.init(@file, $file); + $$ = file_start_args.init(@file, $file); parser_file_start( $file, gt_op, -2 ); } ; @@ -8180,8 +8867,8 @@ merge: MERGE { statement_begin(@1, MERGE); } USING filenames[inputs] sort_output { std::vector <cbl_key_t> keys($sort_keys->key_list.size()); - std::copy( $sort_keys->key_list.begin(), - $sort_keys->key_list.end(), keys.begin() ); + std::copy( $sort_keys->key_list.begin(), + $sort_keys->key_list.end(), keys.begin() ); size_t ninput = $inputs->files.size(); size_t noutput = $sort_output->nfile(); @@ -8200,8 +8887,7 @@ merge: MERGE { statement_begin(@1, MERGE); } out_proc = &$sort_output->tgt; } - parser_file_merge( $file, $sort_seq, - keys.size(), keys.empty()? NULL : keys.data(), + parser_file_merge( $file, $sort_seq, keys, ninput, inputs, noutput, outputs, out_proc ); @@ -8324,6 +9010,24 @@ set: SET set_tgts[tgts] TO set_operand[src] new_literal($src, quoted_e); ast_set_pointers($tgts->targets, literal); } + // Format 12 (save-locale): + | SET set_tgts[tgts] TO LOCALE locale_current + { + if( $tgts->targets.size() > 1 ) { + error_msg(@tgts, "only 1 save-locale data-item is valid"); + } + switch($locale_current) { + case LC_ALL_kw: + case DEFAULT: + ast_save_locale($tgts->targets.front().refer, $locale_current); + break; + default: + gcc_unreachable(); + } + cbl_unimplementedw(SynSetToLocale, + "unimplemented: %<SET ... TO LOCALE%>"); + } + ; | SET set_tgts[tgts] UP BY num_operand[src] { statement_begin(@1, SET); @@ -8367,7 +9071,7 @@ set: SET set_tgts[tgts] TO set_operand[src] class set_conditional { bool tf; public: - set_conditional( int token ) : tf(token == TRUE_kw) {} + explicit set_conditional( int token ) : tf(token == TRUE_kw) {} void operator()(cbl_refer_t& refer) { if( refer.field->data.false_value_of() == NULL && !tf ) { auto loc = symbol_field_location(field_index(refer.field)); @@ -8382,6 +9086,43 @@ set: SET set_tgts[tgts] TO set_operand[src] set_conditional($yn)); } | SET { statement_begin(@1, SET); } many_switches + + // Format 11 (set-locale): + | SET LOCALE locale_tgt[tgt] TO locale_src + { + if( $tgt->is_default() ) { + // do something $tgt->default_of() + } else { + // do something $tgt->lc_categories() + } + cbl_unimplementedw(SynSetLocaleTo, + "unimplemented: %<SET LOCALE ... TO%>"); + } + ; + +locale_tgt: user_default { $$ = new locale_tgt_t(); *$$ = $1; } + | loc_categories + ; +loc_categories: loc_category { $$ = new locale_tgt_t($1); } + | loc_categories loc_category { + $$ = $1; + $$->push_back($2); + } + ; +loc_category: LC_ALL_kw { $$ = LC_ALL_kw; } + | LC_COLLATE_kw { $$ = LC_COLLATE_kw; } + | LC_CTYPE_kw { $$ = LC_CTYPE_kw; } + | LC_MESSAGES_kw { $$ = LC_MESSAGES_kw; } + | LC_MONETARY_kw { $$ = LC_MONETARY_kw; } + | LC_NUMERIC_kw { $$ = LC_NUMERIC_kw; } + | LC_TIME_kw { $$ = LC_TIME_kw; } + ; +locale_src: scalar + | DEFAULT { assert($1 == 'U' || $1 == 'S'); } + ; + +locale_current: LC_ALL_kw { $$ = LC_ALL_kw; } // locale to be saved by SET Format 12. + | user_default { $$ = DEFAULT; } ; many_switches: set_switches @@ -8392,7 +9133,7 @@ set_switches: switches TO on_off { struct switcheroo { bitop_t op; - switcheroo( bool tf ) : op( tf? bit_set_op : bit_clear_op ) {} + explicit switcheroo( bool tf ) : op( tf? bit_set_op : bit_clear_op ) {} switcheroo& operator()(cbl_field_t* sw) { assert(sw->type == FldSwitch); assert(sw->data.initial); // not a switch condition @@ -8447,12 +9188,12 @@ search_1_body: name[table] search_varying[varying] cbl_name_t label_name; auto len = snprintf(label_name, sizeof(label_name), - "linear_search_%d", yylineno); + "linear_search_%d", @1.first_line); if( ! (0 < len && len < int(sizeof(label_name))) ) { gcc_unreachable(); } cbl_label_t *name = label_add( LblSearch, - label_name, yylineno ); + label_name, @1.first_line ); auto varying($varying); if( index == varying ) varying = NULL; parser_lsearch_start( name, $table, index, varying ); @@ -8476,7 +9217,7 @@ search_1_cases: search_1_case lookahead = keyword_str(yychar); } } - yywarn("Just one case, lookahead is '%s'", lookahead); + dbgmsg("Just one case, lookahead is '%s'", lookahead); } } | search_1_cases search_1_case @@ -8505,9 +9246,9 @@ search_binary: SEARCH ALL search_2_body search_2_cases search_2_body: name[table] { statement_begin(@$, SEARCH); - char *label_name = xasprintf("binary_search_%d", yylineno); + char *label_name = xasprintf("binary_search_%d", @1.first_line); cbl_label_t *name = label_add( LblSearch, - label_name, yylineno ); + label_name, @1.first_line ); parser_bsearch_start( name, $table ); search_alloc(name); } @@ -8530,16 +9271,16 @@ search_stmts: statements %prec ADD search_terms: search_term | search_terms AND search_term ; -search_term: scalar[key] '=' search_expr[sarg] +search_term: scalar[key] EQ search_expr[sarg] { - if( $key->nsubscript == 0 ) { + if( $key->nsubscript() == 0 ) { error_msg(@1, "no index for key"); YYERROR; } - if( dimensions($key->field) < $key->nsubscript ) { + if( dimensions($key->field) < $key->nsubscript() ) { error_msg(@1, "too many subscripts: " - "%zu for table of %zu dimensions", - $key->nsubscript, dimensions($key->field) ); + "%u for table of %zu dimensions", + $key->nsubscript(), dimensions($key->field) ); YYERROR; } @@ -8578,8 +9319,7 @@ sort_table: SORT tableref[table] sort_keys sort_dup sort_seq { keys.at(i++) = cbl_key_t(k); } - parser_sort( *$table, $sort_dup, $sort_seq, - keys.size(), keys.empty()? NULL : keys.data() ); + parser_sort( *$table, $sort_dup, $sort_seq, keys ); } | SORT tableref[table] sort_dup sort_seq { statement_begin(@1, SORT); @@ -8589,9 +9329,10 @@ sort_table: SORT tableref[table] sort_keys sort_dup sort_seq { cbl_key_t key = cbl_key_t($table->field->occurs.keys[0]), guess(1, &$table->field); - ; - if( key.nfield == 0 ) key = guess; - parser_sort( *$table, $sort_dup, $sort_seq, 1, &key ); + + if( key.fields.empty() ) key = guess; + std::vector<cbl_key_t> keys(1, key); + parser_sort( *$table, $sort_dup, $sort_seq, keys ); } ; @@ -8632,7 +9373,7 @@ sort_file: SORT FILENAME[file] sort_keys sort_dup sort_seq parser_file_sort( file, $sort_dup, $sort_seq, - keys.size(), keys.empty()? NULL : keys.data(), + keys, ninput, inputs, noutput, outputs, in_proc, out_proc ); @@ -8716,16 +9457,20 @@ sort_target: label_name release: RELEASE NAME[record] FROM scalar[name] { - statement_begin(@1, RELEASE); - symbol_elem_t *record = symbol_find(@record, $record); - parser_move(cbl_field_of(record), *$name); - parser_release(cbl_field_of(record)); + if( ! mode_syntax_only() ) { + statement_begin(@1, RELEASE); + symbol_elem_t *record = symbol_find(@record, $record); + parser_move(cbl_field_of(record), *$name); + parser_release(cbl_field_of(record)); + } } | RELEASE NAME[record] { - statement_begin(@1, RELEASE); - symbol_elem_t *record = symbol_find(@record, $record); - parser_release(cbl_field_of(record)); + if( ! mode_syntax_only() ) { + statement_begin(@1, RELEASE); + symbol_elem_t *record = symbol_find(@record, $record); + parser_release(cbl_field_of(record)); + } } ; @@ -8824,7 +9569,7 @@ backward: %empty { $$ = false; } inspect: INSPECT backward inspected TALLYING tallies { statement_begin(@1, INSPECT); - ast_inspect( *$inspected, $backward, *$tallies ); + ast_inspect( @$, *$inspected, $backward, *$tallies ); } | INSPECT backward inspected TALLYING tallies REPLACING replacements { @@ -8836,8 +9581,8 @@ inspect: INSPECT backward inspected TALLYING tallies } statement_begin(@1, INSPECT); // All tallying is done before any replacing - ast_inspect( *$inspected, $backward, *$tallies ); - ast_inspect( *$inspected, $backward, *$replacements ); + ast_inspect( @$, *$inspected, $backward, *$tallies ); + ast_inspect( @$, *$inspected, $backward, *$replacements ); } | INSPECT backward inspected REPLACING replacements { @@ -8848,11 +9593,11 @@ inspect: INSPECT backward inspected TALLYING tallies YYERROR; } statement_begin(@1, INSPECT); - ast_inspect( *$inspected, $backward, *$replacements ); + ast_inspect( @$, *$inspected, $backward, *$replacements ); } | INSPECT backward inspected CONVERTING alpha_val[match] TO all alpha_val[replace_oper] - insp_mtquals[qual] + insp_mtqual[qual] { if( $all ) { $replace_oper->all = true; @@ -8866,6 +9611,22 @@ inspect: INSPECT backward inspected TALLYING tallies error_msg(@all, "ALL must be part of a figurative constant"); YYERROR; } + } else { + cbl_field_t *match = $match->field, + *replace = $replace_oper->field; + if( is_literal(match) && is_literal(replace) ) { + if( !$match->all && !$replace_oper->all) { + if( match->data.capacity != replace->data.capacity ) { + // Make a copy of replace, because nice_name returns a static + char *replace_name = xstrdup(nice_name_of(replace)); + error_msg(@match, "%qs, size %u NOT EQUAL %qs, size %u", + nice_name_of(match), match->data.capacity, + replace_name, replace->data.capacity); + free(replace_name); + YYERROR; + } + } + } } if( is_constant($inspected->field) ) { auto name = nice_name_of($inspected->field); @@ -8885,7 +9646,7 @@ inspect: INSPECT backward inspected TALLYING tallies tallies: { need_nume_set(); } tally { - $$ = new ast_inspect_list_t( *$tally ); + $$ = new cbl_inspect_opers_t( 1, *$tally ); } | tallies { need_nume_set(); } tally { @@ -8895,12 +9656,17 @@ tallies: { need_nume_set(); } tally if( !next.tally.field ) { // prior tally swallowed one too many cbl_inspect_t& prior = $$->back(); - assert(prior.nbound > 0); - assert(prior.opers); - cbl_inspect_oper_t& prior_op = prior.opers[prior.nbound - 1]; - - assert(prior_op.n_identifier_3 > 0 ); - next.tally = prior_op.matches[--prior_op.n_identifier_3].matching; + assert(prior.nbound() > 0); + cbl_inspect_oper_t& prior_op = prior.back(); + assert(! prior_op.matches.empty() ); + assert(prior_op.n_identifier_3() > 0 ); + cbl_inspect_match_t wrong_match = prior_op.matches.back(); + dbgmsg("moving overeager tally to next clause"); + dump_inspect_match(wrong_match); + next.tally = wrong_match.premature_tally(); + if( wrong_match.empty() ) { + prior_op.matches.pop_back(); + } } if( !next.tally.field ) { error_msg(@$, "missing summation field before FOR"); @@ -8912,44 +9678,37 @@ tallies: { need_nume_set(); } tally /* * numref might be "empty" only because it was consumed by a - * prior insp_mtquals, which can end in a scalar. If that + * prior insp_mtqual, which can end in a scalar. If that * happens, the tallies target, above, takes back the borrowed * scalar and assigns it to be the tally total, as the user * intended. */ tally: numeref[total] FOR tally_fors[fors] - { // reduce ast_inspect_t to cbl_inspect_t + { if( yydebug && !$total ) { - error_msg(@FOR, "caution: missing summation field before FOR"); + dbgmsg("tally: caution: missing summation field before FOR"); } - cbl_refer_t total( $total? *$total : cbl_refer_t() ); - $$ = new cbl_inspect_t( total, $fors->opers() ); + $$ = $fors; + if( $total ) $$->tally = *$total; } ; -tally_fors: tally_forth - { // reduce ast_inspect_oper_t to cbl_inspect_oper_t - cbl_inspect_oper_t oper( $1->bound, $1->matches ); - $$ = new ast_inspect_t; - $$ ->push_back(oper); - } - | tally_fors tally_forth - { - cbl_inspect_oper_t oper( $2->bound, $2->matches ); - $1 ->push_back(oper); - } +tally_fors: tally_forth { $$ = new cbl_inspect_t(1, *$1); } + | tally_fors tally_forth { $$->push_back(*$2); $$ = $1; } ; -tally_forth: CHARACTERS insp_mtquals[q] scalar[next_tally] +tally_forth: CHARACTERS insp_mtqual[q] scalar[next_tally] { // Add ensuing scalar as if it were an argument to CHARACTERS. // It will be moved to the succeeding FOR as its tally. - $q->matching = *$next_tally; - $$ = new ast_inspect_oper_t(*$q); + dbgmsg("saving overeager tally for next clause"); + $q->save_premature_tally(*$next_tally); + $$ = new cbl_inspect_oper_t(*$q); + dump_inspect_match($$->matches.back()); } - | CHARACTERS insp_mtquals[q] + | CHARACTERS insp_mtqual[q] { - $$ = new ast_inspect_oper_t(*$q); + $$ = new cbl_inspect_oper_t(*$q); } | ALL tally_matches[q] { $q->bound = bound_all_e; @@ -8962,32 +9721,27 @@ tally_forth: CHARACTERS insp_mtquals[q] scalar[next_tally] | TRAILING tally_matches[q] { $q->bound = bound_trailing_e; $$ = $q; - if( ! dialect_mf() ) { - dialect_error(@1, "TRAILING", "mf"); - } + dialect_ok(@1, MfTrailing, "TRAILING"); } ; -tally_matches: tally_match { $$ = new ast_inspect_oper_t(*$1); } +tally_matches: tally_match { $$ = new cbl_inspect_oper_t(*$1); } | tally_matches tally_match { // add to the list of matches for an operand $1->matches.push_back(*$2); } ; -tally_match: alpha_val[matching] insp_mtquals[q] +tally_match: alpha_val[matching] insp_mtqual[q] { // include the matching field with the qualifiers $$ = $q; - $$->matching = *$matching; + $$->matching(*$matching); } ; numeref: %empty { $$ = NULL; need_nume_set(false); } | nume[name] subscripts[subs] { - size_t n = $subs->size(); - auto offsets = new cbl_refer_t[n]; - std::copy( $subs->begin(), $subs->end(), offsets ); - $$ = new cbl_refer_t($name, n, offsets); + $$ = new cbl_refer_t($name, $subs->vectorize()); } | nume { $$ = new cbl_refer_t($nume); } ; @@ -9017,13 +9771,13 @@ qnume: NUME { name_queue.qualify(@1, $1); } replacements: replacement { - cbl_inspect_t inspect( cbl_refer_t(), $1->opers() ); - $$ = new ast_inspect_list_t(inspect); + cbl_inspect_t inspect( cbl_refer_t(), *$1 ); + $$ = new cbl_inspect_opers_t(1, inspect); } ; replacement: replace_oper { - $$ = new ast_inspect_t; + $$ = new cbl_inspect_t; $$->push_back( cbl_inspect_oper_t($1->bound, $1->replaces) ); } | replacement replace_oper @@ -9031,9 +9785,9 @@ replacement: replace_oper $$->push_back( cbl_inspect_oper_t($2->bound, $2->replaces) ); } ; -replace_oper: CHARACTERS BY alpha_val[replace] insp_mtquals[q] +replace_oper: CHARACTERS BY alpha_val[replace] insp_mtqual[q] { - $$ = new ast_inspect_oper_t( cbl_inspect_replace_t(NULL, + $$ = new cbl_inspect_oper_t( cbl_inspect_replace_t(NULL, *$replace, $q->before, $q->after) ); @@ -9047,21 +9801,22 @@ replace_oper: CHARACTERS BY alpha_val[replace] insp_mtquals[q] x_by_ys: x_by_y { - $$ = new ast_inspect_oper_t(*$1); + $$ = new cbl_inspect_oper_t(*$1); } | x_by_ys x_by_y { $$->replaces.push_back(*$2); } ; -x_by_y: alpha_val[matching] BY alpha_val[replace] insp_mtquals[q] +x_by_y: alpha_val[matching] BY alpha_val[replace] insp_mtqual[q] { $$ = new cbl_inspect_replace_t(*$matching, *$replace, $q->before, $q->after); } ; -insp_mtquals: %empty { $$ = new cbl_inspect_match_t; } + /* mt may be "empty": match may have no qualifiers */ +insp_mtqual: %empty { $$ = new cbl_inspect_match_t; } | insp_quals ; insp_quals: insp_qual { @@ -9071,6 +9826,7 @@ insp_quals: insp_qual { } else { $$->after = *$insp_qual.qual; } + dump_inspect_match(*$$); } | insp_quals insp_qual { @@ -9096,9 +9852,7 @@ first_leading: FIRST { $$ = bound_first_e; } | ALL { $$ = bound_all_e; } | LEADING { $$ = bound_leading_e; } | TRAILING { $$ = bound_trailing_e; - if( ! dialect_mf() ) { - dialect_error(@1, "TRAILING", "mf"); - } + dialect_ok(@1, MfTrailing, "TRAILING"); } ; @@ -9230,7 +9984,7 @@ call_impl: CALL call_body[body] cbl_ffi_arg_t *pargs = NULL; if( narg > 0 ) { std::copy( params->elems.begin(), - params->elems.end(), args.begin() ); + params->elems.end(), args.begin() ); pargs = args.data(); } ast_call( $body.loc, *$body.ffi_name, @@ -9247,15 +10001,13 @@ call_cond: CALL call_body[body] call_excepts[except] cbl_ffi_arg_t *pargs = NULL; if( narg > 0 ) { std::copy( params->elems.begin(), - params->elems.end(), args.begin() ); + params->elems.end(), args.begin() ); pargs = args.data(); } ast_call( $body.loc, *$body.ffi_name, *$body.ffi_returning, narg, pargs, $except.on_error, $except.not_error, false ); - auto handled = ec_type_t( static_cast<size_t>(ec_program_e) | - static_cast<size_t>(ec_external_e)); - current.declaratives_evaluate(handled); + current.declaratives_evaluate(); } ; end_call: %empty %prec CALL @@ -9290,9 +10042,7 @@ call_body: ffi_name ; call_returning: RETURNING | GIVING { - if( !dialect_mf() ) { - dialect_error(@1, "CALL ... GIVING", "mf"); - } + dialect_ok(@1, MfCallGiving, "CALL ... GIVING"); } ; @@ -9332,7 +10082,12 @@ ffi_name: scalar $$->field = new_literal(strlen(L.name), L.name, quoted_e); } } - | LITERAL { $$ = new_reference(new_literal($1, quoted_e)); } + | LITERAL + { + // Pretend hex-encoded because that means use verbatim. + auto attr = cbl_field_attr_t(quoted_e | hex_encoded_e); + $$ = new_reference(new_literal($1, attr)); + } ; parameters: parameter { $$ = new ffi_args_t($1); } @@ -9444,7 +10199,7 @@ call_except: EXCEPTION { $$.not_error = NULL; $$.on_error = label_add(LblArith, - uniq_label("call"), yylineno); + uniq_label("call"), @1.first_line); if( !$$.on_error ) YYERROR; parser_call_exception( $$.on_error ); @@ -9453,15 +10208,15 @@ call_except: EXCEPTION std::swap($$.on_error, $$.not_error); } } - | OVERFLOW + | OVERFLOW_kw { $$.not_error = NULL; $$.on_error = label_add(LblArith, - uniq_label("call"), yylineno); + uniq_label("call"), @1.first_line); if( !$$.on_error ) YYERROR; parser_call_exception( $$.on_error ); - assert( $1 == OVERFLOW || $1 == NOT ); + assert( $1 == OVERFLOW_kw || $1 == NOT ); if( $1 == NOT ) { std::swap($$.on_error, $$.not_error); } @@ -9492,7 +10247,7 @@ alter_tgt: label_1[old] alter_to label_1[new] cbl_perform_tgt_t tgt( $old, $new ); parser_alter(&tgt); - auto prog = cbl_label_of( symbol_at(symbol_elem_of($old)->program)); + const auto prog = cbl_label_of( symbol_at(symbol_elem_of($old)->program)); if( prog->initial ) { cbl_unimplemented("ALTER %s", $old->name); } @@ -9513,7 +10268,7 @@ go_to: GOTO labels[args] } for( auto& label : $args->elems ) { - label->used = yylineno; + label->used = @2.first_line; } cbl_label_t *arg = $args->elems.front(); parser_goto( cbl_refer_t(), 1, &arg ); @@ -9525,7 +10280,7 @@ go_to: GOTO labels[args] std::vector <cbl_label_t *> args($args->elems.size()); std::copy($args->elems.begin(), $args->elems.end(), args.begin()); for( auto& label : $args->elems ) { - label->used = yylineno; + label->used = @2.first_line; } parser_goto( *$value, args.size(), args.data() ); } @@ -9539,13 +10294,15 @@ go_to: GOTO labels[args] resume: RESUME NEXT STATEMENT { statement_begin(@1, RESUME); + dialect_not_ok( @1, IsoResume, "RESUME"); parser_clear_exception(); } | RESUME label_1[tgt] { statement_begin(@1, RESUME); + dialect_not_ok( @1, IsoResume, "RESUME"); parser_clear_exception(); - $tgt->used = yylineno; + $tgt->used = @1.first_line; parser_goto( cbl_refer_t(), 1, &$tgt ); } ; @@ -9595,14 +10352,14 @@ string: string_impl end_string string_impl: STRING_kw string_body[body] { stringify($body.inputs, *$body.into.first, *$body.into.second); - current.declaratives_evaluate(ec_none_e); + current.declaratives_evaluate(); } ; string_cond: STRING_kw string_body[body] on_overflows[over] { stringify($body.inputs, *$body.into.first, *$body.into.second, $over.on_error, $over.not_error); - current.declaratives_evaluate(ec_overflow_e); + current.declaratives_evaluate(); } ; end_string: %empty %prec LITERAL @@ -9716,15 +10473,15 @@ on_overflows: on_overflow[over] statements %prec ADD } ; -on_overflow: OVERFLOW +on_overflow: OVERFLOW_kw { $$.not_error = NULL; $$.on_error = label_add(LblString, - uniq_label("string"), yylineno); + uniq_label("string"), @1.first_line); if( !$$.on_error ) YYERROR; parser_string_overflow( $$.on_error ); - assert( $1 == OVERFLOW || $1 == NOT ); + assert( $1 == OVERFLOW_kw || $1 == NOT ); if( $1 == NOT ) { std::swap($$.on_error, $$.not_error); } @@ -9741,14 +10498,14 @@ end_unstring: %empty %prec UNSTRING unstring_impl: UNSTRING unstring_body[body] { unstringify( *$body.input, $body.delimited, $body.into ); - current.declaratives_evaluate(ec_none_e); + current.declaratives_evaluate(); } ; unstring_cond: UNSTRING unstring_body[body] on_overflows[over] { unstringify( *$body.input, $body.delimited, $body.into, $over.on_error, $over.not_error ); - current.declaratives_evaluate(ec_overflow_e); + current.declaratives_evaluate(); } ; @@ -9883,18 +10640,22 @@ function_udf: FUNCTION_UDF '(' arg_list[args] ')' { if( ! current.udf_args_valid(L, $args->refers, params) ) { YYERROR; } - $$ = new_temporary_clone(cbl_field_of(symbol_at(L->returning))); + const auto returning = cbl_field_of(symbol_at(L->returning)); + $$ = new_temporary_clone(returning); + $$->data.initial = returning->name; // user's name for the field std::vector <cbl_ffi_arg_t> args($args->refers.size()); size_t i = 0; // Pass parameters as defined by the function. std::transform( $args->refers.begin(), $args->refers.end(), args.begin(), - [params, &i]( cbl_refer_t& arg ) { + [params, &i]( const cbl_refer_t& arg ) { function_descr_arg_t param = params.at(i++); auto ar = new cbl_refer_t(arg); cbl_ffi_arg_t actual(param.crv, ar); return actual; } ); - auto name = new_literal(strlen(L->name), L->name, quoted_e); + // Pretend hex-encoded because that means use verbatim. + auto attr = cbl_field_attr_t(quoted_e | hex_encoded_e); + auto name = new_literal(strlen(L->name), L->name, attr); ast_call( @1, name, $$, args.size(), args.data(), NULL, NULL, true ); } | FUNCTION_UDF_0 { @@ -9902,9 +10663,13 @@ function_udf: FUNCTION_UDF '(' arg_list[args] ')' { static cbl_ffi_arg_t *args = NULL; auto L = cbl_label_of(symbol_at($1)); - $$ = new_temporary_clone(cbl_field_of(symbol_at(L->returning))); + const auto returning = cbl_field_of(symbol_at(L->returning)); + $$ = new_temporary_clone(returning); + $$->data.initial = returning->name; // user's name for the field - auto name = new_literal(strlen(L->name), L->name, quoted_e); + // Pretend hex-encoded because that means use verbatim. + auto attr = cbl_field_attr_t(quoted_e | hex_encoded_e); + auto name = new_literal(strlen(L->name), L->name, attr); ast_call( @1, name, $$, narg, args, NULL, NULL, true ); } ; @@ -9923,7 +10688,6 @@ function_udf: FUNCTION_UDF '(' arg_list[args] ')' { * var: [ALL] LITERAL, NUMSTR, instrinsic, or scalar * num_operand: signed NUMSTR/ZERO, instrinsic, or scalar * alpahaval: LITERAL, reserved_value, instrinsic, or scalar - * Probably any numeric argument could be an expression. */ intrinsic: function_udf | intrinsic0 @@ -9936,90 +10700,91 @@ intrinsic: function_udf args.data()); if( p != NULL ) { auto loc = symbol_field_location(field_index(p->field)); - error_msg(loc, "FUNCTION %s has " - "inconsistent parameter type %zu ('%s')", - keyword_str($1), p - args.data(), name_of(p->field) ); + error_msg(loc, "FUNCTION %qs has " + "inconsistent parameter type %ld (%qs)", + keyword_str($1), (long)(p - args.data()), name_of(p->field) ); YYERROR; } $$ = is_numeric(args[0].field)? new_tempnumeric_float() : - new_alphanumeric(args[0].field->data.capacity); - + new_alphanumeric(); + $$->data.initial = keyword_str($1); parser_intrinsic_callv( $$, intrinsic_cname($1), args.size(), args.data() ); } - | PRESENT_VALUE '(' expr_list[args] ')' + | PRESENT_VALUE '(' arg_list[args] ')' { static char s[] = "__gg__present_value"; location_set(@1); - $$ = new_tempnumeric_float(); + $$ = new_tempnumeric_float("PRESENT-VALUE"); size_t n = $args->size(); assert(n > 0); if( n < 2 ) { - error_msg(@args, "PRESENT VALUE requires 2 parameters"); + error_msg(@args, "PRESENT-VALUE requires 2 parameters"); YYERROR; } std::vector <cbl_refer_t> args(n); std::copy( $args->begin(), $args->end(), args.begin() ); + bool ok = std::all_of( args.begin(), + args.end(), [loc = @1]( auto r ) { + return require_numeric(loc, r); } ); + if( ! ok ) YYERROR; parser_intrinsic_callv( $$, s, args.size(), args.data() ); } | BASECONVERT '(' varg[r1] varg[r2] varg[r3] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("BASECONVERT"); cbl_unimplemented("BASECONVERT"); if( ! intrinsic_call_3($$, BASECONVERT, $r1, $r2, $r3 )) YYERROR; } | BIT_OF '(' expr[r1] ')' { location_set(@1); - $$ = new_alphanumeric(8 * $r1->field->data.capacity); + $$ = new_alphanumeric("BIT-OF"); if( ! intrinsic_call_1($$, BIT_OF, $r1, @r1)) YYERROR; } | CHAR '(' expr[r1] ')' { location_set(@1); - $$ = new_alphanumeric(1); + $$ = new_alphanumeric(1,"CHAR"); if( ! intrinsic_call_1($$, CHAR, $r1, @r1)) YYERROR; } | CONVERT '(' varg[r1] convert_src[src] convert_dst[dst] ')' { location_set(@1); - $$ = new_alphanumeric(1); + $$ = new_alphanumeric(1,"CONVERT"); cbl_unimplemented("CONVERT"); /* if( ! intrinsic_call_3($$, CONVERT, $r1, $src, $dst) ) YYERROR; */ } | DISPLAY_OF '(' varg[r1] ')' { location_set(@1); - uint32_t len = $r1->field->data.capacity; - $$ = new_alphanumeric(4 * len); + $$ = new_alphanumeric("DISPLAY-OF"); if( ! intrinsic_call_2($$, DISPLAY_OF, $r1, NULL) ) YYERROR; } | DISPLAY_OF '(' varg[r1] varg[r2] ')' { location_set(@1); - uint32_t len = $r1->field->data.capacity - + $r2->field->data.capacity; - $$ = new_alphanumeric(4 * len); + $$ = new_alphanumeric("DISPLAY-OF"); if( ! intrinsic_call_2($$, DISPLAY_OF, $r1, $r2) ) YYERROR; } | EXCEPTION_FILE filename { location_set(@1); - $$ = new_alphanumeric(256); + $$ = new_alphanumeric("EXCEPTION-FILE"); parser_exception_file( $$, $filename ); } | FIND_STRING '(' varg[r1] last start_after anycase ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric("FIND-STRING"); /* auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); */ - cbl_unimplemented("FIND_STRING"); + cbl_unimplemented("%<FIND_STRING%>"); /* if( ! intrinsic_call_4($$, FIND_STRING, r1, $r2) ) YYERROR; */ } | FORMATTED_DATE '(' DATE_FMT[r1] expr[r2] ')' { location_set(@1); - $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATE); + $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATE, "FORMATTED-DATE"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, FORMATTED_DATE, r1, $r2) ) YYERROR; } @@ -10028,7 +10793,7 @@ intrinsic: function_udf | FORMATTED_DATETIME '(' DATETIME_FMT[r1] expr[r2] expr[r3] ')' { location_set(@1); - $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME); + $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME, "FORMATTED-DATETIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); static cbl_refer_t r3(literally_zero); if( ! intrinsic_call_4($$, FORMATTED_DATETIME, @@ -10037,7 +10802,7 @@ intrinsic: function_udf | FORMATTED_DATETIME '(' DATETIME_FMT[r1] expr[r2] expr[r3] expr[r4] ')' { location_set(@1); - $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME); + $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME, "FORMATTED-DATETIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_4($$, FORMATTED_DATETIME, r1, $r2, $r3, $r4) ) YYERROR; @@ -10048,14 +10813,14 @@ intrinsic: function_udf | FORMATTED_TIME '(' TIME_FMT[r1] expr[r2] expr[r3] ')' { location_set(@1); - $$ = new_alphanumeric(MAXLENGTH_FORMATTED_TIME); + $$ = new_alphanumeric(MAXLENGTH_FORMATTED_TIME, "FORMATTED-DATETIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_3($$, FORMATTED_TIME, r1, $r2, $r3) ) YYERROR; } | FORMATTED_TIME '(' TIME_FMT[r1] expr[r2] ')' { location_set(@1); - $$ = new_alphanumeric(MAXLENGTH_FORMATTED_TIME); + $$ = new_alphanumeric(MAXLENGTH_FORMATTED_TIME, "FORMATTED-TIME"); auto r3 = new_reference(new_literal("0")); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_3($$, FORMATTED_TIME, @@ -10063,21 +10828,21 @@ intrinsic: function_udf } | FORMATTED_CURRENT_DATE '(' DATETIME_FMT[r1] ')' { location_set(@1); - $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME); + $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME, "FORMATTED-CURRENT_DATE"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_1($$, FORMATTED_CURRENT_DATE, r1, @r1) ) YYERROR; } | TEST_FORMATTED_DATETIME '(' DATE_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("TEST-FORMATTED-DATETIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, TEST_FORMATTED_DATETIME, r1, $r2) ) YYERROR; } | TEST_FORMATTED_DATETIME '(' TIME_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("TEST-FORMATTED-DATETIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, TEST_FORMATTED_DATETIME, r1, $r2) ) YYERROR; @@ -10085,14 +10850,14 @@ intrinsic: function_udf | TEST_FORMATTED_DATETIME '(' DATETIME_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("TEST-FORMATTED-DATETIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, TEST_FORMATTED_DATETIME, r1, $r2) ) YYERROR; } | INTEGER_OF_FORMATTED_DATE '(' DATE_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("INTEGER-OF-FORMATTED-DATE"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, INTEGER_OF_FORMATTED_DATE, r1, $r2) ) YYERROR; @@ -10100,14 +10865,14 @@ intrinsic: function_udf | INTEGER_OF_FORMATTED_DATE '(' DATETIME_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("INTEGER-OF-FORMATTED-DATE"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, INTEGER_OF_FORMATTED_DATE, r1, $r2) ) YYERROR; } | SECONDS_FROM_FORMATTED_TIME '(' TIME_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("SECONDS-FROM-FORMATTED-TIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, SECONDS_FROM_FORMATTED_TIME, r1, $r2) ) YYERROR; @@ -10115,7 +10880,7 @@ intrinsic: function_udf | SECONDS_FROM_FORMATTED_TIME '(' DATETIME_FMT[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("SECONDS-FROM-FORMATTED-TIME"); auto r1 = new_reference(new_literal(strlen($r1), $r1, quoted_e)); if( ! intrinsic_call_2($$, SECONDS_FROM_FORMATTED_TIME, r1, $r2) ) YYERROR; @@ -10123,85 +10888,83 @@ intrinsic: function_udf | HEX_OF '(' varg[r1] ')' { location_set(@1); - $$ = new_alphanumeric(2 * $r1->field->data.capacity); + $$ = new_alphanumeric("HEX-OF"); if( ! intrinsic_call_1($$, HEX_OF, $r1, @r1)) YYERROR; } | LENGTH '(' tableish[val] ')' { location_set(@1); - $$ = new_tempnumeric(); - $$->clear_attr(signable_e); + $$ = new_tempnumeric("LENGTH", none_e); parser_set_numeric($$, $val->field->size()); if( ! intrinsic_call_1($$, LENGTH, $val, @val)) YYERROR; } | LENGTH '(' varg1a[val] ')' { location_set(@1); - $$ = new_tempnumeric(); - $$->clear_attr(signable_e); + $$ = new_tempnumeric("LENGTH", none_e); parser_set_numeric($$, $val->field->data.capacity); if( ! intrinsic_call_1($$, LENGTH, $val, @val)) YYERROR; } | lopper_case[func] '(' alpha_val[r1] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric($r1->field->data.capacity, "lopper_case[func]"); if( ! intrinsic_call_1($$, $func, $r1, @r1)) YYERROR; } | MODULE_NAME '(' module_type[type] ')' { - $$ = new_alphanumeric(sizeof(cbl_name_t)); + $$ = new_alphanumeric(sizeof(cbl_name_t), "MODULE-NAME"); parser_module_name( $$, $type ); } | NUMVAL_C '(' varg[r1] numval_locale[r2] anycase ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("NUMVAL-C"); parser_intrinsic_numval_c( $$, *$r1, $r2.is_locale, *$r2.arg2, $anycase ); } | ORD '(' alpha_val[r1] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("ORD", none_e); if( ! intrinsic_call_1($$, ORD, $r1, @r1)) YYERROR; } | RANDOM { location_set(@1); - $$ = new_tempnumeric_float(); + $$ = new_tempnumeric_float("RANDOM"); parser_intrinsic_call_0( $$, intrinsic_cname(RANDOM) ); } | RANDOM_SEED expr[r1] ')' { // left parenthesis consumed by lexer location_set(@1); - $$ = new_tempnumeric_float(); + $$ = new_tempnumeric_float("RANDOM-SEED"); if( ! intrinsic_call_1($$, RANDOM, $r1, @r1)) YYERROR; } | STANDARD_COMPARE '(' varg[r1] varg[r2] varg[r3] varg[r4] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("STANDARD-COMPARE"); cbl_unimplemented("STANDARD-COMPARE"); /* if( ! intrinsic_call_4($$, STANDARD_COMPARE, $r1) ) YYERROR; */ } | STANDARD_COMPARE '(' varg[r1] varg[r2] varg[r3] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("STANDARD-COMPARE"); cbl_unimplemented("STANDARD-COMPARE"); /* if( ! intrinsic_call_4($$, STANDARD_COMPARE, $r1) ) YYERROR; */ } | STANDARD_COMPARE '(' varg[r1] varg[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("STANDARD-COMPARE"); cbl_unimplemented("STANDARD-COMPARE"); /* if( ! intrinsic_call_4($$, STANDARD_COMPARE, $r1) ) YYERROR; */ } | SUBSTITUTE '(' varg[r1] subst_inputs[inputs] ')' { location_set(@1); - $$ = new_alphanumeric(64); + $$ = new_alphanumeric("SUBSTITUTE"); std::vector <cbl_substitute_t> args($inputs->size()); std::transform( $inputs->begin(), $inputs->end(), args.begin(), []( const substitution_t& arg ) { @@ -10217,7 +10980,7 @@ intrinsic: function_udf | TEST_NUMVAL_C '(' varg[r1] numval_locale[r2] anycase ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("parser_intrinsic_subst($$,"); parser_intrinsic_numval_c( $$, *$r1, $r2.is_locale, *$r2.arg2, $anycase, true ); } @@ -10244,14 +11007,14 @@ intrinsic: function_udf YYERROR; break; } - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric("TRIM"); cbl_refer_t * how = new_reference($trim_trailing); if( ! intrinsic_call_2($$, TRIM, $r1, how) ) YYERROR; } | USUBSTR '(' alpha_val[r1] expr[r2] expr[r3] ')' { location_set(@1); - $$ = new_alphanumeric(32); // how long? + $$ = new_alphanumeric("USUBSTR"); if( ! intrinsic_call_3($$, FORMATTED_DATETIME, $r1, $r2, $r3) ) YYERROR; } @@ -10259,14 +11022,14 @@ intrinsic: function_udf | intrinsic_I '(' expr[r1] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric(keyword_str($1)); if( ! intrinsic_call_1($$, $1, $r1, @r1)) YYERROR; } | intrinsic_N '(' expr[r1] ')' { location_set(@1); - $$ = new_tempnumeric_float(); + $$ = new_tempnumeric_float(keyword_str($1)); if( ! intrinsic_call_1($$, $1, $r1, @r1)) YYERROR; } @@ -10276,30 +11039,20 @@ intrinsic: function_udf auto type = intrinsic_return_type($1); switch(type) { case FldAlphanumeric: - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(keyword_str($1)); break; default: - if( $1 == NUMVAL || $1 == NUMVAL_F ) - { - $$ = new_temporary(FldFloat); - } - else - { - $$ = new_temporary(type); - } + if( $1 == NUMVAL || $1 == NUMVAL_F ) { + $$ = new_temporary(FldFloat, keyword_str($1)); + } else { + $$ = new_temporary(type, keyword_str($1)); + } } if( $1 == NUMVAL_F ) { - if( is_literal($r1->field) ) { - _Float128 output __attribute__ ((__unused__)); + if( is_literal($r1->field) && ! is_numeric($r1->field->type) ) { + // The parameter might be literal, but could be "hello". auto input = $r1->field->data.initial; - auto local = xstrdup(input), pend = local; - std::replace(local, local + strlen(local), ',', '.'); - std::remove_if(local, local + strlen(local), isspace); - output = strtof128(local, &pend); - // bad if strtof128 could not convert input - if( *pend != '\0' ) { - error_msg(@r1, "'%s' is not a numeric string", input); - } + error_msg(@r1, "'%s' is not a numeric literal", input); } } if( ! intrinsic_call_1($$, $1, $r1, @r1)) YYERROR; @@ -10308,7 +11061,7 @@ intrinsic: function_udf | intrinsic_I2 '(' expr[r1] expr[r2] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("intrinsic_I2"); if( ! intrinsic_call_2($$, $1, $r1, $r2) ) YYERROR; } @@ -10319,12 +11072,12 @@ intrinsic: function_udf static auto one = new cbl_refer_t( new_literal("1") ); static auto four = new cbl_refer_t( new_literal("4") ); cbl_span_t year(one, four); - auto r3 = new_reference(new_alphanumeric(21)); + auto r3 = new_reference(new_alphanumeric(MAXLENGTH_CALENDAR_DATE)); r3->refmod = year; parser_intrinsic_call_0( r3->field, "__gg__current_date" ); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("DATE_TO_YYYYMMDD"); if( ! intrinsic_call_3($$, DATE_TO_YYYYMMDD, $r1, r2, r3) ) YYERROR; } @@ -10335,12 +11088,12 @@ intrinsic: function_udf static auto one = new cbl_refer_t( new_literal("1") ); static auto four = new cbl_refer_t( new_literal("4") ); cbl_span_t year(one, four); - auto r3 = new_reference(new_alphanumeric(21)); + auto r3 = new_reference(new_alphanumeric(MAXLENGTH_CALENDAR_DATE)); r3->refmod = year; parser_intrinsic_call_0( r3->field, "__gg__current_date" ); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("DATE_TO_YYYYMMDD"); if( ! intrinsic_call_3($$, DATE_TO_YYYYMMDD, $r1, $r2, r3) ) YYERROR; } @@ -10349,7 +11102,7 @@ intrinsic: function_udf expr[r2] expr[r3] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("DATE_TO_YYYYMMDD"); if( ! intrinsic_call_3($$, DATE_TO_YYYYMMDD, $r1, $r2, $r3) ) YYERROR; } @@ -10361,12 +11114,12 @@ intrinsic: function_udf static auto one = new cbl_refer_t( new_literal("1") ); static auto four = new cbl_refer_t( new_literal("4") ); cbl_span_t year(one, four); - auto r3 = new_reference(new_alphanumeric(21)); + auto r3 = new_reference(new_alphanumeric(MAXLENGTH_CALENDAR_DATE)); r3->refmod = year; parser_intrinsic_call_0( r3->field, "__gg__current_date" ); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("DAY_TO_YYYYDDD"); if( ! intrinsic_call_3($$, DAY_TO_YYYYDDD, $r1, r2, r3) ) YYERROR; } @@ -10377,12 +11130,12 @@ intrinsic: function_udf static auto one = new cbl_refer_t( new_literal("1") ); static auto four = new cbl_refer_t( new_literal("4") ); cbl_span_t year(one, four); - auto r3 = new_reference(new_alphanumeric(21)); + auto r3 = new_reference(new_alphanumeric(MAXLENGTH_CALENDAR_DATE)); r3->refmod = year; parser_intrinsic_call_0( r3->field, "__gg__current_date" ); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("DAY_TO_YYYYDDD"); if( ! intrinsic_call_3($$, DAY_TO_YYYYDDD, $r1, $r2, r3) ) YYERROR; } @@ -10391,7 +11144,7 @@ intrinsic: function_udf expr[r2] expr[r3] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("DAY_TO_YYYYDDD"); if( ! intrinsic_call_3($$, DAY_TO_YYYYDDD, $r1, $r2, $r3) ) YYERROR; } @@ -10403,12 +11156,12 @@ intrinsic: function_udf static auto one = new cbl_refer_t( new_literal("1") ); static auto four = new cbl_refer_t( new_literal("4") ); cbl_span_t year(one, four); - auto r3 = new_reference(new_alphanumeric(21)); + auto r3 = new_reference(new_alphanumeric(MAXLENGTH_CALENDAR_DATE)); r3->refmod = year; parser_intrinsic_call_0( r3->field, "__gg__current_date" ); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("YEAR_TO_YYYY"); if( ! intrinsic_call_3($$, YEAR_TO_YYYY, $r1, r2, r3) ) YYERROR; } @@ -10419,12 +11172,12 @@ intrinsic: function_udf static auto one = new cbl_refer_t( new_literal("1") ); static auto four = new cbl_refer_t( new_literal("4") ); cbl_span_t year(one, four); - auto r3 = new_reference(new_alphanumeric(21)); + auto r3 = new_reference(new_alphanumeric(MAXLENGTH_CALENDAR_DATE)); r3->refmod = year; parser_intrinsic_call_0( r3->field, "__gg__current_date" ); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("YEAR_TO_YYYY"); if( ! intrinsic_call_3($$, YEAR_TO_YYYY, $r1, $r2, r3) ) YYERROR; } @@ -10433,7 +11186,7 @@ intrinsic: function_udf expr[r2] expr[r3] ')' { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("YEAR_TO_YYYY"); if( ! intrinsic_call_3($$, YEAR_TO_YYYY, $r1, $r2, $r3) ) YYERROR; } @@ -10441,25 +11194,25 @@ intrinsic: function_udf | intrinsic_N2 '(' expr[r1] expr[r2] ')' { location_set(@1); - switch($1) - { - case ANNUITY: - $$ = new_tempnumeric_float(); - break; - case COMBINED_DATETIME: - $$ = new_tempnumeric(); - break; - case REM: - $$ = new_tempnumeric_float(); - break; - } + switch($1) { + case ANNUITY: + $$ = new_tempnumeric_float(); + break; + case COMBINED_DATETIME: + $$ = new_tempnumeric(); + break; + case REM: + $$ = new_tempnumeric_float(); + break; + } + $$->data.initial = keyword_str($1); // function name if( ! intrinsic_call_2($$, $1, $r1, $r2) ) YYERROR; } | intrinsic_X2 '(' varg[r1] varg[r2] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(keyword_str($1)); if( ! intrinsic_call_2($$, $1, $r1, $r2) ) YYERROR; } | intrinsic_locale @@ -10490,7 +11243,7 @@ numval_locale: %empty { $$.arg2 = cbl_refer_t::empty(); } | LOCALE NAME { $$.is_locale = true; $$.arg2 = NULL; - cbl_unimplemented("NUMVAL_C LOCALE"); YYERROR; + cbl_unimplemented("%<NUMVAL_C LOCALE%>"); YYERROR; } | varg { $$.is_locale = false; $$.arg2 = $1; } ; @@ -10503,58 +11256,71 @@ subst_input: anycase first_last varg[v1] varg[v2] { } ; +locale_name: NAME + { + auto e = symbol_locale(PROGRAM, $NAME); + if( !e ) { + error_msg(@NAME, "no such SPECIAL-NAMES LOCALE: %qs", $NAME); + YYERROR; + } + $$ = const_cast<char*>( + __gg__encoding_iconv_name(cbl_locale_of(e)->encoding) ); + } + ; + intrinsic_locale: LOCALE_COMPARE '(' varg[r1] varg[r2] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); cbl_refer_t dummy = {}; if( ! intrinsic_call_3($$, LOCALE_COMPARE, $r1, $r2, &dummy) ) YYERROR; } - | LOCALE_COMPARE '(' varg[r1] varg[r2] varg[r3] ')' + | LOCALE_COMPARE '(' varg[r1] varg[r2] locale_name ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); - if( ! intrinsic_call_3($$, LOCALE_COMPARE, $r1, $r2, $r3) ) YYERROR; + $$ = new_alphanumeric(); + cbl_refer_t locale(new_literal($locale_name)); + if( ! intrinsic_call_3($$, LOCALE_COMPARE, $r1, $r2, &locale) ) YYERROR; } | LOCALE_DATE '(' varg[r1] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); cbl_refer_t dummy = {}; if( ! intrinsic_call_2($$, LOCALE_DATE, $r1, &dummy) ) YYERROR; } | LOCALE_DATE '(' varg[r1] varg[r2] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); if( ! intrinsic_call_2($$, LOCALE_DATE, $r1, $r2) ) YYERROR; } | LOCALE_TIME '(' varg[r1] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); cbl_refer_t dummy = {}; if( ! intrinsic_call_2($$, LOCALE_TIME, $r1, &dummy) ) YYERROR; } | LOCALE_TIME '(' varg[r1] varg[r2] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); if( ! intrinsic_call_2($$, LOCALE_TIME, $r1, $r2) ) YYERROR; } | LOCALE_TIME_FROM_SECONDS '(' varg[r1] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); cbl_refer_t dummy = {}; if( ! intrinsic_call_2($$, LOCALE_TIME_FROM_SECONDS, $r1, &dummy) ) YYERROR; } | LOCALE_TIME_FROM_SECONDS '(' varg[r1] varg[r2] ')' { location_set(@1); - $$ = new_alphanumeric($r1->field->data.capacity); + $$ = new_alphanumeric(); if( ! intrinsic_call_2($$, LOCALE_TIME_FROM_SECONDS, $r1, $r2) ) YYERROR; } ; @@ -10570,65 +11336,66 @@ trim_trailing: %empty { $$ = new_literal("0"); } // Remove both intrinsic0: CURRENT_DATE { location_set(@1); - $$ = new_alphanumeric(21); + $$ = new_alphanumeric(MAXLENGTH_CALENDAR_DATE, "CURRENT-DATE"); parser_intrinsic_call_0( $$, "__gg__current_date" ); } | E { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("E"); parser_intrinsic_call_0( $$, "__gg__e" ); } | EXCEPTION_FILE_N { location_set(@1); - $$ = new_alphanumeric(256); + $$ = new_alphanumeric("EXCEPTION-FILE-N"); intrinsic_call_0( $$, EXCEPTION_FILE_N ); } | EXCEPTION_FILE { location_set(@1); - $$ = new_alphanumeric(256); + $$ = new_alphanumeric("EXCEPTION-FILE"); parser_exception_file( $$ ); } | EXCEPTION_LOCATION_N { location_set(@1); - $$ = new_alphanumeric(256); + $$ = new_alphanumeric("EXCEPTION-LOCATION-N"); intrinsic_call_0( $$, EXCEPTION_LOCATION_N ); } | EXCEPTION_LOCATION { location_set(@1); - $$ = new_alphanumeric(256); + $$ = new_alphanumeric("EXCEPTION-LOCATION"); intrinsic_call_0( $$, EXCEPTION_LOCATION ); } | EXCEPTION_STATEMENT { location_set(@1); - $$ = new_alphanumeric(63); + $$ = new_alphanumeric("EXCEPTION-STATEMENT"); intrinsic_call_0( $$, EXCEPTION_STATEMENT ); } | EXCEPTION_STATUS { location_set(@1); - $$ = new_alphanumeric(31); + $$ = new_alphanumeric("EXCEPTION-STATUS"); intrinsic_call_0( $$, EXCEPTION_STATUS ); } | PI { location_set(@1); - $$ = new_tempnumeric_float(); + $$ = new_tempnumeric_float("PI"); parser_intrinsic_call_0( $$, "__gg__pi" ); } | SECONDS_PAST_MIDNIGHT { location_set(@1); - $$ = new_tempnumeric(); + $$ = new_tempnumeric("SECONDS-PAST-MIDNIGHT"); intrinsic_call_0( $$, SECONDS_PAST_MIDNIGHT ); } | UUID4 { location_set(@1); - $$ = new_alphanumeric(32); // don't know correct size + $$ = new_alphanumeric("UUID4"); parser_intrinsic_call_0( $$, "__gg__uuid4" ); } | WHEN_COMPILED { location_set(@1); - $$ = new_alphanumeric(21); // Returns YYYYMMDDhhmmssss-0500 + // Returns YYYYMMDDhhmmssss-0500) + $$ = new_alphanumeric(MAXLENGTH_CALENDAR_DATE, "WHEN-COMPILED"); parser_intrinsic_call_0( $$, "__gg__when_compiled" ); } ; @@ -10767,6 +11534,10 @@ first_last: %empty { $$ = 0; } | LAST { $$ = 'L'; } ; +for: %empty + | FOR + ; + is_global: %empty %prec GLOBAL { $$ = false; } | is GLOBAL { $$ = true; } ; @@ -10816,6 +11587,10 @@ optional: %empty { $$ = false; } | OPTIONAL { $$ = true; } ; +override: %empty { $$ = false; } + | OVERRIDE { $$ = true; } + ; + program_kw: %empty | PROGRAM_kw ; @@ -10832,6 +11607,11 @@ sign: %empty | SIGN ; +is_signed: %empty { $$ = true; } + | SIGNED_kw { $$ = true; } + | UNSIGNED_kw { $$ = false; } + ; + start_after: %empty %prec AFTER | START AFTER varg ; @@ -10859,6 +11639,20 @@ usage: %empty | USAGE IS ; +user_default: DEFAULT + { // cannot be empty + switch( $1 ) { + case 'U': break; + case 'S': + error_msg(@1, "invalid syntax: SYSTEM-DEFAULT"); + break; + default: + error_msg(@1, "invalid syntax: DEFAULT"); + gcc_unreachable(); + } + } + ; + with: %empty | WITH ; @@ -10880,7 +11674,12 @@ cdf_basis: BASIS NAME /* BASIS is never passed to the parser. */ | BASIS LITERAL ; -cdf_use: USE DEBUGGING on labels +cdf_use: cdf_use_when { + statement_cleanup = false; + } + ; + +cdf_use_when: USE DEBUGGING on labels { if( ! current.declarative_section_name() ) { error_msg(@1, "USE valid only in DECLARATIVES"); @@ -10898,12 +11697,11 @@ cdf_use: USE DEBUGGING on labels } static const cbl_label_t all = { LblNone, 0, 0,0,0, false, false, false, 0,0, ":all:" }; - ////.name = { ':', 'a', 'l', 'l', ':', } // workaround for gcc < 11.3 add_debugging_declarative(&all); } | USE globally mistake procedure on filenames - { + { // Format 1 if( ! current.declarative_section_name() ) { error_msg(@1, "USE valid only in DECLARATIVES"); YYERROR; @@ -10915,8 +11713,8 @@ cdf_use: USE DEBUGGING on labels std::back_inserter(files), file_list_t::symbol_index ); cbl_declarative_t declarative(current.declarative_section(), - ec_all_e, files, - file_mode_none_e, global); + ec_io_e, files, + file_mode_any_e, global); current.declaratives.add(declarative); } @@ -10929,12 +11727,12 @@ cdf_use: USE DEBUGGING on labels bool global = $globally == GLOBAL; std::list<size_t> files; cbl_declarative_t declarative(current.declarative_section(), - ec_all_e, files, + ec_io_e, files, $io_mode, global); current.declaratives.add(declarative); } - | USE cdf_use_excepts // Format 3: AFTER swallowed by lexer - { + | USE cdf_use_excepts + { // Format 3 (AFTER swallowed by lexer) if( ! current.declarative_section_name() ) { error_msg(@1, "USE valid only in DECLARATIVES"); YYERROR; @@ -11019,7 +11817,209 @@ cdf_none: ENTER | SERVICE_RELOAD ; +xmlgenerate: xmlgen_impl end_xml { + cbl_unimplemented("XML GENERATE"); + } + | xmlgen_cond end_xml { + cbl_unimplemented("XML GENERATE"); + } + ; +xmlgen_impl: + XMLGENERATE xmlgen_body + ; +xmlgen_cond: XMLGENERATE xmlgen_body[body] xmlexcepts[err] + ; + +xmlgen_body: XMLGENERATE name[id1] FROM name[id2] + xmlgen_count xmlencoding xmlgen_decl xmlgen_namespace + xmlgen_nameof xmlgen_typeof xmlgen_suppress + ; + +xmlgen_count: %empty + | COUNT in name[id3] + ; +xmlgen_decl: %empty + | with XML_DECLARATION with ATTRIBUTES + ; +xmlgen_namespace: + %empty + | NAMESPACE is name[id4] namespace_prefix + ; +namespace_prefix: + %empty + | NAMESPACE_PREFIX is namestr[id5] + ; +xmlgen_nameof: %empty + | NAME of xmlgen_ids + ; +xmlgen_ids: xmlgen_id + | xmlgen_ids xmlgen_id + ; +xmlgen_id: name[id6] is LITERAL[lit] + ; + +xmlgen_typeof: %empty + | TYPE of xmlgen_types + ; +xmlgen_types: xmlgen_type + | xmlgen_types xmlgen_type + ; +xmlgen_type: name[id6] is xmlgen_eltype + ; +xmlgen_eltype: ATTRIBUTE + | ELEMENT + | CONTENT + ; + +xmlgen_suppress: + %empty + | SUPPRESS xml_suppressions + ; +xml_suppressions: + xml_suppression + | xml_suppressions xml_suppression + ; +xml_suppression: + name[id8] xml_when_phrase + | xml_generic_suppression xml_when_figs + ; +xml_when_phrase: + %empty %prec ZERO + | xml_when_figs + ; +xml_when_figs: xml_when_fig + | xml_when_figs OR xml_when_fig + ; +xml_when_fig: ZERO + | SPACES + | LOW_VALUES + | HIGH_VALUES + ; +xml_generic_suppression: + %empty + | EVERY xml_generic_numeric xmlgen_eltype + ; +xml_generic_numeric: + %empty + | NUMERIC + | NONNUMERIC + ; + +xmlparse: xmlparse_impl end_xml { + auto xml_stmt = xml_statements.top(); + parser_xml_end(xml_stmt); + xml_statements.pop(); + current.declaratives_evaluate(); + } + | xmlparse_cond end_xml { + auto xml_stmt = xml_statements.top(); + parser_xml_end(xml_stmt); + xml_statements.pop(); + current.declaratives_evaluate(); + } + ; +xmlparse_impl: XMLPARSE xmlparse_body[body] + { + parser_xml_on_exception($body); + parser_xml_not_exception($body); + } + ; +xmlparse_cond: XMLPARSE xmlparse_body[body] xmlexcepts[err] + { + if( ! $err.on_error ) parser_xml_on_exception($body); + if( ! $err.not_error ) parser_xml_not_exception($body); + } + ; + +xmlparse_body: scalar xmlencoding xmlreturning xmlvalidating + PROCESSING PROCEDURE is xmlprocs[procs] + { + $$ = label_add(@$, LblXml, uniq_label("xml")); + xml_statements.push($$); + statement_begin(@$, XMLPARSE); + parser_xml_parse( $$, + *$scalar, + $xmlencoding, + $xmlvalidating, + $xmlreturning == NATIONAL, + $procs.from, + $procs.to ); + } + ; +xmlencoding: %empty %prec NAME { $$ = nullptr; } + | with ENCODING name [codepage] { $$ = $codepage; } + ; + +xmlreturning: %empty { $$ = 0; } + | RETURNING NATIONAL { $$ = NATIONAL; } + ; +xmlvalidating: %empty { $$ = nullptr; } + | VALIDATING with name { $$ = $name; } + | VALIDATING with FILE_KW name { $$ = $name; } + ; +xmlprocs: label_1 { + $$ = label_pair_t{$1}; + } + | label_1[from] THRU label_1[to] { + $$ = label_pair_t{$from, $to}; + } + ; + +xmlexcepts: xmlexcept[a] statements %prec XMLPARSE + { + assert( $a.on_error || $a.not_error ); + assert( ! ($a.on_error && $a.not_error) ); + $$ = $a; + } + | xmlexcepts[a] xmlexcept[b] statements %prec XMLPARSE + { + if( $a.on_error && $a.not_error ) { + error_msg(@1, "too many ON ERROR clauses"); + YYERROR; + } + // "ON" and "NOT ON" could be reversed, but not duplicated. + if( $a.on_error && $b.on_error ) { + error_msg(@1, "duplicate ON ERROR clauses"); + YYERROR; + } + if( $a.not_error && $b.not_error ) { + error_msg(@1, "duplicate NOT ON ERROR clauses"); + YYERROR; + } + $$ = $a; + if( $$.on_error ) { + assert($b.not_error); + $$.not_error = $b.not_error; + } else { + assert($b.on_error); + $$.on_error = $b.on_error; + } + } + ; +xmlexcept: EXCEPTION + { + auto xml_stmt = xml_statements.top(); + // The value of the pointer no longer matters, only NULL or not. + $$.on_error = $$.not_error = nullptr; + switch($1) { + case EXCEPTION: + $$.on_error = xml_stmt; + parser_xml_on_exception(xml_stmt); + break; + case NOT: + $$.not_error = xml_stmt; + parser_xml_not_exception(xml_stmt); + break; + default: + gcc_unreachable(); + } + } + ; + +end_xml: %empty %prec XMLPARSE + | END_XML %prec XMLPARSE + ; %% static YYLTYPE @@ -11029,60 +12029,93 @@ first_line_of( YYLTYPE loc ) { return loc; } -void ast_call( const YYLTYPE& loc, cbl_refer_t name, cbl_refer_t returning, +void ast_call( const YYLTYPE& loc, cbl_refer_t name, const cbl_refer_t& returning, size_t narg, cbl_ffi_arg_t args[], cbl_label_t *except, cbl_label_t *not_except, bool is_function) { if( is_literal(name.field) ) { - cbl_field_t called = { 0, FldLiteralA, FldInvalid, quoted_e | constant_e, - 0, 0, 77, nonarray, 0, "", - 0, cbl_field_t::linkage_t(), {}, NULL }; + cbl_field_t called = { FldLiteralA, quoted_e | constant_e, + name.field->data, 77 }; + called.attr |= name.field->attr; snprintf(called.name, sizeof(called.name), "_%s", name.field->data.initial); - called.data = name.field->data; + called.attr |= name.field->attr; name.field = cbl_field_of(symbol_field_add(PROGRAM, &called)); symbol_field_location(field_index(name.field), loc); parser_symbol_add(name.field); } - if( getenv("ast_call") ) { - dbgmsg("%s: calling %s returning %s with %zu args:", __func__, - name_of(name.field), - (returning.field)? returning.field->name : "[none]", - narg); - for( size_t i=0; i < narg; i++ ) { - const char *crv = "?"; - switch(args[i].crv) { - case by_default_e: crv = "def"; break; - case by_reference_e: crv = "ref"; break; - case by_content_e: crv = "con"; break; - case by_value_e: crv = "val"; break; - } - dbgmsg("%s: %4zu: %s @%p %s", __func__, - i, crv, args[i].refer.field, args[i].refer.field->name); - } - } parser_call( name, returning, narg, args, except, not_except, is_function ); } -static size_t -statement_begin( const YYLTYPE& loc, int token ) { - // The following statement generates a message at run-time - // parser_print_string("statement_begin()\n"); - location_set(loc); - prior_statement = token; - - parser_statement_begin(); +/* + * Check if any EC *could* be raised that would be handled by a declarative. If + * so, the generated statement epilog will ask the runtime library to attempt + * to match any raised EC with a declarative. If not, the statement epilog + * will be limited to calling the default EC handler, which logs unhandled ECs + * [todo] and calls abort(3) for fatal ECs. + */ +static bool +possible_ec() { + cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); + bool format_1 = current.declaratives.has_format_1(); + + bool enabled = 0xFF < (current.declaratives.status() + & + enabled_exceptions.status()); + bool epilog = enabled || format_1; + + dbgmsg("%sEC handling for DCL %08x && EC %08x with %s Format 1", + epilog? "" : "no ", + current.declaratives.status(), + enabled_exceptions.status(), format_1? "a" : "no"); + + return epilog; +} - if( token != CONTINUE ) { +/* + * If there's potential overlap between enabled ECs and Declaratives, generate + * a PERFORM of the _DECLARATIVES_EVAL "ladder" that matches a section number + * to its name, and executes the Declarative. + */ +static void +statement_epilog( int token ) { + cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); + if( possible_ec() && token != CONTINUE ) { if( enabled_exceptions.size() ) { - current.declaratives_evaluate(ec_none_e); - cbl_enabled_exceptions_array_t enabled(enabled_exceptions); - parser_exception_prepare( keyword_str(token), &enabled ); + current.declaratives_evaluate(); } } - return 0; + parser_check_fatal_exception(); +} + +static inline void +statement_prolog( int token ) { + parser_statement_begin( keyword_str(token), + current.declaratives.runtime.ena, + current.declaratives.runtime.dcl ); +} + +/* + * We check the EC against the Declarative status prior to parsing the + * statement because a TURN directive can be embedded in the statement. An + * embedded directive applies to the following statement, not the one being + * parsed. + */ +static void +statement_begin( const YYLTYPE& loc, int token ) { + static int prior_token = 0; + + if( statement_cleanup ) { + statement_epilog(prior_token); + } else { + statement_cleanup = true; + } + location_set(loc); + statement_prolog(token); + + prior_token = token; } #include "parse_util.h" @@ -11090,7 +12123,7 @@ statement_begin( const YYLTYPE& loc, int token ) { struct string_match { const char *name; - string_match( const char name[] ) : name(name) {} + explicit string_match( const char name[] ) : name(name) {} bool operator()( const char input[] ) const { return strlen(name) == strlen(input) && 0 == strcasecmp(name, input); } @@ -11098,18 +12131,24 @@ struct string_match { const char * keyword_str( int token ) { - if( token == YYEOF ) return "YYEOF"; - if( token == YYEMPTY ) return "YYEMPTY"; - + switch( token ) { + case YYEOF: return "YYEOF"; + case YYEMPTY: return "YYEMPTY"; + case 256: return "YYerror"; + case 257: return "invalid token"; // YYUNDEF + } + if( token < 256 ) { static char ascii[2]; ascii[0] = token; return ascii; } - return tokens.name_of(token); + return cdf_tokens.name_of(token); } +bool iso_cobol_word( const std::string& name, bool include_context ); + /* * Return the token for the Cobol name, unless it is a function name. The * lexer uses keyword_tok to determine if what appears to be a NAME is in fact @@ -11119,14 +12158,9 @@ keyword_str( int token ) { * REPOSITORY names. */ -// tokens.h is generated as needed from parse.h with tokens.h.gen -tokenset_t::tokenset_t() { -#include "token_names.h" -} - // Look up the lowercase form of a keyword, excluding some CDF names. int -tokenset_t::find( const cbl_name_t name, bool include_intrinsics ) { +current_tokens_t::tokenset_t::find( const cbl_name_t name, bool include_intrinsics ) { static const cbl_name_t non_names[] = { // including CDF NAMES, and "SWITCH" "CHECKING", "LIST", "LOCATION", "MAP", "SWITCH", }, * const eonames = non_names + COUNT_OF(non_names); @@ -11142,9 +12176,9 @@ tokenset_t::find( const cbl_name_t name, bool include_intrinsics ) { if( dialect_ibm() ) { static const cbl_name_t ibm_non_names[] = { "RESUME", - }, * const eonames = ibm_non_names + COUNT_OF(ibm_non_names); + }, * const eoibm = ibm_non_names + COUNT_OF(ibm_non_names); - if( std::any_of(ibm_non_names, eonames, + if( std::any_of(ibm_non_names, eoibm, [candidate=name](const cbl_name_t non_name) { return 0 == strcasecmp(non_name, candidate) && strlen(non_name) == strlen(candidate); @@ -11153,27 +12187,35 @@ tokenset_t::find( const cbl_name_t name, bool include_intrinsics ) { } } + /* + * The input name may be one of: + * 1. an intrinsic function name (OK if include_intrinsics) + * 2. an ISO/GCC reserved word or context-sensitive word (OK) + * 3. a token in our token list for convenience, such as BINARY_INTEGER (bzzt) + */ + cbl_name_t lname; - std::transform(name, name + strlen(name) + 1, lname, tolower); + std::transform(name, name + strlen(name) + 1, lname, ftolower); auto p = tokens.find(lname); if( p == tokens.end() ) return 0; int token = p->second; if( token == SECTION ) yylval.number = 0; - if( include_intrinsics ) return token; - - return intrinsic_cname(token)? 0 : token; + if( include_intrinsics && intrinsic_cname(token) ) return token; + if( iso_cobol_word(uppercase(name), true) ) return token; + + return 0; } int keyword_tok( const char * text, bool include_intrinsics ) { - return tokens.find(text, include_intrinsics); + return cdf_tokens.find(text, include_intrinsics); } static inline size_t verify_figconst( enum cbl_figconst_t figconst , size_t pos ) { - cbl_field_t *f = cbl_field_of(symbol_at(pos)); + const cbl_field_t *f = cbl_field_of(symbol_at(pos)); assert((f->attr & FIGCONST_MASK) == figconst); return pos; } @@ -11198,7 +12240,7 @@ relop_of(int token) { switch(token) { case '<': return lt_op; case LE: return le_op; - case '=': return eq_op; + case EQ: return eq_op; case NE: return ne_op; case GE: return ge_op; case '>': return gt_op; @@ -11219,7 +12261,7 @@ relop_invert(relop_t op) { case ge_op: return lt_op; case gt_op: return le_op; } - cbl_errx( "%s:%d: invalid relop_t %d", __func__, __LINE__, op); + cbl_internal_error("%s:%d: invalid %<relop_t%> %d", __func__, __LINE__, op); return relop_t(0); // not reached } @@ -11231,7 +12273,7 @@ relop_debug_str(int token) { case 0: return "zilch"; case '<': return "<"; case LE: return "LE"; - case '=': return "="; + case EQ: return "="; case NE: return "NE"; case GE: return "GE"; case '>': return ">"; @@ -11245,7 +12287,7 @@ token_of(enum relop_t op) { switch(op) { case lt_op: return '<'; case le_op: return LE; - case eq_op: return '='; + case eq_op: return EQ; case ne_op: return NE; case ge_op: return GE; case gt_op: return '>'; @@ -11360,7 +12402,7 @@ label_add( const YYLTYPE& loc, name, cbl_label_of(p)->name, cbl_label_of(p)->line); } } - struct cbl_label_t label = { type, parent, loc.last_line }; + struct cbl_label_t label = { type, parent, loc.first_line }; if( !namcpy(loc, label.name, name) ) return NULL; auto p = symbol_label_add(PROGRAM, &label); @@ -11371,11 +12413,6 @@ label_add( const YYLTYPE& loc, assert( !(p->type == LblSection && p->parent > 0) ); - if( getenv(__func__) ) { - yywarn("%s: added label %3zu %10s for '%s' of %zu", __func__, - symbol_elem_of(p) - symbols_begin(), p->type_str()+3, p->name, p->parent); - } - return p; } @@ -11396,7 +12433,7 @@ perform_t::ec_labels_t::new_label( cbl_label_type_t type, { size_t n = 1 + symbols_end() - symbols_begin(); cbl_name_t name; - sprintf(name, "_perf_%s_%zu", role, n); + sprintf(name, "_perf_%s_" HOST_SIZE_T_PRINT_UNSIGNED, role, (fmt_size_t)n); return label_add( type, name, yylineno ); } @@ -11436,34 +12473,15 @@ paragraph_reference( const char name[], size_t section ) strcpy(label.name, name); if( label.type == LblNone ) assert(label.parent == 0); - const symbol_elem_t *last = symbols_end(); - p = symbol_label_add(PROGRAM, &label); assert(p); const char *sect_name = section? cbl_label_of(symbol_at(section))->name : NULL; procedure_reference_add(sect_name, p->name, yylineno, current.program_section()); - if( getenv(__func__) ) { - yywarn("%s: %s label %3zu %10s for '%s' of %zu", __func__, - symbols_end() == last? "added" : "found", - symbol_index(symbol_elem_of(p)), p->type_str()+3, p->name, p->parent); - } - return p; } -static struct cbl_refer_t * -use_vargs( struct vargs_t *v, struct cbl_refer_t *tgt) { - assert(v); - assert(tgt); - std::copy(v->args.begin(), v->args.end(), tgt); - v->args.clear(); - delete v; - - return tgt; -} - void current_t::repository_add_all() { assert( !programs.empty() ); @@ -11489,7 +12507,7 @@ current_t::udf_update( const ffi_args_t *ffi_args ) { if( ! ffi_args ) return; assert(ffi_args->elems.size() < sizeof(function_descr_t::types)); - auto returning = cbl_field_of(symbol_at(L->returning)); + const auto returning = cbl_field_of(symbol_at(L->returning)); auto key = function_descr_t::init(L->name); auto func = udfs.find(key); assert(func != udfs.end()); @@ -11531,12 +12549,15 @@ current_t::udf_args_valid( const cbl_label_t *L, } size_t i = 0; - for( cbl_refer_t arg : args ) { + for( const cbl_refer_t& arg : args ) { if( arg.field ) { // else omitted auto tgt = cbl_field_of(symbol_at(udf.linkage_fields.at(i).isym)); if( ! valid_move(tgt, arg.field) ) { - auto loc = symbol_field_location(field_index(arg.field)); - error_msg(loc, "FUNCTION %s arg %zu, '%s' cannot be passed to %s, type %s", + auto loc = current_location; + if( ! is_temporary(arg.field) ) { + loc = symbol_field_location(field_index(arg.field)); + } + error_msg(loc, "FUNCTION %s argument %zu, '%s' cannot be passed to %s, type %s", L->name, i, arg.field->pretty_name(), tgt->pretty_name(), 3 + cbl_field_type_str(tgt->type) ); return false; @@ -11552,7 +12573,10 @@ current_t::repository_add( const char name[]) { assert( !programs.empty() ); function_descr_t arg = function_descr_t::init(name); auto parg = std::find( function_descrs, function_descrs_end, arg ); - if( parg == function_descrs_end ) return false; + if( parg == function_descrs_end ) { + dbgmsg("%s:%d: no intrinsic %s found", __func__, __LINE__, name); + return false; + } auto p = programs.top().function_repository.insert(*parg); if( yydebug ) { for( auto descr : programs.top().function_repository ) { @@ -11588,7 +12612,7 @@ function_descr_t function_descr_t::init( int isym ) { function_descr_t descr = { FUNCTION_UDF_0 }; descr.ret_type = FldInvalid; - auto L = cbl_label_of(symbol_at(isym)); + const auto L = cbl_label_of(symbol_at(isym)); bool ok = namcpy(YYLTYPE(), descr.name, L->name); gcc_assert(ok); return descr; @@ -11602,16 +12626,16 @@ arith_t::arith_t( cbl_arith_format_t format, refer_list_t * refers ) delete refers; } - -cbl_key_t::cbl_key_t( const sort_key_t& that ) +cbl_key_t::cbl_key_t( sort_key_t that ) : ascending(that.ascending) - , nfield(that.fields.size()) - , fields(NULL) -{ - if( nfield > 0 ) { - fields = new cbl_field_t* [nfield]; - std::copy(that.fields.begin(), that.fields.end(), fields); - } + , fields( that.fields.begin(), that.fields.end() ) +{} + +cbl_key_t& +cbl_key_t::operator=( const sort_key_t& that ) { + ascending = that.ascending; + fields = that.as_vector(); + return *this; } static cbl_refer_t * @@ -11654,14 +12678,9 @@ ast_add( arith_t *arith ) { pC = use_any(arith->tgts, C); pA = use_any(arith->A, A); - if( getenv(__func__) ) { - dbgmsg("%s:%d: %-12s C{%zu %p} A{%zu %p}", __func__, __LINE__, - arith->format_str(), nC, pC, nA, pA ); - } parser_add( nC, pC, nA, pA, arith->format, arith->on_error, arith->not_error ); - ec_type_t handled = arith->on_error || arith->not_error ? ec_size_e : ec_none_e; - current.declaratives_evaluate(handled); + current.declaratives_evaluate(); } static bool @@ -11677,8 +12696,7 @@ ast_subtract( arith_t *arith ) { parser_subtract( nC, pC, nA, pA, nB, pB, arith->format, arith->on_error, arith->not_error ); - ec_type_t handled = arith->on_error || arith->not_error ? ec_size_e : ec_none_e; - current.declaratives_evaluate(handled); + current.declaratives_evaluate(); return true; } @@ -11695,8 +12713,7 @@ ast_multiply( arith_t *arith ) { parser_multiply( nC, pC, nA, pA, nB, pB, arith->on_error, arith->not_error ); - ec_type_t handled = arith->on_error || arith->not_error ? ec_size_e : ec_none_e; - current.declaratives_evaluate(handled); + current.declaratives_evaluate(); return true; } @@ -11714,8 +12731,7 @@ ast_divide( arith_t *arith ) { parser_divide( nC, pC, nA, pA, nB, pB, arith->remainder, arith->on_error, arith->not_error ); - ec_type_t handled = arith->on_error || arith->not_error ? ec_size_e : ec_none_e; - current.declaratives_evaluate(handled); + current.declaratives_evaluate(); return true; } @@ -11725,48 +12741,45 @@ ast_divide( arith_t *arith ) { * the convenience of the parser. */ struct stringify_src_t : public cbl_string_src_t { - stringify_src_t( const refer_marked_list_t& marked = refer_marked_list_t() ) - : cbl_string_src_t( marked.marker? *marked.marker : null_reference, - marked.refers.size(), - new cbl_refer_t[marked.refers.size()] ) + stringify_src_t( const refer_marked_list_t& marked = refer_marked_list_t() ) + : cbl_string_src_t( marked.marker? *marked.marker : null_reference, + marked.refers.size(), + new cbl_refer_t[marked.refers.size()] ) { std::copy( marked.refers.begin(), marked.refers.end(), inputs ); } static void dump( const cbl_string_src_t& src ) { - dbgmsg( "%s:%d:, %zu inputs delimited by %s:", __func__, __LINE__, - src.ninput, + dbgmsg( "%s:%d:, " HOST_SIZE_T_PRINT_UNSIGNED " inputs delimited by %s:", + __func__, __LINE__, (fmt_size_t)src.ninput, src.delimited_by.field? field_str(src.delimited_by.field) : "SIZE" ); std::for_each(src.inputs, src.inputs + src.ninput, dump_input); } protected: static void dump_input( const cbl_refer_t& refer ) { - yywarn( "%s:\t%s", __func__, field_str(refer.field) ); + dbgmsg( "%s: %s", __func__, field_str(refer.field) ); } }; void stringify( refer_collection_t *inputs, - cbl_refer_t into, cbl_refer_t pointer, + const cbl_refer_t& into, const cbl_refer_t& pointer, cbl_label_t *on_error, cbl_label_t *not_error ) { - std::vector <stringify_src_t> sources(inputs->lists.size()); - if( inputs->lists.back().marker == NULL ) { inputs->lists.back().marker = cbl_refer_t::empty(); } assert( inputs->lists.back().marker ); - std::copy( inputs->lists.begin(), inputs->lists.end(), sources.begin() ); - if( yydebug && getenv(__func__) ) { - std::for_each(sources.begin(), sources.end(), stringify_src_t::dump); - } + + std::vector <stringify_src_t> sources(inputs->lists.begin(), inputs->lists.end()); + parser_string( into, pointer, sources.size(), sources.data(), on_error, not_error ); } void -unstringify( cbl_refer_t& src, +unstringify( const cbl_refer_t& src, refer_list_t *delimited, unstring_into_t * into, cbl_label_t *on_error, @@ -11774,6 +12787,7 @@ unstringify( cbl_refer_t& src, { size_t ndelimited = delimited? delimited->size() : 0; cbl_refer_t *pdelimited = NULL; + // cppcheck-suppress [variableScope] pdelimited points to delimiteds.data() std::vector <cbl_refer_t> delimiteds(ndelimited); if( ndelimited > 0 ) { pdelimited = use_any( delimited->refers, delimiteds ); @@ -11885,15 +12899,19 @@ lang_check_failed (const char* file, int line, const char* function) {} #pragma GCC diagnostic pop -void ast_inspect( cbl_refer_t& input, bool backward, ast_inspect_list_t& inspects ) { +void +ast_inspect( YYLTYPE loc, cbl_refer_t& input, bool backward, + cbl_inspect_opers_t& inspects ) +{ if( yydebug ) { - dbgmsg("%s:%d: INSPECT %zu operations on %s, line %d", __func__, __LINE__, - inspects.size(), input.field->name, yylineno); + dbgmsg("%s:%d: INSPECT " HOST_SIZE_T_PRINT_UNSIGNED " operations on %s, " + "lines %d:%d - %d:%d", + __func__, __LINE__, + (fmt_size_t)inspects.size(), input.field->name, + loc.first_line, loc.first_column, loc.last_line, loc.last_column ); } std::for_each(inspects.begin(), inspects.end(), dump_inspect); - auto array = inspects.as_array(); - parser_inspect( input, backward, inspects.size(), array ); - delete[] array; + parser_inspect( input, backward, inspects ); } static const char * @@ -11905,28 +12923,29 @@ cbl_refer_str( char output[], const cbl_refer_t& R ) { return output; } -static void +void dump_inspect_match( const cbl_inspect_match_t& M ) { - static char fields[3][4 * 64]; - cbl_refer_str(fields[0], M.matching); - cbl_refer_str(fields[1], M.before.identifier_4); - cbl_refer_str(fields[2], M.after.identifier_4); - - yywarn( "matching %s \n\t\tbefore %s%s \n\t\tafter %s%s", - fields[0], - M.before.initial? "initial " : "", fields[1], - M.after.initial? "initial " : "", fields[2] ); + static char fields[4][4 * 64]; + cbl_refer_str(fields[0], M.match); + cbl_refer_str(fields[1], M.tally); + cbl_refer_str(fields[2], M.before.identifier_4); + cbl_refer_str(fields[3], M.after.identifier_4); + + dbgmsg( "matching %s [tally %s]\n\t\tbefore %s%s \n\t\tafter %s%s", + fields[0], fields[1], + M.before.initial? "initial " : "", fields[2], + M.after.initial? "initial " : "", fields[3] ); } static void dump_inspect_replace( const cbl_inspect_replace_t& R ) { static char fields[4][4 * 64]; - cbl_refer_str(fields[0], R.matching); + cbl_refer_str(fields[0], R.matching()); cbl_refer_str(fields[1], R.before.identifier_4); cbl_refer_str(fields[2], R.after.identifier_4); cbl_refer_str(fields[3], R.replacement); - yywarn( "matching %s \n\treplacement %s\n\t\tbefore %s%s \n\t\tafter %s%s", + dbgmsg( "matching %s \n\treplacement %s\n\t\tbefore %s%s \n\t\tafter %s%s", fields[0], fields[3], R.before.initial? "initial " : "", fields[1], R.after.initial? "initial " : "", fields[2] ); @@ -11985,7 +13004,6 @@ data_category_of( const cbl_refer_t& refer ) { case FldIndex: case FldSwitch: case FldDisplay: - case FldBlob: return data_category_none; } gcc_unreachable(); @@ -12018,54 +13036,54 @@ valid_target( const cbl_refer_t& refer ) { case FldIndex: case FldSwitch: case FldDisplay: - case FldBlob: return false; } gcc_unreachable(); return false; } -static _Float128 +static REAL_VALUE_TYPE numstr2i( const char input[], radix_t radix ) { - _Float128 output = 0.0; - size_t bit, integer = 0; - int erc=0, n=0; + REAL_VALUE_TYPE output; + size_t integer = 0; + fmt_size_t integerf = 0; + int erc=0; switch( radix ) { case decimal_e: { // Use decimal point for comma, just in case. - auto local = xstrdup(input), pend = local; + auto local = xstrdup(input); if( !local ) { erc = -1; break; } std::replace(local, local + strlen(local), ',', '.'); - output = strtof128(local, &pend); - n = pend - local; + real_from_string3 (&output, local, TYPE_MODE (float128_type_node)); } break; case hexadecimal_e: - erc = sscanf(input, "%zx%n", &integer, &n); - output = integer; + erc = sscanf(input, "%" GCC_PRISZ "x", &integerf); + integer = integerf; + real_from_integer (&output, VOIDmode, integer, UNSIGNED); break; case boolean_e: for( const char *p = input; *p != '\0'; p++ ) { if( ssize_t(8 * sizeof(integer) - 1) < p - input ) { - yywarn("'%s' was accepted as %d", input, integer); - return integer; + dbgmsg("'%s' was accepted as %lu", input, (unsigned long)integer); + break; } switch(*p) { - case '0': bit = 0; break; - case '1': bit = 1; break; + case '0': + case '1': + integer = (integer << (p - input)); + integer |= ((*p) == '0' ? 0 : 1); break; default: - yywarn("'%s' was accepted as %d", input, integer); - return integer; + dbgmsg("'%s' was accepted as %lu", input, (unsigned long)integer); + break; } - integer = (integer << (p - input)); - integer |= bit; } - return integer; - break; + real_from_integer (&output, VOIDmode, integer, UNSIGNED); + return output; } - if( erc == -1 || n < int(strlen(input)) ) { - yywarn("'%s' was accepted as %lld", input, output); + if( erc == -1 ) { + cbl_message(ParNumstrW, "'%s' was accepted as %zu", input, integer); } return output; } @@ -12091,7 +13109,7 @@ new_literal( const char initial[], enum radix_t radix ) { class is_elementary_type { // for INITIALIZE purposes bool with_filler; public: - is_elementary_type( bool with_filler ) : with_filler(with_filler) {} + explicit is_elementary_type( bool with_filler ) : with_filler(with_filler) {} bool operator()( const symbol_elem_t& elem ) const { if( elem.type != SymField ) return false; @@ -12105,7 +13123,7 @@ public: size_t end_of_group( size_t igroup ); static std::list<cbl_refer_t> -symbol_group_data_members( cbl_refer_t refer, bool with_filler ) { +symbol_group_data_members( const cbl_refer_t& refer, bool with_filler ) { std::list<cbl_refer_t> refers; refers.push_front( refer ); @@ -12113,7 +13131,7 @@ symbol_group_data_members( cbl_refer_t refer, bool with_filler ) { class refer_of : public cbl_refer_t { public: - refer_of( const cbl_refer_t& refer ) : cbl_refer_t(refer) {} + explicit refer_of( const cbl_refer_t& refer ) : cbl_refer_t(refer) {} cbl_refer_t operator()( symbol_elem_t& elem ) { this->field = cbl_field_of(&elem); // preserve subscript/refmod return *this; @@ -12137,7 +13155,7 @@ struct expand_group : public std::list<cbl_refer_t> { return cbl_refer_t(field); } bool with_filler; - expand_group( bool with_filler ) : with_filler(with_filler) {} + explicit expand_group( bool with_filler ) : with_filler(with_filler) {} void operator()( const cbl_refer_t& refer ) { assert(refer.field); @@ -12152,18 +13170,18 @@ struct expand_group : public std::list<cbl_refer_t> { }; -static const char * initial_default_value; - const char * wsclear() { return initial_default_value; } +static const uint32_t * initial_default_value; + const uint32_t * wsclear() { return initial_default_value; } void -wsclear( char ch ) { - static char byte = ch; - initial_default_value = &byte; +wsclear( uint32_t i ) { + static uint32_t init_val = i; + initial_default_value = &init_val; current.program_needs_initial(); } static void -initialize_allocated( cbl_refer_t input ) { +initialize_allocated( const cbl_refer_t& input ) { cbl_num_result_t result = { truncation_e, input }; std::list<cbl_num_result_t> results; results.push_back(result); @@ -12172,13 +13190,14 @@ initialize_allocated( cbl_refer_t input ) { } static int -initialize_with( cbl_refer_t tgt ) { +initialize_with( const cbl_refer_t& tgt ) { if( tgt.field->type == FldPointer ) return ZERO; if( tgt.is_refmod_reference() ) return SPACES; return is_numeric(tgt.field)? ZERO : SPACES; } static bool +// cppcheck-suppress [passedByValue] target.refer.field is modified initialize_one( cbl_num_result_t target, bool with_filler, data_category_t value_category, const category_map_t& replacements, @@ -12207,9 +13226,6 @@ initialize_one( cbl_num_result_t target, bool with_filler, } else { parser_move(tgt, src, current_rounded_mode()); } - if( getenv(__func__) ) { - yywarn("%s:%-5s: %s", __func__, keyword_str(token), field_str(tgt.field)); - } return true; } @@ -12226,10 +13242,6 @@ initialize_one( cbl_num_result_t target, bool with_filler, parser_initialize(tgt); } } - - if( getenv(__func__) ) { - yywarn("%s: value: %s", __func__, field_str(tgt.field)); - } } // apply REPLACING, possibly overwriting VALUE @@ -12242,87 +13254,24 @@ initialize_one( cbl_num_result_t target, bool with_filler, if( r != replacements.end() ) { parser_move( tgt, *r->second ); - if( getenv(__func__) ) { - cbl_field_t *from = r->second->field; - char from_str[128]; // copy static buffer from field_str - strcpy( from_str, field_str(from) ); - yywarn("%s: move: %-18s %s \n\t from %-18s %s", __func__, - cbl_field_type_str(tgt.field->type) + 3, field_str(tgt.field), - cbl_field_type_str(from->type) + 3, from_str); - } return true; } return true; - } typedef std::pair<cbl_field_t*,cbl_field_t*> field_span_t; typedef std::pair<size_t, size_t> cbl_bytespan_t; -static void -dump_spans( size_t isym, - const cbl_field_t *table, - const std::list<field_span_t>& spans, - size_t nrange, - const cbl_bytespan_t ranges[], - size_t depth, - const std::list<cbl_subtable_t>& subtables ) -{ - int i=0; - assert( nrange == 0 || nrange == spans.size() ); - - if( isym != field_index(table) ) { - dbgmsg("%s:%d: isym %zu is not #%zu %02u %s", __func__, __LINE__, - isym, field_index(table), table->level, table->name); - } - dbgmsg( "%s: [%zu] #%zu %s has %zu spans and %zu subtables", - __func__, depth, isym, table->name, nrange, subtables.size() ); - for( auto span : spans ) { - unsigned int last_level = 0; - const char *last_name = "<none>"; - if( span.second ) { - last_level = span.second->level; - last_name = span.second->name; - } - - char at_subtable[64] = {}; - size_t offset = nrange? ranges[i].first : 0; - auto p = std::find_if(subtables.begin(), subtables.end(), - [offset]( const cbl_subtable_t& tbl ) { - return tbl.offset == offset; - }); - if( p != subtables.end() ) { - sprintf(at_subtable, "(subtable #%zu)", p->isym); - } - dbgmsg("\t %02u %-20s to %02u %-20s: %3zu-%zu %s", - span.first->level, span.first->name, - last_level, last_name, - nrange? ranges[i].first : 1, - nrange? ranges[i].second : 0, - at_subtable); - i++; - } - if( ! subtables.empty() ) { - dbgmsg("\ttable #%zu has %zu subtables", isym, subtables.size()); - for( auto tbl : subtables ) { - dbgmsg("\t #%zu @ %4zu", tbl.isym, tbl.offset); - } - } -} - /* * After the 1st record is initialized, copy it to the others. */ static bool -initialize_table( cbl_num_result_t target, +initialize_table( const cbl_num_result_t& target, size_t nspan, const cbl_bytespan_t spans[], const std::list<cbl_subtable_t>& subtables ) { - if( getenv("initialize_statement") ) { - dbgmsg("%s:%d: %s ", __func__, __LINE__, target.refer.str()); - } - assert( target.refer.nsubscript == dimensions(target.refer.field) ); + assert( target.refer.nsubscript() == dimensions(target.refer.field) ); const cbl_refer_t& src( target.refer ); size_t n( src.field->occurs.ntimes()); assert( 0 < n ); @@ -12338,17 +13287,17 @@ static cbl_refer_t synthesize_table_refer( cbl_refer_t tgt ) { // For a table, use supplied subscripts or start with 1. auto ndim( dimensions(tgt.field) ); - if( tgt.nsubscript < ndim ) { // it's an incomplete table + if( tgt.nsubscript() < ndim ) { // it's an incomplete table std::vector <cbl_refer_t> subscripts(ndim); for( size_t i=0; i < ndim; i++ ) { - if( i < tgt.nsubscript ) { + if( i < tgt.nsubscript() ) { subscripts[i] = tgt.subscripts[i]; continue; } subscripts[i].field = new_tempnumeric(); parser_set_numeric(subscripts[i].field, 1); } - return cbl_refer_t( tgt.field, subscripts.size(), subscripts.data() ); + return cbl_refer_t( tgt.field, subscripts ); } return tgt; } @@ -12358,7 +13307,7 @@ group_offset( const cbl_field_t *field ) { if( field->parent ) { auto e = symbol_at(field->parent); if( e->type == SymField ) { - auto parent = cbl_field_of(e); + const auto parent = cbl_field_of(e); return field->offset - parent->offset; } } @@ -12371,14 +13320,8 @@ initialize_statement( const cbl_num_result_t& target, bool with_filler, const category_map_t& replacements, size_t depth = 0 ) { - if( getenv(__func__) ) { - dbgmsg("%s:%d: %2zu: %s (%s%zuR)", - __func__, __LINE__, depth, target.refer.str(), - with_filler? "F" : "", - replacements.size()); - } const cbl_refer_t& tgt( target.refer ); - assert(dimensions(tgt.field) == tgt.nsubscript || 0 < depth); + assert(dimensions(tgt.field) == tgt.nsubscript() || 0 < depth); assert(!is_literal(tgt.field)); if( tgt.field->type == FldGroup ) { @@ -12415,7 +13358,7 @@ initialize_statement( const cbl_num_result_t& target, bool with_filler, if( fOK && is_table(tgt.field) ) { cbl_num_result_t output = { target.rounded, synthesize_table_refer(tgt) }; - if( tgt.nsubscript < output.refer.nsubscript ) { // tgt is whole table + if( tgt.nsubscript() < output.refer.nsubscript() ) { // tgt is whole table std::list<field_span_t> field_spans; static const field_span_t empty_span = { NULL, NULL }; field_span_t span = empty_span; @@ -12460,10 +13403,6 @@ initialize_statement( const cbl_num_result_t& target, bool with_filler, return std::make_pair(first, second); } ); } - if( getenv("initialize_statement") ) { - dump_spans( field_index(output.refer.field), output.refer.field, - field_spans, ranges.size(), ranges.data(), depth, subtables ); - } return initialize_table( output, nrange, ranges.data(), subtables ); } } @@ -12530,29 +13469,7 @@ static void initialize_statement( std::list<cbl_num_result_t>& tgts, bool with_filler, data_category_t value_category, const category_map_t& replacements) { - if( yydebug && getenv(__func__) ) { - yywarn( "%s: %zu targets, %s filler", - __func__, tgts.size(), with_filler? "with" : "no"); - for( auto tgt : tgts ) { - fprintf( stderr, "%28s: %s\n", __func__, name_of(tgt.refer.field) ); - } - for( const auto& elem : replacements ) { - fprintf( stderr, "%28s: %s <-%s\n", __func__, - data_category_str(elem.first), - name_of(elem.second->field) ); - } - } - - bool is_refmod = std::any_of( tgts.begin(), tgts.end(), - []( const auto& tgt ) { - return tgt.refer.is_refmod_reference(); - } ); - if( false && is_refmod ) { // refmod seems valid per ISO - dbgmsg("INITIALIZE cannot initialize a refmod"); - return; - } - - for( auto tgt : tgts ) { + for( const auto& tgt : tgts ) { initialize_statement( tgt, with_filler, value_category, replacements ); } @@ -12561,13 +13478,13 @@ initialize_statement( std::list<cbl_num_result_t>& tgts, bool with_filler, static void dump_inspect_oper( const cbl_inspect_oper_t& op ) { - dbgmsg("\t%s: %zu \"matches\", %zu \"replaces\"", - bound_str(op.bound), - op.matches? op.n_identifier_3 : 0, op.replaces? op.n_identifier_3 : 0); - if( op.matches ) - std::for_each(op.matches, op.matches + op.n_identifier_3, dump_inspect_match); - if( op.replaces ) - std::for_each(op.replaces, op.replaces + op.n_identifier_3, dump_inspect_replace); + dbgmsg("\t%s: " HOST_SIZE_T_PRINT_UNSIGNED + " \"matches\", " HOST_SIZE_T_PRINT_UNSIGNED " \"replaces\"", + bound_str(op.bound), + (fmt_size_t)op.matches.size(), + (fmt_size_t)op.replaces.size()); + std::for_each(op.matches.begin(), op.matches.end(), dump_inspect_match); + std::for_each(op.replaces.begin(), op.replaces.end(), dump_inspect_replace); } #pragma GCC diagnostic push @@ -12584,14 +13501,14 @@ dump_inspect( const cbl_inspect_t& I ) { } else { fprintf( stderr, "\tREPLACING:\n" ); } - std::for_each( I.opers, I.opers + I.nbound, dump_inspect_oper ); + std::for_each( I.begin(), I.end(), dump_inspect_oper ); } #pragma GCC diagnostic pop #include <iterator> struct declarative_file_list_t : protected cbl_declarative_t { - declarative_file_list_t( const cbl_declarative_t& d ) + explicit declarative_file_list_t( const cbl_declarative_t& d ) : cbl_declarative_t(d) { if( nfile > 0 ) @@ -12616,7 +13533,7 @@ operator<<( std::ostream& os, const declarative_file_list_t& dcl ) { static declarative_file_list_t file_list_of( const cbl_declarative_t& dcl ) { - return dcl; + return declarative_file_list_t(dcl); } std::ostream& @@ -12651,10 +13568,11 @@ cbl_field_t * new_literal( const literal_t& lit, enum cbl_field_attr_t attr ) { bool zstring = lit.prefix[0] == 'Z'; if( !zstring && lit.data[lit.len] != '\0' ) { - dbgmsg("%s:%d: line %d, no NUL terminator '%-*.*s'{%zu/%zu}", + dbgmsg("%s:%d: line %d, no NUL terminator '%-*.*s'{" + HOST_SIZE_T_PRINT_UNSIGNED "/" HOST_SIZE_T_PRINT_UNSIGNED "}", __func__, __LINE__, yylineno, int(lit.len), int(lit.len), - lit.data, strlen(lit.data), lit.len); + lit.data, (fmt_size_t)strlen(lit.data), (fmt_size_t)lit.len); } assert(zstring || lit.data[lit.len] == '\0'); @@ -12662,7 +13580,7 @@ new_literal( const literal_t& lit, enum cbl_field_attr_t attr ) { attrs |= constant_e; attrs |= literal_attr(lit.prefix); - return new_literal(lit.len, lit.data, cbl_field_attr_t(attrs)); + return new_literal(lit.len, lit.data, cbl_field_attr_t(attrs), lit.encoding); } bool @@ -12687,7 +13605,7 @@ cbl_file_t::validate_key( const cbl_file_key_t& key ) const { bool cbl_file_t::validate() const { - size_t members[] = { user_status, vsam_status, record_length }; + const size_t members[] = { user_status, vsam_status, record_length }; bool tf = true; for( auto isym : members ) { @@ -12734,6 +13652,82 @@ cbl_figconst_of( const char *value ) { return p == eovalues? normal_value_e : p->type; } +int +cbl_figconst_tok( const char *value ) { + struct values_t { + const char *value; int token; + } static const values[] = { + { constant_of(constant_index(ZERO))->data.initial, ZERO }, + { constant_of(constant_index(SPACES))->data.initial, SPACES }, + { constant_of(constant_index(HIGH_VALUES))->data.initial, HIGH_VALUES }, + { constant_of(constant_index(LOW_VALUES))->data.initial, LOW_VALUES }, + { constant_of(constant_index(QUOTES))->data.initial, QUOTES }, + { constant_of(constant_index(NULLS))->data.initial, NULLS }, + }, *eovalues = values + COUNT_OF(values); + + auto p = std::find_if( values, eovalues, + [value]( const values_t& elem ) { + return elem.value == value; + } ); + + return p == eovalues? 0 : p->token; +} + +const cbl_field_t * +cbl_figconst_field_of( const char *value ) { + int token = cbl_figconst_tok(value); + return token == 0 ? nullptr : constant_of(constant_index(token)); +} + +const char * +literal_t::symbol_name() const { + return isym? cbl_field_of(symbol_at(isym))->name : ""; +} + +literal_t& +literal_t::set( const cbl_field_t * field ) { + assert(field->has_attr(constant_e)); + assert(is_literal(field)); + + set_prefix( "", 0 ); + set_data( field->data.capacity, + const_cast<char*>(field->data.initial), + field_index(field) ); + return *this; +} + +literal_t& +literal_t::set_prefix( const char *input, size_t len ) { + encoding = current_encoding(display_encoding_e); + assert(len < sizeof(prefix)); + std::fill(prefix, prefix + sizeof(prefix), '\0'); + std::transform(input, input + len, prefix, toupper); + switch(prefix[0]) { + case '\0': case 'Z': + encoding = current_encoding(display_encoding_e); + break; + case 'N': + encoding = current_encoding(national_encoding_e); + if( 'X' == prefix[1] ) { + cbl_unimplemented("NX literals"); + } + break; + case 'G': + cbl_unimplemented("DBCS encoding not supported"); + break; + case 'U': + encoding = UTF8_e; + break; + case 'X': + break; + default: + gcc_unreachable(); + } + assert(valid_encoding(encoding)); + return *this; +} + + cbl_field_attr_t literal_attr( const char prefix[] ) { switch(strlen(prefix)) { @@ -12742,7 +13736,8 @@ literal_attr( const char prefix[] ) { case 1: switch(prefix[0]) { case 'B': return bool_encoded_e; - case 'N': cbl_unimplemented("National"); return none_e; + case 'N': + case 'U': return none_e; // nothing to say yet case 'X': return hex_encoded_e; case 'Z': return quoted_e; } @@ -12753,14 +13748,15 @@ literal_attr( const char prefix[] ) { case 'X': switch(prefix[0]) { case 'B': return cbl_field_attr_t(hex_encoded_e | bool_encoded_e); - case 'N': cbl_unimplemented("National"); return none_e; + case 'N': cbl_unimplemented("Hexadecimal National"); return none_e; + case 'U': cbl_unimplemented("Hexadecimal Unicode"); return none_e; } break; } } // must be [BN]X - cbl_internal_error("'%s': invalid literal prefix", prefix); + cbl_internal_error("invalid literal prefix: %qs", prefix); gcc_unreachable(); return none_e; } @@ -12773,28 +13769,6 @@ cbl_field_t::has_subordinate( const cbl_field_t *that ) const { return false; } -bool -cbl_field_t::value_set( _Float128 value ) { - data = value; - char *initial = string_of(data.value_of()); - if( !initial ) return false; - - // Trim trailing zeros. - char *p = initial + strlen(initial); - for( --p; initial <= p; --p ) { - if( *p != '0' ) break; - *p = '\0'; - } - - data.digits = (p - initial) + 1; - p = strchr(initial, '.'); - data.rdigits = p? initial + data.digits - p : 0; - - data.initial = initial; - data.capacity = type_capacity(type, data.digits); - return true; -} - const char * cbl_field_t::value_str() const { if( data.etc_type == cbl_field_data_t::value_e ) @@ -12814,15 +13788,28 @@ mode_syntax_only( cbl_division_t division ) { bool mode_syntax_only() { return cbl_syntax_only != not_syntax_only - && cbl_syntax_only <= current_division; + && cbl_syntax_only <= current_division; } void cobol_dialect_set( cbl_dialect_t dialect ) { - cbl_dialect = dialect; - if( dialect & dialect_ibm_e ) cobol_gcobol_feature_set(feature_embiggen_e); + switch(dialect) { + case dialect_iso_e: + case dialect_gcc_e: + break; + case dialect_ibm_e: + cobol_gcobol_feature_set(feature_embiggen_e); + break; + case dialect_mf_e: + break; + case dialect_gnu_e: + if( 0 == (cbl_dialects & dialect) ) { // first time + cdf_tokens.equate(YYLTYPE(), "BINARY-DOUBLE", "BINARY-C-LONG"); + } + break; + } + cbl_dialects |= dialect; } -cbl_dialect_t cobol_dialect() { return cbl_dialect; } static bool internal_ebcdic_locked = false; @@ -12837,6 +13824,8 @@ bool cobol_gcobol_feature_set( cbl_gcobol_feature_t gcobol_feature, bool on ) { if( gcobol_feature == feature_internal_ebcdic_e ) { if( internal_ebcdic_locked ) return false; + if( ! on ) gcc_unreachable(); + current.default_encoding.set(EBCDIC_e); } if( on ) { cbl_gcobol_features |= gcobol_feature; @@ -12855,36 +13844,37 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ) { if( ! is_literal(refmod.from->field) ) { if( ! refmod.len ) return true; if( ! is_literal(refmod.len->field) ) return true; - auto edge = refmod.len->field->data.value_of(); + auto edge = refmod.len->field->as_integer(); if( 0 < edge ) { - if( --edge < r.field->data.capacity ) return true; + if( edge-1 < r.field->data.capacity ) return true; } // len < 0 or not: 0 < from + len <= capacity error_msg(loc, "%s(%s:%zu) out of bounds, " "size is %u", r.field->name, refmod.from->name(), - size_t(refmod.len->field->data.value_of()), + size_t(edge), static_cast<unsigned int>(r.field->data.capacity) ); return false; } - if( refmod.from->field->data.value_of() > 0 ) { - auto edge = refmod.from->field->data.value_of(); + auto edge = refmod.from->field->as_integer(); + if( edge > 0 ) { if( --edge < r.field->data.capacity ) { if( ! refmod.len ) return true; if( ! is_literal(refmod.len->field) ) return true; - if( refmod.len->field->data.value_of() > 0 ) { - edge += refmod.len->field->data.value_of(); + auto len = refmod.len->field->as_integer(); + if( len > 0 ) { + edge += len; if( --edge < r.field->data.capacity ) return true; } // len < 0 or not: 0 < from + len <= capacity - auto loc = symbol_field_location(field_index(r.field)); + loc = symbol_field_location(field_index(r.field)); error_msg(loc, "%s(%zu:%zu) out of bounds, " "size is %u", r.field->name, - size_t(refmod.from->field->data.value_of()), - size_t(refmod.len->field->data.value_of()), + size_t(refmod.from->field->as_integer()), + size_t(len), static_cast<unsigned int>(r.field->data.capacity) ); return false; } @@ -12892,7 +13882,7 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ) { // not: 0 < from <= capacity error_msg(loc,"%s(%zu) out of bounds, size is %u", r.field->name, - size_t(refmod.from->field->data.value_of()), + size_t(refmod.from->field->as_integer()), static_cast<unsigned int>(r.field->data.capacity) ); return false; } @@ -12902,30 +13892,34 @@ literal_subscript_oob( const cbl_refer_t& r, size_t& isub ); static bool literal_subscripts_valid( YYLTYPE loc, const cbl_refer_t& name ) { - static char subs[ 7 * 32 ], *esub = subs + sizeof(subs); - char *p = subs; size_t isub; - // Find subscript in the supplied refer + // Report any out-of-bound subscript. const cbl_field_t *oob = literal_subscript_oob(name, isub); if( oob ) { - const char *sep = ""; - for( auto r = name.subscripts; r < name.subscripts + name.nsubscript; r++ ) { - snprintf( p, esub - p, "%s%s", sep, nice_name_of(r->field) ); - sep = " "; - } - + std::string sep(""); + std::string subscript_names = + std::accumulate( name.subscripts.begin(), + name.subscripts.end(), + std::string(), + [&sep]( std::string acc, const auto& sub ) { + acc += sep; + sep = " "; + return acc + nice_name_of(sub.field); + } ); + const char *upper_phrase = ""; if( ! oob->occurs.bounds.fixed_size() ) { static char ub[32] = "boo"; - sprintf(ub, " to %lu", oob->occurs.bounds.upper); + sprintf(ub, " to " HOST_SIZE_T_PRINT_UNSIGNED, + (fmt_size_t)oob->occurs.bounds.upper); upper_phrase = ub; } // X(0): subscript 1 of for out of range for 02 X OCCURS 4 to 6 error_msg(loc, "%s(%s): subscript %zu out of range " - "for %s %s OCCURS %lu%s", - oob->name, subs, 1 + isub, + "for %s %s OCCURS %zu%s", + oob->name, subscript_names.c_str(), 1 + isub, oob->level_str(), oob->name, oob->occurs.bounds.lower, upper_phrase ); return false; @@ -12947,14 +13941,14 @@ subscript_dimension_error( YYLTYPE loc, size_t nsub, const cbl_refer_t *scalar ) } static void -reject_refmod( YYLTYPE loc, cbl_refer_t scalar ) { +reject_refmod( YYLTYPE loc, const cbl_refer_t& scalar ) { if( scalar.is_refmod_reference() ) { error_msg(loc, "%s cannot be reference-modified here", scalar.name()); } } static bool -require_pointer( YYLTYPE loc, cbl_refer_t scalar ) { +require_pointer( YYLTYPE loc, const cbl_refer_t& scalar ) { if( scalar.field->type != FldPointer ) { error_msg(loc, "%s must have USAGE POINTER", scalar.name()); return false; @@ -12963,7 +13957,7 @@ require_pointer( YYLTYPE loc, cbl_refer_t scalar ) { } static bool -require_numeric( YYLTYPE loc, cbl_refer_t scalar ) { +require_numeric( YYLTYPE loc, const cbl_refer_t& scalar ) { if( ! is_numeric(scalar.field) ) { error_msg(loc, "%s must have numeric USAGE", scalar.name()); return false; @@ -12971,6 +13965,17 @@ require_numeric( YYLTYPE loc, cbl_refer_t scalar ) { return true; } +static bool +require_integer( YYLTYPE loc, const cbl_refer_t& scalar ) { + if( is_literal(scalar.field) ) { + if( ! is_integer_literal(scalar.field) ) { + error_msg(loc, "numeric literal '%s' must be an integer", + scalar.field->pretty_name()); + return false; + } + } + return require_numeric(loc, scalar); +} /* eval methods */ eval_subject_t::eval_subject_t() @@ -12988,7 +13993,8 @@ eval_subject_t::label( const char skel[] ) { cbl_label_t label = protolabel; label.line = yylineno; size_t n = 1 + symbols_end() - symbols_begin(); - snprintf(label.name, sizeof(label.name), "_eval_%s_%zu", skel, n); + snprintf(label.name, sizeof(label.name), + "_eval_%s_" HOST_SIZE_T_PRINT_UNSIGNED, skel, (fmt_size_t)n); auto output = symbol_label_add( PROGRAM, &label ); return output; } @@ -13097,3 +14103,4 @@ eval_subject_t::compare( const cbl_refer_t& object, parser_relop(result, subject, eq_op, object); return result; } + diff --git a/gcc/cobol/parse_ante.h b/gcc/cobol/parse_ante.h index 8ae51c5..54d1f9a 100644 --- a/gcc/cobol/parse_ante.h +++ b/gcc/cobol/parse_ante.h @@ -28,9 +28,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <assert.h> -#include <string.h> -#include <stdio.h> +#include <cassert> +#include <cstring> +#include <cstdio> #include <algorithm> #include <list> @@ -41,14 +41,12 @@ #define MAXLENGTH_FORMATTED_DATE 10 #define MAXLENGTH_FORMATTED_TIME 19 +#define MAXLENGTH_CALENDAR_DATE 21 #define MAXLENGTH_FORMATTED_DATETIME 30 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" -extern void declarative_runtime_match(cbl_field_t *declaratives, - cbl_label_t *lave ); - extern YYLTYPE yylloc; extern int yylineno, yyleng, yychar; @@ -72,42 +70,55 @@ void apply_declaratives(); const char * keyword_str( int token ); void labels_dump(); -cbl_dialect_t cbl_dialect; +unsigned int cbl_dialects; size_t cbl_gcobol_features; +static enum cbl_division_t current_division; static size_t nparse_error = 0; -size_t parse_error_inc() { return ++nparse_error; } +size_t parse_error_inc() { + mode_syntax_only(current_division); + return ++nparse_error; +} size_t parse_error_count() { return nparse_error; } void input_file_status_notify(); -#define YYLLOC_DEFAULT(Current, Rhs, N) \ - do { \ - if (N) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - location_dump("parse.c", N, \ - "rhs N ", YYRHSLOC (Rhs, N)); \ - } \ - else \ - { \ - (Current).first_line = \ - (Current).last_line = YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = \ - (Current).last_column = YYRHSLOC (Rhs, 0).last_column; \ - } \ - location_dump("parse.c", __LINE__, "current", (Current)); \ - gcc_location_set( location_set(Current) ); \ - input_file_status_notify(); \ +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + location_dump("parse.c", N, \ + "rhs N ", YYRHSLOC (Rhs, N)); \ + } \ + else \ + { \ + (Current).first_line = \ + (Current).last_line = YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = \ + (Current).last_column = YYRHSLOC (Rhs, 0).last_column; \ + } \ + location_dump("parse.c", __LINE__, "current", (Current)); \ + input_file_status_notify(); \ + location_set(Current); \ } while (0) int yylex(void); extern int yydebug; -#include <stdarg.h> +#include <cstdarg> + +// These programs in libgcobol/compat are allowed to use ANY LENGTH even though +// they look like top-level programs. +static const std::set<std::string> compat_programs { + "CBL_ALLOC_MEM", + "CBL_CHECK_FILE_EXIST", + "CBL_DELETE_FILE", + "CBL_FREE_MEM", +}; const char * consistent_encoding_check( const YYLTYPE& loc, const char input[] ) { @@ -130,8 +141,6 @@ const char * original_picture(); static const relop_t invalid_relop = static_cast<relop_t>(-1); -static enum cbl_division_t current_division; - static cbl_refer_t null_reference; static cbl_field_t *literally_one, *literally_zero; @@ -141,7 +150,8 @@ literal_of( size_t value ) { case 0: return literally_zero; case 1: return literally_one; } - cbl_err("logic error: %s: %zu not supported", __func__, value); + cbl_err("logic error: %s: " HOST_SIZE_T_PRINT_UNSIGNED " not supported", + __func__, (fmt_size_t)value); return NULL; } @@ -180,20 +190,21 @@ has_clause( int data_clauses, data_clause_t clause ) { } static bool -is_cobol_word( const char name[] ) { +is_cobol_charset( const char name[] ) { auto eoname = name + strlen(name); - auto p = std::find_if( name, eoname, + auto ok = std::all_of( name, eoname, []( char ch ) { switch(ch) { case '-': case '_': - return false; + return true; case '$': // maybe one day (IBM allows) + return false; break; } - return !ISALNUM(ch); + return 0 != ISALNUM(ch); } ); - return p == eoname; + return ok; } bool @@ -207,6 +218,9 @@ in_file_section(void) { return current_data_section == file_datasect_e; } static cbl_refer_t * intrinsic_inconsistent_parameter( size_t n, cbl_refer_t *args ); +static int +intrinsic_token_of( const char name[] ); + static inline bool namcpy(const YYLTYPE& loc, cbl_name_t tgt, const char *src ) { // snprintf(3): writes at most size bytes (including the terminating NUL byte) @@ -220,7 +234,13 @@ namcpy(const YYLTYPE& loc, cbl_name_t tgt, const char *src ) { } cbl_field_t * -new_alphanumeric( size_t capacity ); +new_alphanumeric( size_t capacity = MAXIMUM_ALPHA_LENGTH, + const cbl_name_t name = nullptr ); + +static inline cbl_field_t * +new_alphanumeric( const cbl_name_t name ) { + return new_alphanumeric(MAXIMUM_ALPHA_LENGTH, name); +} static inline cbl_refer_t * new_reference( enum cbl_field_type_t type, const char *initial ) { @@ -235,9 +255,9 @@ new_reference_like( const cbl_field_t& skel ) { return new cbl_refer_t( new_temporary_like(skel) ); } -static void reject_refmod( YYLTYPE loc, cbl_refer_t ); -static bool require_pointer( YYLTYPE loc, cbl_refer_t ); -static bool require_numeric( YYLTYPE loc, cbl_refer_t ); +static void reject_refmod( YYLTYPE loc, const cbl_refer_t& ); +static bool require_pointer( YYLTYPE loc, const cbl_refer_t& ); +static bool require_integer( YYLTYPE loc, const cbl_refer_t& ); struct cbl_field_t * constant_of( size_t isym ); @@ -261,8 +281,11 @@ static inline char * dequote( char input[] ) { static const char * name_of( cbl_field_t *field ) { assert(field); + if( field->data.initial == nullptr ) { + return field->name; + } return field->name[0] == '_' && field->data.initial? - field->data.initial : field->name; + field->data.original() : field->name; } static const char * @@ -280,7 +303,7 @@ struct evaluate_elem_t { relop_t oper; public: cbl_field_t *subject, *object, *cond; - case_t( cbl_field_t * subject ) + explicit case_t( cbl_field_t * subject ) : oper(eq_op) , subject(subject) , object(NULL) @@ -311,21 +334,22 @@ struct evaluate_elem_t { case_iter pcase; void dump() const { - dbgmsg( "nother=%zu label '%s', %zu cases", nother, label.name, cases.size() ); + dbgmsg( "nother=" HOST_SIZE_T_PRINT_UNSIGNED " label '%s', " + HOST_SIZE_T_PRINT_UNSIGNED " cases", + (fmt_size_t)nother, label.name, (fmt_size_t)cases.size() ); std::for_each( cases.begin(), cases.end(), case_t::Dump ); } explicit evaluate_elem_t( const char skel[] ) : nother(0) + , label{LblEvaluate} , result( keep_temporary(FldConditional) ) , pcase( cases.end() ) { - static const cbl_label_t protolabel = { LblEvaluate }; - label = protolabel; label.line = yylineno; if( -1 == snprintf(label.name, sizeof(label.name), "%.*s_%d", (int)sizeof(label.name)-6, skel, yylineno) ) { - yyerror("could not create unique label '%s_%d' because it is too long", + yyerror("could not create unique label %<%s_%d%> because it is too long", skel, yylineno); } } @@ -356,13 +380,14 @@ struct evaluate_elem_t { static class file_delete_args_t { cbl_file_t *file; public: + file_delete_args_t() : file(nullptr) {} void init( cbl_file_t *file ) { this->file = file; } - bool ready() const { return file != NULL; } + bool ready() const { return file != nullptr; } void call_parser_file_delete( bool sequentially ) { parser_file_delete(file, sequentially); - file = NULL; + file = nullptr; } } file_delete_args; @@ -378,7 +403,7 @@ static struct file_read_args_t { void init( struct cbl_file_t *file, - cbl_refer_t record, + const cbl_refer_t& record, cbl_refer_t *read_into, int where ) { this->file = file; @@ -427,7 +452,7 @@ public: this->file = file; } bool ready() const { return file != NULL; } - void call_parser_return_start(cbl_refer_t into = cbl_refer_t() ) { + void call_parser_return_start(const cbl_refer_t& into = cbl_refer_t() ) { parser_return_start(file, into); file = NULL; } @@ -437,17 +462,18 @@ static class file_rewrite_args_t { cbl_file_t *file; cbl_field_t *record; public: + file_rewrite_args_t() : file(nullptr), record(nullptr) {} void init( cbl_file_t *file, cbl_field_t *record ) { this->file = file; this->record = record; } - bool ready() const { return file != NULL; } + bool ready() const { return file != nullptr; } void call_parser_file_rewrite( bool sequentially ) { sequentially = sequentially || file->access == file_access_seq_e; if( file->access == file_access_rnd_e ) sequentially = false; parser_file_rewrite(file, record, sequentially); - file = NULL; - record = NULL; + file = nullptr; + record = nullptr; } } file_rewrite_args; @@ -455,11 +481,12 @@ static class file_start_args_t { cbl_file_t *file; public: file_start_args_t() : file(NULL) {} - void init( YYLTYPE loc, cbl_file_t *file ) { + cbl_file_t * init( YYLTYPE loc, cbl_file_t *file ) { this->file = file; if( is_sequential(file) ) { error_msg(loc, "START invalid with sequential file %s", file->name); } + return file; } bool ready() const { return file != NULL; } void call_parser_file_start() { @@ -475,21 +502,22 @@ static class file_write_args_t { cbl_refer_t *advance; public: file_write_args_t() - : file(NULL) + : file(nullptr) + , data_source(nullptr) , after(false) - , advance(NULL) + , advance(nullptr) {} cbl_file_t * init( cbl_file_t *file, cbl_field_t *data_source, bool after, - cbl_refer_t *advance ) { + const cbl_refer_t *advance ) { this->file = file; this->data_source = data_source; this->after = after; this->advance = new cbl_refer_t(*advance); return this->file; } - bool ready() const { return file != NULL; } + bool ready() const { return file != nullptr; } void call_parser_file_write( bool sequentially ) { sequentially = sequentially || file->access == file_access_seq_e; parser_file_write(file, data_source, after, *advance, sequentially); @@ -523,7 +551,7 @@ struct arith_t { cbl_refer_t remainder; cbl_label_t *on_error, *not_error; - arith_t( cbl_arith_format_t format ) + explicit arith_t( cbl_arith_format_t format ) : format(format), on_error(NULL), not_error(NULL) {} arith_t( cbl_arith_format_t format, refer_list_t * refers ); @@ -541,8 +569,10 @@ struct arith_t { res.refer.field = cbl_field_of(symbol_at(tgt)); tgts.push_back( res ); - dbgmsg("%s:%d: SRC: %3zu %s", __func__, __LINE__, src, a.str()); - dbgmsg("%s:%d: to %3zu %s", __func__, __LINE__, tgt, res.refer.str()); + dbgmsg("%s:%d: SRC: %3" GCC_PRISZ "u %s", + __func__, __LINE__, (fmt_size_t)src, a.str()); + dbgmsg("%s:%d: to %3" GCC_PRISZ "u %s", + __func__, __LINE__, (fmt_size_t)tgt, res.refer.str()); } void operator()( const corresponding_fields_t::const_reference elem ) { another_pair( elem.first, elem.second ); @@ -605,7 +635,7 @@ class eval_subject_t { void new_object_labels(); public: eval_subject_t(); - void append( cbl_refer_t field ) { + void append( const cbl_refer_t& field ) { columns.push_back(field); pcol = columns.begin(); } @@ -711,6 +741,8 @@ class eval_subject_t { } }; +static std::stack<cbl_label_t *> xml_statements; + class evaluate_t : private std::stack<eval_subject_t> { public: size_t depth() const { return size(); } @@ -736,6 +768,7 @@ public: static void dump_inspect( const cbl_inspect_t& i ); +void dump_inspect_match( const cbl_inspect_match_t& M ); struct perform_t { struct cbl_perform_tgt_t tgt; @@ -775,11 +808,10 @@ struct perform_t { cbl_refer_t table; } search; - perform_t( cbl_label_t *from, cbl_label_t *to = NULL ) + explicit perform_t( cbl_label_t *from, cbl_label_t *to = NULL ) : tgt( from, to ), before(true) - { - search = {}; - } + , search() + {} ~perform_t() { varys.clear(); } cbl_field_t * until() { assert(!varys.empty()); @@ -878,7 +910,7 @@ static struct cbl_label_t * paragraph_reference( const char name[], size_t section ); static inline void -list_add( list<cbl_num_result_t>& list, cbl_refer_t refer, int round ) { +list_add( list<cbl_num_result_t>& list, const cbl_refer_t& refer, int round ) { struct cbl_num_result_t arg = { static_cast<cbl_round_t>(round), refer }; list.push_back(arg); } @@ -916,130 +948,17 @@ teed_up_names() { return name_queue_t::namelist_of( name_queue.peek() ); } -class tokenset_t { - std::vector<const char *>token_names; - std::map <std::string, int> tokens; - std::set<std::string> cobol_words; - - static std::string - lowercase( const cbl_name_t name ) { - cbl_name_t lname; - std::transform(name, name + strlen(name) + 1, lname, ftolower); - return lname; - } - - public: - tokenset_t(); - int find( const cbl_name_t name, bool include_intrinsics ); - - bool equate( const YYLTYPE& loc, int token, const cbl_name_t name ) { - auto lname( lowercase(name) ); - auto cw = cobol_words.insert(lname); - if( ! cw.second ) { - error_msg(loc, "COBOL-WORDS EQUATE: %s may appear but once", name); - return false; - } - auto p = tokens.find(lowercase(name)); - bool fOK = p == tokens.end(); - if( fOK ) { // name not already in use - tokens[lname] = token; - } else { - error_msg(loc, "EQUATE: %s already defined as a token", name); - } - return fOK; - } - bool undefine( const YYLTYPE& loc, const cbl_name_t name ) { - auto lname( lowercase(name) ); - auto cw = cobol_words.insert(lname); - if( ! cw.second ) { - error_msg(loc, "COBOL-WORDS UNDEFINE: %s may appear but once", name); - return false; - } - auto p = tokens.find(lname); - bool fOK = p != tokens.end(); - if( fOK ) { // name in use - tokens.erase(p); - } else { - error_msg(loc, "UNDEFINE: %s not defined as a token", name); - } - return fOK; - } - bool substitute( const YYLTYPE& loc, const cbl_name_t extant, int token, const cbl_name_t name ) { - return equate( loc, token, name ) && undefine( loc, extant ); - } - bool reserve( const YYLTYPE& loc, const cbl_name_t name ) { - auto lname( lowercase(name) ); - auto cw = cobol_words.insert(lname); - if( ! cw.second ) { - error_msg(loc, "COBOL-WORDS RESERVE: %s may appear but once", name); - return false; - } - tokens[lname] = -42; - return true; - } - int redefined_as( const cbl_name_t name ) { - auto lname( lowercase(name) ); - if( cobol_words.find(lname) != cobol_words.end() ) { - auto p = tokens.find(lname); - if( p != tokens.end() ) { - return p->second; - } - } - return 0; - } - const char * name_of( int tok ) const { - tok -= (255 + 3); - gcc_assert(0 <= tok && size_t(tok) < token_names.size()); - return token_names[tok]; - } -}; - -class current_tokens_t { - tokenset_t tokens; - public: - current_tokens_t() {} - int find( const cbl_name_t name, bool include_intrinsics ) { - return tokens.find(name, include_intrinsics); - } - bool equate( const YYLTYPE& loc, cbl_name_t keyword, const cbl_name_t name ) { - int token = keyword_tok(keyword); - if( 0 == token ) { - error_msg(loc, "EQUATE %s: not a valid token", keyword); - return false; - } - return tokens.equate(loc, token, name); - } - bool undefine( const YYLTYPE& loc, cbl_name_t keyword ) { - return tokens.undefine(loc, keyword); - } - bool substitute( const YYLTYPE& loc, cbl_name_t keyword, const cbl_name_t name ) { - int token = keyword_tok(keyword); - if( 0 == token ) { - error_msg(loc, "SUBSTITUTE %s: not a valid token", keyword); - return false; - } - return tokens.substitute(loc, keyword, token, name); - } - bool reserve( const YYLTYPE& loc, const cbl_name_t name ) { - return tokens.reserve(loc, name); - } - int redefined_as( const cbl_name_t name ) { - return tokens.redefined_as(name); - } - const char * name_of( int tok ) const { - return tokens.name_of(tok); - } -} tokens; +#define cdf_tokens cdf_current_tokens() int redefined_token( const cbl_name_t name ) { - return tokens.redefined_as(name); + return cdf_tokens.redefined_as(name); } struct file_list_t { list<cbl_file_t*> files; file_list_t() {} - file_list_t( cbl_file_t* file ) { + explicit file_list_t( cbl_file_t* file ) { files.push_back(file); } file_list_t( file_list_t& that ) : files(that.files.size()) { @@ -1053,10 +972,15 @@ struct file_list_t { struct field_list_t { list<cbl_field_t*> fields; - field_list_t( cbl_field_t *field ) { + field_list_t() {} + explicit field_list_t( cbl_field_t *field ) { fields.push_back(field); } - explicit field_list_t() {} + std::vector<const cbl_field_t*> + as_vector() const { + std::vector<const cbl_field_t*> output( fields.begin(), fields.end() ); + return output; + } }; cbl_field_t ** @@ -1083,7 +1007,7 @@ cbl_file_t ** struct refer_list_t { list<cbl_refer_t> refers; - refer_list_t( cbl_refer_t *refer ) { + explicit refer_list_t( cbl_refer_t *refer ) { if( refer ) { refers.push_back(*refer); delete refer; @@ -1105,13 +1029,20 @@ struct refer_list_t { refers.clear(); return tgt; } + std::vector<cbl_refer_t> + vectorize() { + std::vector<cbl_refer_t> tgt(refers.size()); + std::copy(refers.begin(), refers.end(), tgt.begin()); + refers.clear(); + return tgt; + } }; struct refer_marked_list_t : public refer_list_t { cbl_refer_t *marker; refer_marked_list_t() : refer_list_t(NULL), marker(NULL) {} - refer_marked_list_t( cbl_refer_t *marker, refer_list_t *refers ) + refer_marked_list_t( cbl_refer_t *marker, const refer_list_t *refers ) : refer_list_t(*refers), marker(marker) {} refer_marked_list_t( cbl_refer_t *marker, cbl_refer_t *input ) : refer_list_t(input) @@ -1131,7 +1062,7 @@ struct refer_marked_list_t : public refer_list_t { struct refer_collection_t { list<refer_marked_list_t> lists; - refer_collection_t( const refer_marked_list_t& marked_list ) + explicit refer_collection_t( const refer_marked_list_t& marked_list ) { lists.push_back( marked_list ); } @@ -1157,48 +1088,13 @@ struct refer_collection_t { } }; -struct ast_inspect_oper_t { - cbl_inspect_bound_t bound; // CHARACTERS/ALL/LEADING/FIRST - std::list<cbl_inspect_match_t> matches; - std::list<cbl_inspect_replace_t> replaces; - -ast_inspect_oper_t( const cbl_inspect_match_t& match, - cbl_inspect_bound_t bound = bound_characters_e ) - : bound(bound) - { - matches.push_back(match); - } - ast_inspect_oper_t( const cbl_inspect_replace_t& replace, - cbl_inspect_bound_t bound = bound_characters_e ) - : bound(bound) - { - replaces.push_back(replace); - } -}; - -struct ast_inspect_t : public std::list<cbl_inspect_oper_t> { - cbl_refer_t tally; // field is NULL for REPLACING - const std::list<cbl_inspect_oper_t>& opers() const { return *this; } -}; - -struct ast_inspect_list_t : public std::list<cbl_inspect_t> { - ast_inspect_list_t( const cbl_inspect_t& insp ) { - push_back(insp); - } - - cbl_inspect_t * as_array() { - cbl_inspect_t *output = new cbl_inspect_t[ size() ]; - std::copy( begin(), end(), output ); - return output; - } -}; - -void ast_inspect( cbl_refer_t& input, bool backward, ast_inspect_list_t& inspects ); +void ast_inspect( YYLTYPE loc, cbl_refer_t& input, bool backward, + cbl_inspect_opers_t& inspects ); template <typename E> struct elem_list_t { list<E*> elems; - elem_list_t( E *elem ) { + explicit elem_list_t( E *elem ) { elems.push_back(elem); } void clear() { @@ -1223,7 +1119,7 @@ template <typename L, typename E> struct unstring_tgt_t { cbl_refer_t *tgt, *delimiter, *count; - unstring_tgt_t( cbl_refer_t *tgt, + explicit unstring_tgt_t( cbl_refer_t *tgt, cbl_refer_t *delimiter = NULL, cbl_refer_t *count = NULL ) : tgt(tgt), delimiter(delimiter), count(count) @@ -1247,7 +1143,7 @@ private: struct unstring_tgt_list_t { list<unstring_tgt_t> unstring_tgts; - unstring_tgt_list_t( unstring_tgt_t *unstring_tgt ) { + explicit unstring_tgt_list_t( unstring_tgt_t *unstring_tgt ) { unstring_tgts.push_back(*unstring_tgt); delete unstring_tgt; } @@ -1269,7 +1165,7 @@ struct unstring_tgt_list_t { struct unstring_into_t : public unstring_tgt_list_t { cbl_refer_t pointer, tally; - unstring_into_t( unstring_tgt_list_t *tgt_list, + explicit unstring_into_t( unstring_tgt_list_t *tgt_list, cbl_refer_t *pointer = NULL, cbl_refer_t *tally = NULL ) : unstring_tgt_list_t(*tgt_list) @@ -1285,7 +1181,7 @@ struct unstring_into_t : public unstring_tgt_list_t { struct ffi_args_t { list<cbl_ffi_arg_t> elems; - ffi_args_t( cbl_ffi_arg_t *arg ) { + explicit ffi_args_t( cbl_ffi_arg_t *arg ) { this->push_back(arg); } @@ -1361,8 +1257,8 @@ struct file_sort_io_t { file_list_t file_list; cbl_perform_tgt_t tgt; - file_sort_io_t( file_list_t& files ) : file_list(files) {} - file_sort_io_t( cbl_perform_tgt_t& tgt ) : tgt(tgt.from(), tgt.to()) {} + explicit file_sort_io_t( file_list_t& files ) : file_list(files) {} + explicit file_sort_io_t( cbl_perform_tgt_t& tgt ) : tgt(tgt.from(), tgt.to()) {} size_t nfile() const { return file_list.files.size(); } }; @@ -1377,14 +1273,14 @@ struct merge_t { cbl_perform_tgt_t tgt; list<cbl_file_t*> outputs; - merge_t( cbl_file_t *input ) : master(input), type(output_unknown_e) {} + explicit merge_t( cbl_file_t *input ) : master(input), type(output_unknown_e) {} }; static list<merge_t> merges; static inline merge_t& merge_alloc( cbl_file_t *file ) { - merges.push_back(file); + merges.push_back(merge_t(file)); return merges.back(); } @@ -1405,7 +1301,7 @@ static list<cbl_refer_t> lhs; struct vargs_t { std::list<cbl_refer_t> args; vargs_t() {} - vargs_t( struct cbl_refer_t *p ) { args.push_back(*p); delete p; } + explicit vargs_t( struct cbl_refer_t *p ) { args.push_back(*p); delete p; } void push_back( cbl_refer_t *p ) { args.push_back(*p); delete p; } }; @@ -1422,34 +1318,48 @@ std::map<std::string, std::list<std::string>> class prog_descr_t { std::set<std::string> call_targets, subprograms; - public: + std::set<cbl_locale_t> locales; +public: std::set<function_descr_t> function_repository; - size_t program_index, declaratives_index; + size_t program_index; cbl_label_t *declaratives_eval, *paragraph, *section; const char *collating_sequence; - struct locale_t { - cbl_name_t name; const char *os_name; - locale_t(const cbl_name_t name = NULL, const char *os_name = NULL) - : name(""), os_name(os_name) { - if( name ) { - bool ok = namcpy(YYLTYPE(), this->name, name); - gcc_assert(ok); + struct encoding_t { + struct encoding_base_t { + size_t isym; + cbl_encoding_t encoding; + encoding_base_t() : isym(0), encoding(CP1252_e) {} + encoding_base_t(cbl_encoding_t encoding) : isym(0), encoding(encoding) {} + void set( size_t isym, cbl_encoding_t encoding ) { + this->isym = isym; + this->encoding = encoding; } - } - } locale; - cbl_call_convention_t call_convention; + void set( cbl_encoding_t encoding ) { + assert(encoding != custom_encoding_e); + this->isym = 0; + this->encoding = encoding; + } + + } alpha, national; + encoding_t() : national(EBCDIC_e) {} + } alphabet; + + bool locale_add( const cbl_locale_t& locale ) { + auto e = symbol_locale_add(program_index, &locale); + assert(e); + auto p = locales.insert(locale); + return p.second; + } + cbl_options_t options; - prog_descr_t( size_t isymbol ) + explicit prog_descr_t( size_t isymbol ) : program_index(isymbol) - , declaratives_index(0) , declaratives_eval(NULL) , paragraph(NULL) , section(NULL) , collating_sequence(NULL) - { - call_convention = current_call_convention(); - } + {} std::set<std::string> external_targets() { std::set<std::string> externals; @@ -1538,24 +1448,13 @@ static cbl_label_t * implicit_section(); class program_stack_t : protected std::stack<prog_descr_t> { struct pending_t { - cbl_call_convention_t call_convention; bool initial; - pending_t() - : call_convention(cbl_call_convention_t(0)) - , initial(false) - {} + pending_t() : initial(false) {} } pending; public: - cbl_call_convention_t - pending_call_convention( cbl_call_convention_t convention ) { - return pending.call_convention = convention; - } bool pending_initial() { return pending.initial = true; } void push( prog_descr_t descr ) { - cbl_call_convention_t current_call_convention = cbl_call_cobol_e; - if( !empty() ) current_call_convention = top().call_convention; - descr.call_convention = current_call_convention; std::stack<prog_descr_t>& me(*this); me.push(descr); } @@ -1581,9 +1480,6 @@ class program_stack_t : protected std::stack<prog_descr_t> { } void apply_pending() { - if( size() == 1 && 0 != pending.call_convention ) { - top().call_convention = pending.call_convention; - } if( pending.initial ) { auto e = symbol_at(top().program_index); auto prog(cbl_label_of(e)); @@ -1591,11 +1487,12 @@ class program_stack_t : protected std::stack<prog_descr_t> { } } + // cppcheck-suppress-begin useStlAlgorithm cbl_label_t *first_declarative() { auto eval = top().declaratives_eval; if( eval ) return eval; // scan stack container for declaratives - for( auto& prog : c ) { + for( const auto& prog : c ) { if( prog.declaratives_eval ) { eval = prog.declaratives_eval; break; @@ -1603,6 +1500,7 @@ class program_stack_t : protected std::stack<prog_descr_t> { } return eval; } + // cppcheck-suppress-end useStlAlgorithm }; struct rel_part_t { @@ -1610,9 +1508,13 @@ struct rel_part_t { bool has_relop, invert; relop_t relop; - rel_part_t( cbl_refer_t *operand = NULL, - relop_t relop = relop_t(-1), - bool invert = false ) + rel_part_t() + : operand(nullptr), + has_relop(false), + invert(false), + relop(relop_t(-1)) + {} + rel_part_t( cbl_refer_t *operand, relop_t relop, bool invert ) : operand(operand), has_relop(relop != -1), invert(invert), @@ -1646,7 +1548,7 @@ struct rel_part_t { class log_expr_t { cbl_field_t *orable, *andable; public: - log_expr_t( cbl_field_t *init ) : orable(NULL), andable(init) { + explicit log_expr_t( cbl_field_t *init ) : orable(NULL), andable(init) { if( ! is_conditional(init) ) { dbgmsg("%s:%d: logic error: %s is not a truth value", __func__, __LINE__, name_of(init)); @@ -1705,18 +1607,11 @@ static class current_t { int first_statement; bool in_declaratives; // from command line or early TURN - std::list<cbl_exception_files_t> cobol_exceptions; + std::list<exception_turn_t> exception_turns; error_labels_t error_labels; static void declarative_execute( cbl_label_t *eval ) { - if( !eval ) { - if( !enabled_exceptions.empty() ) { - auto index = new_temporary(FldNumericBin5); - parser_match_exception(index, NULL); - } - return; - } assert(eval); auto iprog = symbol_elem_of(eval)->program; if( iprog == current_program_index() ) { @@ -1734,6 +1629,8 @@ static class current_t { rel_part_t antecedent_cache; public: + static prog_descr_t::encoding_t::encoding_base_t default_encoding; + current_t() : first_statement(0) , in_declaratives(false) @@ -1812,6 +1709,10 @@ static class current_t { class declaratives_t : protected declaratives_list_t { struct file_exception_t { ec_type_t type; uint32_t file; + file_exception_t() : type(ec_none_e), file(0) {} + file_exception_t(ec_type_t type, uint32_t file) + : type(type), file(file) + {} bool operator<( const file_exception_t& that ) const { if( type == that.type ) return file < that.file; return type < that.type; @@ -1819,6 +1720,13 @@ static class current_t { }; std::set<file_exception_t> file_exceptions; public: + declaratives_t() {} + // current compiled data for enabled ECs and Declaratives, used by library. + struct runtime_t { + tree ena, dcl; + runtime_t() : ena(nullptr), dcl(nullptr) {} + } runtime; + bool empty() const { return declaratives_list_t::empty(); } @@ -1836,7 +1744,7 @@ static class current_t { } for( auto f = declarative.files; f && f < declarative.files + declarative.nfile; f++ ) { - file_exception_t ex = { declarative.type, *f }; + file_exception_t ex( declarative.type, *f ); auto result = file_exceptions.insert(ex); if( ! result.second ) { yyerror("%s defined twice for %s", @@ -1848,14 +1756,46 @@ static class current_t { declaratives_list_t::push_back(declarative); return true; } + + // cppcheck-suppress-begin useStlAlgorithm + uint32_t status() const { + uint32_t status_word = 0; + for( auto dcl : *this ) { + status_word |= (EC_ALL_E & dcl.type ); + } + return status_word; + } + // cppcheck-suppress-end useStlAlgorithm + + bool has_format_1() const { + return std::any_of( begin(), end(), + []( const cbl_declarative_t& dcl ) { + return dcl.is_format_1(); + } ); + } + + std::vector<uint64_t> + encode() const { + std::vector<uint64_t> encoded; + auto p = std::back_inserter(encoded); + for( const auto& dcl : *this ) { + *p++ = dcl.section; + *p++ = dcl.global; + *p++ = dcl.type; + *p++ = dcl.nfile; + p = std::copy(dcl.files, std::end(dcl.files), p); + *p++ = dcl.mode; + } + return encoded; + } + } declaratives; void exception_add( ec_type_t ec, bool enabled = true) { - std::set<size_t> files; - enabled_exceptions.turn_on_off(enabled, - false, // for now - ec, files); - if( yydebug) enabled_exceptions.dump(); + exception_turns.push_back(exception_turn_t(ec, enabled)); + } + std::list<exception_turn_t>& pending_exceptions() { + return exception_turns; } bool typedef_add( const cbl_field_t *field ) { @@ -1865,7 +1805,6 @@ static class current_t { const cbl_field_t * has_typedef( const cbl_field_t *field ) { auto found = typedefs.find(field); return found == typedefs.end()? NULL : *found; - return found == typedefs.end()? NULL : *found; } void udf_add( size_t isym ) { @@ -1921,12 +1860,39 @@ static class current_t { std::list<std::string>& debugging_declaratives(bool all) const { const char *para = programs.top().paragraph->name; - auto declaratives = debugging_clients.find(all? ":all:" : para); - if( declaratives == debugging_clients.end() ) { + auto client = debugging_clients.find(all? ":all:" : para); + if( client == debugging_clients.end() ) { static std::list<std::string> empty; return empty; } - return declaratives->second; + return client->second; + } + + void alpha_encoding( size_t isym, cbl_encoding_t encoding ) { + prog_descr_t& program = programs.top(); + program.alphabet.alpha.set(isym, encoding); + } + void national_encoding( size_t isym, cbl_encoding_t encoding ) { + prog_descr_t& program = programs.top(); + program.alphabet.national.set(isym, encoding); + } + + cbl_encoding_t alpha_encoding() const { + if( programs.empty() ) return CP1252_e; + const prog_descr_t& program = programs.top(); + return program.alphabet.alpha.encoding; + } + cbl_encoding_t national_encoding() const { + cbl_encoding_t when_empty = EBCDIC_e; + char *alternate = getenv("NATIONAL"); + if( alternate ) + { + when_empty = __gg__encoding_iconv_type(alternate); + gcc_assert(when_empty); + } + if( programs.empty() ) return when_empty; + const prog_descr_t& program = programs.top(); + return program.alphabet.national.encoding; } bool @@ -1949,36 +1915,8 @@ static class current_t { return programs.top().options.default_round = mode; } - cbl_call_convention_t - call_convention() { - return programs.empty()? cbl_call_cobol_e : programs.top().call_convention; - } - cbl_call_convention_t - call_convention( cbl_call_convention_t convention) { - if( programs.empty() ) { - return programs.pending_call_convention(convention); - } - auto& prog( programs.top() ); - return prog.call_convention = convention; - } - - const char * - locale() { - return programs.empty()? NULL : programs.top().locale.os_name; - } - const char * - locale( const cbl_name_t name ) { - if( programs.empty() ) return NULL; - const prog_descr_t::locale_t& locale = programs.top().locale; - return 0 == strcmp(name, locale.name)? locale.name : NULL; - } - const prog_descr_t::locale_t& - locale( const cbl_name_t name, const char os_name[] ) { - if( programs.empty() ) { - static prog_descr_t::locale_t empty; - return empty; - } - return programs.top().locale = prog_descr_t::locale_t(name, os_name); + bool locale_add( const cbl_locale_t& locale ) { + return programs.top().locale_add(locale); } bool new_program ( const YYLTYPE& loc, cbl_label_type_t type, @@ -1997,7 +1935,16 @@ static class current_t { const cbl_label_t *L; if( (L = symbol_program_add(parent, &label)) == NULL ) return false; - programs.push( symbol_index(symbol_elem_of(L))); + prog_descr_t program(symbol_index(symbol_elem_of(L))); +#if 1 //EBCDIC // enable when ready + auto alpha_encoding = + programs.empty()? default_encoding : programs.top().alphabet.alpha; + if( alpha_encoding.encoding == EBCDIC_e ) { + dbgmsg("%s:%d: We're in EBCDIC", __func__, __LINE__); + } + program.alphabet.alpha = alpha_encoding; +#endif + programs.push( program ); programs.apply_pending(); bool fOK = symbol_at(programs.top().program_index) + 1 == symbols_end(); @@ -2021,10 +1968,6 @@ static class current_t { assert(!programs.empty()); return programs.top().program_index; } - size_t program_declaratives(void) const { - if( programs.empty() ) return 0; - return programs.top().declaratives_index; - } const cbl_label_t * program(void) { return programs.empty()? NULL : cbl_label_of(symbol_at(programs.top().program_index)); @@ -2038,12 +1981,16 @@ static class current_t { bool is_first_statement( const YYLTYPE& loc ) { if( ! in_declaratives && first_statement == 0 ) { - if( ! symbol_label_section_exists(program_index()) ) { - if( ! dialect_ibm() ) { - error_msg(loc, - "Per ISO a program with DECLARATIVES must begin with a SECTION, " - "requires -dialect ibm"); - } + auto eval = programs.top().declaratives_eval; + if( eval ) { + size_t ilabel = symbol_index(symbol_elem_of(eval)); + if( ! symbol_label_section_exists(ilabel) ) { + if( ! dialect_ibm() ) { + error_msg(loc, + "Per ISO a program with DECLARATIVES must begin with a SECTION, " + "requires %<-dialect ibm%>"); + } + } } first_statement = loc.first_line; return true; @@ -2059,13 +2006,14 @@ static class current_t { * ISO, in new_program. */ std::set<std::string> end_program() { + cbl_enabled_exceptions_t& enabled_exceptions( cdf_enabled_exceptions() ); if( enabled_exceptions.size() ) { - declaratives_evaluate(ec_none_e); + declaratives_evaluate(); } assert(!programs.empty()); - procref_t *ref = ambiguous_reference(program_index()); + const procref_t *ref = ambiguous_reference(program_index()); std::set<std::string> externals = programs.top().external_targets(); /* @@ -2076,9 +2024,19 @@ static class current_t { * subprograms, and whether or not they are COMMON. PROGRAM may be * the caller, or a subprogram could call COMMON sibling. */ + + static std::unordered_set<size_t> callers_we_have_seen; if( programs.size() == 1 ) { if( yydebug ) parser_call_targets_dump(); for( size_t caller : symbol_program_programs() ) { + // We are running through the entire growing list of called programs + // at the point of each END PROGRAM. This confuses the name changing + // routines, so we use a std::set to avoid doing callers more than + // once. + if( callers_we_have_seen.find(caller) != callers_we_have_seen.end() ) + { + continue; + } const char *caller_name = cbl_label_of(symbol_at(caller))->name; for( auto callable : symbol_program_callables(caller) ) { auto called = cbl_label_of(symbol_at(callable)); @@ -2086,11 +2044,16 @@ static class current_t { called->mangled_name? called->mangled_name : called->name; size_t n = - parser_call_target_update(caller, called->name, mangled_name); + parser_call_target_update(caller, + called->name, + mangled_name); // Zero is not an error - dbgmsg("updated %zu calls from #%-3zu (%s) s/%s/%s/", - n, caller, caller_name, called->name, mangled_name); + dbgmsg("updated " HOST_SIZE_T_PRINT_UNSIGNED + " calls from #%-3" GCC_PRISZ "u (%s) s/%s/%s/", + (fmt_size_t)n, (fmt_size_t)caller, caller_name, + called->name, mangled_name); } + callers_we_have_seen.insert(caller); } if( yydebug ) parser_call_targets_dump(); } @@ -2104,8 +2067,9 @@ static class current_t { exception_clients.clear(); if( ref ) { - yywarn("could not resolve paragraph (or section) '%s' at line %d", - ref->paragraph(), ref->line_number()); + cbl_message(ParUnresolvedProcE, + "could not resolve paragraph (or section) '%s' at line %d", + ref->paragraph(), ref->line_number()); // add string to indicate ambiguity error externals.insert(":ambiguous:"); } @@ -2120,7 +2084,7 @@ static class current_t { return symbol_index(symbol_elem_of(section)); } - cbl_label_t *doing_declaratives( bool begin ) { + cbl_label_t * doing_declaratives( bool begin ) { if( begin ) { in_declaratives = true; return NULL; @@ -2130,24 +2094,27 @@ static class current_t { if( declaratives.empty() ) return NULL; assert(!declaratives.empty()); - size_t idcl = symbol_declaratives_add(program_index(), declaratives.as_list()); - programs.top().declaratives_index = idcl; + declaratives.runtime.dcl = parser_compile_dcls(declaratives.encode()); // Create section to evaluate declaratives. Given them unique names so // that we can figure out what is going on in a trace or looking at the // assembly language. - static int eval_count=1; - char eval[32]; - char lave[32]; + static int eval_count = 1; + char eval[32], lave[32]; + sprintf(eval, "_DECLARATIVES_EVAL%d", eval_count); - sprintf(lave, "_DECLARATIVES_LAVE%d", eval_count); - eval_count +=1 ; + sprintf(lave, "_DECLARATIVES_LAVE%d", eval_count++); struct cbl_label_t*& eval_label = programs.top().declaratives_eval; eval_label = label_add(LblSection, eval, yylineno); struct cbl_label_t * lave_label = label_add(LblSection, lave, yylineno); + ast_enter_section(eval_label); - declarative_runtime_match(cbl_field_of(symbol_at(idcl)), lave_label); + + declarative_runtime_match(declaratives.as_list(), lave_label); + + parser_label_label(lave_label); + return lave_label; } @@ -2155,14 +2122,32 @@ static class current_t { std::swap( programs.top().section, section ); return section; } + + ec_type_t ec_type_of( file_status_t status ) { + static std::vector<ec_type_t> ec_by_status { + /* 0 */ ec_none_e, // ec_io_warning_e if low byte is nonzero + /* 1 */ ec_io_at_end_e, + /* 2 */ ec_io_invalid_key_e, + /* 3 */ ec_io_permanent_error_e, + /* 4 */ ec_io_logic_error_e, + /* 5 */ ec_io_record_operation_e, + /* 6 */ ec_io_file_sharing_e, + /* 7 */ ec_io_record_content_e, + /* 8 */ ec_io_imp_e, // unused, not defined by ISO + /* 9 */ ec_io_imp_e, + }; + int status10 = static_cast<unsigned int>(status) / 10; + gcc_assert(ec_by_status.size() == 10); + gcc_assert(0 <= status10 && status10 < 10 && status10 != 8); + return ec_by_status[status10]; + } /* * END DECLARATIVES causes: - * 1. Add DECLARATIVES symbol, containing criteria blob. - * 2. Create section _DECLARATIVES_EVAL + * 1. Create section _DECLARATIVES_EVAL * and exit label _DECLARATIVES_LAVE - * 3. declarative_runtime_match generates runtime evaluation "ladder". - * 4. After a declarative is executed, control branches to the exit label. + * 2. declarative_runtime_match generates runtime evaluation "ladder". + * 3. After a declarative is executed, control branches to the exit label. * * After each verb, we call declaratives_evaluate, * which PERFORMs _DECLARATIVES_EVAL. @@ -2172,18 +2157,8 @@ static class current_t { * alternative entry point (TODO). */ void - declaratives_evaluate( cbl_file_t *file, - file_status_t status = FsSuccess ) { - // The exception file number is assumed to be zero at all times unless - // it has been set to non-zero, at which point whoever picks it up and takes - // action on it is charged with setting it back to zero. - if( file ) - { - parser_set_file_number((int)symbol_index(symbol_elem_of(file))); - } - // parser_set_file_number(file ? (int)symbol_index(symbol_elem_of(file)) : 0); - parser_set_handled((ec_type_t)status); - + declaratives_evaluate( cbl_file_t *file ) { + gcc_assert(file); parser_file_stash(file); cbl_label_t *eval = programs.first_declarative(); @@ -2211,7 +2186,7 @@ static class current_t { * To indicate to the runtime-match function that we want to evaluate * only the exception condition, unrelated to a file, we set the * file register to 0 and the handled-exception register to the - * handled exception condition (not file status). + * handled exception condition. * * declaratives_execute performs the "declarative ladder" produced * by declaratives_runtime_match. That section CALLs the @@ -2222,16 +2197,9 @@ static class current_t { * index, per usual. */ void - declaratives_evaluate( ec_type_t handled = ec_none_e ) { - // The exception file number is assumed to be zero unless it has been - // changed to a non-zero value. The program picking it up and referencing - // it is charged with setting it back to zero. - // parser_set_file_number(0); - - parser_set_handled(handled); - + declaratives_evaluate() { cbl_label_t *eval = programs.first_declarative(); - declarative_execute(eval); + if( eval ) declarative_execute(eval); } cbl_label_t * new_paragraph( cbl_label_t *para ) { @@ -2242,11 +2210,10 @@ static class current_t { } void antecedent_dump() const { - if( ! yydebug ) return; if( ! antecedent_cache.operand ) { - yywarn( "Antecedent: none" ); + dbgmsg( "Antecedent: none" ); } else { - yywarn( "Antecedent: %c %s %s %c", + dbgmsg( "Antecedent: %c %s %s %c", antecedent_cache.invert? '!':' ', name_of(antecedent_cache.operand->field), relop_str(antecedent_cache.relop), @@ -2275,28 +2242,56 @@ static class current_t { cbl_label_t * compute_label() { return error_labels.compute_error; } } current; +prog_descr_t::encoding_t::encoding_base_t current_t::default_encoding; + +void current_enabled_ecs( tree ena ) { + current.declaratives.runtime.ena = ena; +} + #define PROGRAM current.program_index() static void add_debugging_declarative( const cbl_label_t * label ) { + // cppcheck-suppress [unreadVariable] obviously not true const char *section = current.declarative_section_name(); if( section ) { debugging_clients[label->name].push_back(section); } -}; +} -cbl_options_t current_options() { +cbl_options_t +current_options() { return current.options_paragraph; } -size_t current_program_index() { +cbl_encoding_t +current_encoding( char a_or_n ) { + cbl_encoding_t retval; + switch(a_or_n) { + case 'A': + retval = current.alpha_encoding(); + break; + case 'N': + retval = current.national_encoding(); + break; + default: + gcc_unreachable(); + break; + } + return retval; +} + +size_t +current_program_index() { return current.program()? current.program_index() : 0; } -cbl_label_t * current_section() { +cbl_label_t * +current_section() { return current.section(); } -cbl_label_t * current_paragraph() { +cbl_label_t * +current_paragraph() { return current.paragraph(); } @@ -2325,15 +2320,6 @@ current_rounded_mode( cbl_round_t rounded) { #endif static cbl_round_t current_rounded_mode( int token ); -cbl_call_convention_t -current_call_convention() { - return current.call_convention(); -} -cbl_call_convention_t -current_call_convention( cbl_call_convention_t convention) { - return current.call_convention(convention); -} - size_t program_level() { return current.program_level(); } static size_t constant_index( int token ); @@ -2353,10 +2339,19 @@ char * normalize_picture( char picture[] ); static inline cbl_field_t * -new_tempnumeric(void) { return new_temporary(FldNumericBin5); } +new_tempnumeric(const cbl_name_t name = nullptr, cbl_field_attr_t attr = signable_e ) { + return new_temporary(FldNumericBin5, name, attr == signable_e); +} static inline cbl_field_t * -new_tempnumeric_float(void) { return new_temporary(FldFloat); } +new_tempnumeric(const cbl_field_attr_t attr ) { + return new_temporary(FldNumericBin5, nullptr, attr == signable_e); +} + +static inline cbl_field_t * +new_tempnumeric_float(const cbl_name_t name = nullptr) { + return new_temporary(FldFloat, name); +} uint32_t type_capacity( enum cbl_field_type_t type, uint32_t digits ); @@ -2374,11 +2369,32 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ); static bool is_integer_literal( const cbl_field_t *field ) { - if( is_literal(field) ) { - int v, n; - const char *initial = field->data.initial; - - return 1 == sscanf(initial, "%d%n", &v, &n) && n == (int)strlen(initial); + if( field->type == FldLiteralN ) { + size_t nchar; + const char *initial = __gg__iconverter(field->codeset.encoding, + DEFAULT_SOURCE_ENCODING, + field->data.initial, + strlen(field->data.initial), + &nchar); + assert(strlen(initial) == nchar); + switch( *initial ) { + case '-': case '+': ++initial; + } + + const char *eos = initial + strlen(initial); + auto p = std::find_if_not( initial, eos, fisdigit ); + if( p == eos ) return true; + + if( *p++ == symbol_decimal_point() ) { + switch( *p++ ) { + case 'E': case 'e': + switch( *p++ ) { + case '+': case '-': + return std::all_of(p, eos, []( char ch ) { return ch == '0'; } ); + break; + } + } + } } return false; } @@ -2408,7 +2424,6 @@ needs_picture( cbl_field_type_t type ) { case FldNumericBin5: return false; - case FldBlob: case FldClass: case FldConditional: case FldForward: @@ -2437,7 +2452,6 @@ is_callable( const cbl_field_t *field ) { case FldForward: case FldSwitch: case FldDisplay: - case FldBlob: case FldNumericDisplay: case FldNumericBinary: case FldFloat: @@ -2452,7 +2466,8 @@ is_callable( const cbl_field_t *field ) { case FldPointer: return true; } - cbl_internal_error( "%s:%d: invalid symbol_type_t %d", __func__, __LINE__, field->type ); + cbl_internal_error( "%s:%d: invalid %<symbol_type_t%> %d", + __func__, __LINE__, field->type ); return false; } @@ -2499,16 +2514,16 @@ intrinsic_call_1( cbl_field_t *output, int token, } static bool -intrinsic_call_2( cbl_field_t *tgt, int token, cbl_refer_t *r1, cbl_refer_t *r2 ) { - std::vector<cbl_refer_t> args { *r1, *r2 }; +intrinsic_call_2( cbl_field_t *tgt, int token, const cbl_refer_t *r1, cbl_refer_t *r2 ) { + std::vector<cbl_refer_t> args { *r1, r2? *r2 : cbl_refer_t() }; size_t n = intrinsic_invalid_parameter(token, args); if( n < args.size() ) { error_msg(args[n].loc, "invalid parameter '%s'", args[n].field->name); return false; } - const char *fund = intrinsic_cname(token); - if( !fund ) return false; - parser_intrinsic_call_2( tgt, fund, args[0], args[1] ); + const char *func = intrinsic_cname(token); + if( !func ) return false; + parser_intrinsic_call_2( tgt, func, args[0], args[1] ); return true; } @@ -2577,18 +2592,14 @@ table_primary_index( cbl_field_t *table ) { NULL : cbl_field_of(symbol_at(table->occurs.indexes.fields[0])); } -static inline const cbl_refer_t // & // Removed the '&' to stop a weird compiler error +static inline const cbl_refer_t // return copy, not element reference invalid_key( const cbl_refer_t& ref ) { assert(ref.field); - - if( ref.nsubscript == 0 ) return ref; - - for( size_t i=0; i < ref.nsubscript; i++ ) { - if( ref.subscripts[i].field->parent != ref.field->parent ) { - return ref.subscripts[i]; - } - } - return NULL; + auto p = std::find_if( ref.subscripts.begin(), ref.subscripts.end(), + [parent = ref.field->parent]( const auto &sub ) { + return sub.field->parent == parent; + } ); + return p != ref.subscripts.end() ? *p : nullptr; } static inline symbol_elem_t * @@ -2790,17 +2801,6 @@ group_attr( const cbl_field_t * field ) { return p->attr; } -static struct symbol_elem_t * -field_of( const char F[], int L, const char name[] ) { - struct symbol_elem_t *e = symbol_field(PROGRAM, 0, name); - if( !e ) { - cbl_internal_error("%s:%d: no symbol '%s' found", F, L, name); - } - assert( procedure_div_e != current_division ); - return e; -} -#define field_of( F ) field_of(__func__, __LINE__, (F)) - static struct cbl_field_t * field_add( const YYLTYPE& loc, cbl_field_t *field ) { switch(current_data_section) { @@ -2847,7 +2847,7 @@ field_attr_str( const cbl_field_t *field ) { intermediate_e, embiggened_e, all_alpha_e, all_x_e, all_ax_e, prog_ptr_e, scaled_e, refmod_e, based_e, any_length_e, global_e, external_e, blank_zero_e, linkage_e, local_e, leading_e, - separate_e, envar_e, dnu_1_e, bool_encoded_e, hex_encoded_e, + separate_e, envar_e, encoded_e, bool_encoded_e, hex_encoded_e, depends_on_e, initialized_e, has_value_e, ieeedec_e, big_endian_e, same_as_e, record_key_e, typedef_e, strongdef_e, }; @@ -2955,29 +2955,39 @@ blank_pad_initial( const char initial[], size_t capacity, size_t new_size ) { return p; } -static bool +/* + * When cbl_field_t::internalize is called, its data.initial value has been + * set, but nothing has been done to it. It is encoded according to the source + * code. internalize() converts data.initial to the field's encoding. + * + * If syntax used was was PIC VALUE, in that order, then PIC set the field's + * encoding, and the VALUE clause can verify that its encoding matches. If the + * order was VALUE PIC, the value leaves the encoding uninitialized unless the + * value string bore an encoding prefix. When PIC is processed, codeset_t::set + * allows it to set the encoding only if it's either uninitialized, or the PIC + * encoding matches the existing one set by VALUE. In no event does one + * override the other; they must agree. + * + * internalize() fails if data.initial cannot be converted to the field's + * encoding. + */ +static void value_encoding_check( const YYLTYPE& loc, cbl_field_t *field ) { if( ! field->internalize() ) { error_msg(loc, "inconsistent string literal encoding for '%s'", field->data.initial); - return false; } - return true; } - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" static struct cbl_field_t * field_alloc( const YYLTYPE& loc, cbl_field_type_t type, size_t parent, const char name[] ) { - cbl_field_t *f, field = {}; - field.type = type; - field.usage = FldInvalid; + static const uint32_t level = 0; + cbl_field_t *f, field = { type, 0, cbl_field_data_t(), level, name, yylineno }; field.parent = parent; - field.line = yylineno; - if( !namcpy(loc, field.name, name) ) return NULL; f = field_add(loc, &field); assert(f); return f; @@ -2993,7 +3003,7 @@ static cbl_file_t * file_add( YYLTYPE loc, cbl_file_t *file ) { gcc_assert(file); enum { level = 1 }; - struct cbl_field_t area = { 0, FldAlphanumeric, FldInvalid, 0, 0,0, level, {}, yylineno }, + struct cbl_field_t area{ FldAlphanumeric, level, yylineno }, *field = field_add(loc, &area); file->default_record = field_index(field); @@ -3005,13 +3015,14 @@ file_add( YYLTYPE loc, cbl_file_t *file ) { } file = cbl_file_of(e); snprintf(field->name, sizeof(field->name), - "%s%zu_%s", - record_area_name_stem, symbol_index(e), file->name); + "%s" HOST_SIZE_T_PRINT_UNSIGNED "_%s", + record_area_name_stem, (fmt_size_t)symbol_index(e), file->name); if( file->attr & external_e ) { snprintf(field->name, sizeof(field->name), "%s%s", record_area_name_stem, file->name); } field->file = field->parent = symbol_index(e); + field->codeset.set(); return file; } @@ -3020,12 +3031,16 @@ file_add( YYLTYPE loc, cbl_file_t *file ) { static cbl_alphabet_t * -alphabet_add( const YYLTYPE& loc, cbl_encoding_t encoding ) { - cbl_alphabet_t alphabet(loc, encoding); +alphabet_add( const cbl_alphabet_t& alphabet ) { symbol_elem_t *e = symbol_alphabet_add(PROGRAM, &alphabet); assert(e); return cbl_alphabet_of(e); } +static cbl_alphabet_t * +alphabet_add( const YYLTYPE& loc, cbl_encoding_t encoding ) { + cbl_alphabet_t alphabet(loc, encoding); + return alphabet_add(alphabet); +} // The current field always exists in the symbol table, even if it's incomplete. static cbl_field_t * @@ -3036,6 +3051,17 @@ current_field(cbl_field_t * field = NULL) { return local; } +static void +set_real_from_capacity( const YYLTYPE& loc, + cbl_field_t *field, + REAL_VALUE_TYPE *r ) { + if( field == current_field() ) { + error_msg(loc, "cannot define %s via self-reference", field->name); + return; + } + field->data.set_real_from_capacity(r); +} + static struct cbl_special_name_t * special_of( const char F[], int L, const char name[] ) { struct symbol_elem_t *e = symbol_special(PROGRAM, name); @@ -3047,15 +3073,30 @@ special_of( const char F[], int L, const char name[] ) { } #define special_of( F ) special_of(__func__, __LINE__, (F)) +static const special_name_t * +cmd_or_env_special_of( std::string name ) { + static const std::map< std::string, special_name_t > fujitsus + { // Fujitsu calls these "function names", not device names + { "ARGUMENT-NUMBER", ARG_NUM_e }, + { "ARGUMENT-VALUE", ARG_VALUE_e } , + { "ENVIRONMENT-NAME", ENV_NAME_e }, + { "ENVIRONMENT-VALUE", ENV_VALUE_e }, + }; + + std::transform(name.begin(), name.end(), name.begin(), ::toupper); + auto p = fujitsus.find(name.c_str()); + return p != fujitsus.end()? &p->second : nullptr; +} + static inline void -parser_add2( struct cbl_num_result_t& to, - struct cbl_refer_t from ) { +parser_add2( const cbl_num_result_t& to, + const cbl_refer_t& from ) { parser_add(to.refer, to.refer, from, to.rounded); } static inline void -parser_subtract2( struct cbl_num_result_t to, - struct cbl_refer_t from ) { +parser_subtract2( const cbl_num_result_t& to, + const cbl_refer_t& from ) { parser_subtract(to.refer, to.refer, from, to.rounded); } @@ -3078,6 +3119,10 @@ parser_move_carefully( const char */*F*/, int /*L*/, } } else { if( ! valid_move( tgt.field, src.field ) ) { + if( src.field->type == FldPointer && + tgt.field->type == FldPointer ) { + dialect_ok(src.loc, MfMovePointer, "MOVE POINTER"); + } if( ! is_index ) { char ach[16]; char stype[32]; @@ -3103,7 +3148,6 @@ parser_move_carefully( const char */*F*/, int /*L*/, sprintf(ach, ".%d", tgt.field->data.rdigits); strcat(dtype, ach); } - error_msg(src.loc, "cannot MOVE '%s' (%s) to '%s' (%s)", name_of(src.field), stype, name_of(tgt.field), dtype); @@ -3137,13 +3181,23 @@ ast_set_pointers( const list<cbl_num_result_t>& tgts, cbl_refer_t src ) { parser_set_pointers(nptr, ptrs.data(), src); } +static void +ast_save_locale( cbl_refer_t refer, int /* token */ ) { + assert( ! refer.addr_of && ! refer.is_reference() ); + if( ! refer.is_pointer() ) { + error_msg(refer.loc, "%s must be USAGE POINTER", refer.name()); + return; + } + cbl_unimplemented("SET identifier-11 TO LOCALE"); +} + void stringify( refer_collection_t *inputs, - cbl_refer_t into, cbl_refer_t pointer, + const cbl_refer_t& into, const cbl_refer_t& pointer, cbl_label_t *on_error = NULL, cbl_label_t *not_error = NULL); -void unstringify( cbl_refer_t& src, refer_list_t *delimited, +void unstringify( const cbl_refer_t& src, refer_list_t *delimited, unstring_into_t * into, cbl_label_t *on_error = NULL, cbl_label_t *not_error = NULL ); @@ -3152,7 +3206,8 @@ static cbl_label_t * implicit_paragraph() { cbl_name_t name; - sprintf(name, "_implicit_paragraph_%zu", symbol_index()); + sprintf(name, "_implicit_paragraph_" HOST_SIZE_T_PRINT_UNSIGNED, + (fmt_size_t)symbol_index()); // Programs have to start with an implicit paragraph return label_add(LblParagraph, name, yylineno); } @@ -3160,12 +3215,14 @@ static cbl_label_t * implicit_section() { cbl_name_t name; - sprintf(name, "_implicit_section_%zu", symbol_index()); + sprintf(name, "_implicit_section_" HOST_SIZE_T_PRINT_UNSIGNED, + (fmt_size_t)symbol_index()); // Programs have to start with an implicit section return label_add(LblSection, name, yylineno); } static void +// cppcheck-suppress constParameterPointer ast_enter_exit_section( cbl_label_t * section ) { auto implicit = section? implicit_paragraph() : NULL; @@ -3220,7 +3277,11 @@ data_division_ready() { const char *name = current.collating_sequence(); if( ! symbols_alphabet_set(PROGRAM, name) ) { - error_msg(yylloc, "no alphabet '%s' defined", name); + if( name ) { + error_msg(yylloc, "no alphabet '%s' defined", name); + } else { + error_msg(yylloc, "no alphabet defined"); + } return false; } } @@ -3229,13 +3290,15 @@ data_division_ready() { static size_t nsymbol = 0; if( (nsymbol = symbols_update(nsymbol, nparse_error == 0)) > 0 ) { if( ! literally_one ) { - literally_one = new_literal("1"); - literally_zero = new_literal("0"); + // Use strdup so cbl_field_t::internalize can free them if need be. + literally_one = new_literal(xstrdup("1")); + literally_zero = new_literal(xstrdup("0")); } } if( nsymbol == 0 || nparse_error > 0 ) { - dbgmsg( "%d errors in DATA DIVISION, compilation ceases", nparse_error ); + dbgmsg( HOST_SIZE_T_PRINT_DEC " errors in DATA DIVISION, compilation ceases", + (fmt_size_t)nparse_error ); return false; } @@ -3244,7 +3307,7 @@ data_division_ready() { static bool -anybody_redefines(cbl_field_t *tree) +anybody_redefines( const cbl_field_t *tree ) { bool retval = false; while(tree) @@ -3254,7 +3317,8 @@ anybody_redefines(cbl_field_t *tree) retval = true; break; } - tree = parent_of(tree); + // cppcheck-suppress [unreadVariable] obviously not true + tree = parent_of(tree); } return retval; } @@ -3301,6 +3365,13 @@ procedure_division_ready( YYLTYPE loc, cbl_field_t *returning, ffi_args_t *ffi_a } } + // Apply ECs from the command line + std::list<exception_turn_t>& exception_turns = current.pending_exceptions(); + for( const auto& exception_turn : exception_turns) { + apply_cdf_turn(exception_turn); + } + exception_turns.clear(); + // Start the Procedure Division. size_t narg = ffi_args? ffi_args->elems.size() : 0; std::vector <cbl_ffi_arg_t> args(narg); @@ -3467,14 +3538,14 @@ file_section_parent_set( cbl_field_t *field ) { field->data.capacity); field->file = file_section_fd; - auto redefined = symbol_redefines(record_area); + const auto redefined = symbol_redefines(record_area); field->parent = redefined? record_area->parent : file->default_record; } return file_section_fd > 0; } void ast_call(const YYLTYPE& loc, cbl_refer_t name, - cbl_refer_t returning, + const cbl_refer_t& returning, size_t narg, cbl_ffi_arg_t args[], cbl_label_t *except, cbl_label_t *not_except, @@ -3522,7 +3593,7 @@ goodnight_gracie() { if( !externals.empty() ) { for( const auto& name : externals ) { - yywarn("%s calls external symbol '%s'", + dbgmsg("%s calls external symbol '%s'", prog->name, name.c_str()); } return false; @@ -3533,20 +3604,23 @@ goodnight_gracie() { return true; } -const char * keyword_str( int token ); - +// false after USE statement, to enter Declarative with EC intact. +static bool statement_cleanup = true; static YYLTYPE current_location; +static void statement_epilog( int token ); + +const char * keyword_str( int token ); + const YYLTYPE& cobol_location() { return current_location; } -static inline YYLTYPE +static inline void location_set( const YYLTYPE& loc ) { - return current_location = loc; + current_location = loc; + gcc_location_set(loc); } -static int prior_statement; - -static size_t statement_begin( const YYLTYPE& loc, int token ); +static void statement_begin( const YYLTYPE& loc, int token ); static void ast_first_statement( const YYLTYPE& loc ) { if( current.is_first_statement( loc ) ) { diff --git a/gcc/cobol/parse_util.h b/gcc/cobol/parse_util.h index e504f46..20847e3 100644 --- a/gcc/cobol/parse_util.h +++ b/gcc/cobol/parse_util.h @@ -271,7 +271,7 @@ function_descr_t *function_descrs_end = function_descrs + COUNT_OF(function_desc class cname_cmp { const char *cname; public: - cname_cmp( const char *cname ) : cname(cname) {} + explicit cname_cmp( const char *cname ) : cname(cname) {} bool operator()( const function_descr_t& descr ) { return strlen(cname) == strlen(descr.cname) && @@ -283,6 +283,15 @@ class cname_cmp { } }; +static int +intrinsic_token_of( const char name[] ) { + auto pdescr = std::find_if( function_descrs, function_descrs_end, + [name]( const function_descr_t& descr ) { + return 0 == strcmp(name, descr.name); + } ); + return pdescr == function_descrs_end? 0 : pdescr->token; +} + /* * For variadic intrinsic functions, ensure all parameters are commensurate. * Return pointer in 1st inconsistent parameter type. @@ -293,8 +302,8 @@ intrinsic_inconsistent_parameter( size_t n, cbl_refer_t *args ) { class commensurate_type { cbl_refer_t first; public: - commensurate_type( const cbl_refer_t& first ) : first(first) {} - bool operator()( cbl_refer_t& arg ) const { + explicit commensurate_type( const cbl_refer_t& first ) : first(first) {} + bool operator()( const cbl_refer_t& arg ) const { return is_numeric(first.field) == is_numeric(arg.field); } }; @@ -348,7 +357,7 @@ intrinsic_invalid_parameter( int token, return token == descr.token; } ); if( p == function_descrs_end ) { - cbl_internal_error( "%s: intrinsic function %s not found", + cbl_internal_error( "%s: intrinsic function %qs not found", __func__, keyword_str(token) ); } diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l index 18d0d82..75b2f3c 100644 --- a/gcc/cobol/scan.l +++ b/gcc/cobol/scan.l @@ -27,9 +27,15 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +%top{ +#include "config.h" +} %{ #include <fstream> // Before cobol-system because it uses poisoned functions #include "cobol-system.h" +#include "coretypes.h" +#include "tree.h" +#undef yy_flex_debug #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" @@ -73,14 +79,19 @@ nonseq (([''][[:alnum:]]+][''])|([""][[:alnum:]]+[""])) INTEGER 0*[1-9][[:digit:]]* INTEGERZ [[:digit:]]+ +NONWORD [^[:alnum:]$_-]+ + SPC [[:space:]]+ OSPC [[:space:]]* +BLANK [[:blank:]]+ +OBLANK [[:blank:]]* EOL \r?\n BLANK_EOL [[:blank:]]*{EOL} BLANK_OEOL [[:blank:]]*{EOL}? +PICTURE [^[:space:]]+ -DOTSEP [.][[:space:]] +DOTSEP [.]+[[:space:]] DOTEOL [[:blank:]]*[.]{BLANK_EOL} SKIP [[:blank:]]*SKIP[123][[:blank:]]*[.]?{BLANK_EOL} @@ -92,9 +103,10 @@ NP P+|(P{COUNT}) UNSIGNED [[:space:]]+UNSIGNED SIGNED [[:space:]]+SIGNED -DBLLONG (LONG-LONG|DOUBLE) -ALNUM [AX9]+ +PREFIX G|N|U|Z + +ALNUM [AX9]+|N+|U+ AX [AX]{COUNT}? B0 [B0/]{COUNT}? @@ -113,7 +125,7 @@ NUMEDCHAR [BPVZ90/,]+{COUNT}? NUMEDCHARS {NUMEDCHAR}([.]?{NUMEDCHAR})* NUMED ([+-]{NUMEDCHARS}+)|({NUMEDCHARS}+[+-]) CURRENCY [A-Zfhijklmoqtuwy\x80-\xFF]{-}[ABCDEGNPRSVXZ] -NUMEDCUR (([.]?[-$0B/Z*+,P9()V+–]|{CURRENCY}+|{COUNT})+([.][$0B/Z*+P9()V+\–])*)+ +NUMEDCUR (([.]?[$0B/Z*+,P9()V+-]|{CURRENCY}+|{COUNT})+([.][$0B/Z*+P9()V+-])*)+ NUMEDITED {NUMED}|{NUMEDCUR} EDITED {ALPHED}|{NUMED}|{NUMEDCUR} @@ -153,7 +165,7 @@ COMMA [,;][[:blank:]]* ISNT (IS{SPC})?NOT -COMMENTARY DATE-COMPILED|DATE-WRITTEN|INSTALLATION|SECURITY +COMMENTARY AUTHOR|DATE-COMPILED|DATE-WRITTEN|INSTALLATION|SECURITY SORT_MERGE SORT(-MERGE)? @@ -167,15 +179,15 @@ SIZE_ERROR (ON[[[:space:]]+)?SIZE[[:space:]]+ERROR VARTYPE NUMERIC|ALPHABETIC|ALPHABETIC_LOWER|ALPHABETIC_UPPER|DBCS|KANJI NAMTYP {NAME}|{VARTYPE} -NL [[:blank:]]*\r?\n[[:blank:]]* +NL [[:blank:]]*{EOL}[[:blank:]]* PUSH_FILE \f?[#]FILE{SPC}PUSH{SPC}[^\f]+\f POP_FILE \f?[#]FILE{SPC}POP\f -LINE_DIRECTIVE [#]line{SPC}[[:alnum:]]+{SPC}[""''].+\n +LINE_DIRECTIVE ^[#]line{SPC}[[:alnum:]]+{SPC}[""''].+\n %x procedure_div ident_state addr_of function classify %x program_id_state comment_entries -%x author_state date_state field_level field_state dot_state +%x date_state field_level field_state dot_state %x numeric_state name_state %x quoted1 quoted2 quoteq %x picture picture_count integer_count @@ -231,30 +243,23 @@ WORKING-STORAGE{SPC}SECTION { yy_push_state(field_state); return WORKING_STORAGE_SECT; } LOCAL-STORAGE{SPC}SECTION { - yy_push_state(field_state); - return LOCAL_STORAGE_SECT; } -WORKING-STORAGE { - return WORKING_STORAGE; } -LOCAL-STORAGE { - return LOCAL_STORAGE; } -SCREEN { - return SCREEN; } + yy_push_state(field_state); + return LOCAL_STORAGE_SECT; } +WORKING-STORAGE { return WORKING_STORAGE; } +LOCAL-STORAGE { return LOCAL_STORAGE; } +SCREEN { return SCREEN; } LINKAGE{SPC}SECTION { yy_push_state(field_state); return LINKAGE_SECT; } -FUNCTION-ID { yy_push_state(ident_state); - yy_push_state(program_id_state); - yy_push_state(name_state); return FUNCTION; } - -PROGRAM-ID { yy_push_state(ident_state); - yy_push_state(program_id_state); - yy_push_state(name_state); return PROGRAM_ID; } +FUNCTION-ID{OSPC}{DOTSEP}? { yy_push_state(ident_state); + yy_push_state(program_id_state); + yy_push_state(name_state); return FUNCTION; } -PROGRAM-ID/{DOTEOL} { yy_push_state(ident_state); - yy_push_state(name_state); - yy_push_state(dot_state); return PROGRAM_ID; } +PROGRAM-ID{OSPC}{DOTSEP}? { yy_push_state(ident_state); + yy_push_state(program_id_state); + yy_push_state(name_state); return PROGRAM_ID; } PROCEDURE{SPC}DIVISION { yy_push_state(procedure_div); return PROCEDURE_DIV; } @@ -265,29 +270,18 @@ PROCEDURE{SPC}DIVISION { yy_push_state(procedure_div); } <ident_state>{ + {BLANK_OEOL} + ID(ENTIFICATION)?{SPC}DIVISION { myless(0); yy_pop_state(); } + (ENVIRONMENT|DATA|PROCEDURE){SPC}DIVISION { + myless(0); yy_pop_state(); } + OPTIONS { myless(0); yy_pop_state(); } + AS{SPC}[""] { yy_push_state(quoted2); return AS; } AS{SPC}[''] { yy_push_state(quoted1); return AS; } IS { pop_return IS; } - OPTIONS { yy_pop_state(); myless(0); } - [[:blank:]]*(ENVIRONMENT|DATA|PROCEDURE)[[:blank:]]+DIVISION/.+\n { - yy_pop_state(); myless(0); } - [[:blank:]]*AUTHOR[[:blank:].]+{EOL}? { - // Might not have an EOL, but stop on one. - yy_push_state(author_state); } - - {DOTEOL} - {COMMENTARY} { BEGIN(comment_entries); } } -<author_state>{ - [[:blank:]]+ - ^{BLANK_EOL} - [^\r\n]+ { yy_pop_state(); - yylval.string = xstrdup(yytext); - } -} - <INITIAL>{ COBOL { return COBOL; } @@ -299,6 +293,15 @@ PROCEDURE{SPC}DIVISION { yy_push_state(procedure_div); yy_push_state(field_state); yy_set_bol(1); myless(0); } + + END{SPC}PROGRAM { yy_push_state(name_state); + return program_level() > 1? + END_SUBPROGRAM : END_PROGRAM; } + + END{SPC}FUNCTION { yy_push_state(name_state); + return program_level() > 1? + END_SUBPROGRAM /*invalid*/ : + END_FUNCTION; } } <INITIAL,procedure_div,cdf_state>{ @@ -321,6 +324,15 @@ CENTER { return typed_name(yytext); } + /* figurative constants that are otherwise matched as names */ + +ZEROE?S?/{OSPC}{DOTSEP} { return ZERO; } +SPACES?/{OSPC}{DOTSEP} { yylval.string = NULL; return SPACES; } +QUOTES?/{OSPC}{DOTSEP} { return QUOTES; } +NULLS?/{OSPC}{DOTSEP} { return NULLS; } +LOW-VALUES?/{OSPC}{DOTSEP} { return LOW_VALUES; } +HIGH-VALUES?/{OSPC}{DOTSEP} { return HIGH_VALUES; } + BINARY { return BINARY; } CLASSIFICATION { return CLASSIFICATION; } CYCLE { return CYCLE; } @@ -371,7 +383,7 @@ ROUNDING { return ROUNDING; } SECONDS { return SECONDS; } SECURE { return SECURE; } SHORT { return SHORT; } -SIGNED { return SIGNED; } +SIGNED { return SIGNED_kw; } STANDARD-BINARY { return STANDARD_BINARY; } STANDARD-DECIMAL { return STANDARD_DECIMAL; } STATEMENT { return STATEMENT; } @@ -391,7 +403,7 @@ TOWARD-LESSER { return TOWARD_LESSER; } TRUNCATION { return TRUNCATION; } UCS-4 { return UCS_4; } UNDERLINE { return UNDERLINE; } -UNSIGNED { return UNSIGNED; } +UNSIGNED { return UNSIGNED_kw; } UTF-16 { return UTF_16; } UTF-8 { return UTF_8; } @@ -427,6 +439,11 @@ STDOUT { return STDOUT; } STDERR { return STDERR; } SYSERR { return STDERR; } +ARGUMENT-NUMBER { return ARGUMENT_NUMBER; } +ARGUMENT-VALUE { return ARGUMENT_VALUE; } +ENVIRONMENT-NAME { return ENVIRONMENT_NAME; } +ENVIRONMENT-VALUE { return ENVIRONMENT_VALUE; } + CANCEL { return CANCEL; } COMMIT { return COMMIT; } COMMON { return COMMON; } @@ -437,16 +454,26 @@ COPY { myless(0); } -EXTEND { return EXTEND;} -INITIALIZE { return INITIALIZE; } -INSPECT { return INSPECT; } -INVOKE { return INVOKE; } -INTRINSIC { return INTRINSIC; } -MERGE { return MERGE; } -UNSTRING { return UNSTRING; } -XML { return XML; } -XMLGENERATE { return XMLGENERATE; } -XMLPARSE { return XMLPARSE; } +ATTRIBUTES { return ATTRIBUTES; } +ELEMENT { return ELEMENT; } +ENCODING { return ENCODING; } +EXTEND { return EXTEND;} +INITIALIZE { return INITIALIZE; } +INSPECT { return INSPECT; } +INTRINSIC { return INTRINSIC; } +INVOKE { return INVOKE; } +MERGE { return MERGE; } +NAMESPACE { return NAMESPACE; } +NAMESPACE-PREFIX { return NAMESPACE_PREFIX; } +NONNUMERIC { return NONNUMERIC; } +PROCESSING { return PROCESSING; } +UNSTRING { return UNSTRING; } +VALIDATING { return VALIDATING; } + +XML{SPC}GENERATE { return XMLGENERATE; } +XML{SPC}PARSE { return XMLPARSE; } +XML-DECLARATION { return XML_DECLARATION; } +END-XML { return END_XML; } ZEROE?S? { return ZERO; } @@ -490,7 +517,6 @@ THAN { return THAN; } TEST { return TEST; } TERMINATE { return TERMINATE; } TALLYING { return TALLYING; } -TALLY { return TALLY; } SYSPUNCH { return SYSPUNCH; } SYSOUT { return SYSOUT; } SYSIN { return SYSIN; } @@ -524,7 +550,7 @@ SECTION{SPC}[+-]?{INTEGERZ}/{OSPC}{DOTSEP} { auto eotext = yytext + yyleng; auto p = std::find_if(yytext, eotext, fisspace); p = std::find_if(p, eotext, nonspace); - yylval.string = p; + yylval.string = xstrdup(p); return SECTION; } @@ -546,7 +572,7 @@ REVERSED { return REVERSED; } RETURN { return RETURN; } RESTRICTED { return RESTRICTED; } -RESUME { +RESUME { // RESUME is ISO syntax, not IBM. if( ! dialect_ibm() ) return RESUME; yylval.string = xstrdup(yytext); return typed_name(yytext); @@ -736,7 +762,9 @@ EVERY { return EVERY; } ERROR { return ERROR; } EVALUATE { return EVALUATE; } -EQUALS? { return '='; } +EQUALS? { ydflval.string = yylval.string = xstrdup(yytext); + return '='; } + ENVIRONMENT[[:blank:]]+DIVISION { return ENVIRONMENT_DIV; } ENTRY { return ENTRY; } @@ -787,7 +815,11 @@ DEPENDING { return DEPENDING; } DELIMITER { return DELIMITER; } DELETE { return DELETE; } -DEFAULT { return DEFAULT; } + +DEFAULT { yylval.number = 'D'; return DEFAULT; } +SYSTEM-DEFAULT { yylval.number = 'S'; return DEFAULT; } +USER-DEFAULT { yylval.number = 'U'; return DEFAULT; } + DECLARATIVES { return DECLARATIVES; } DECIMAL-POINT { return DECIMAL_POINT; } DEBUGGING { return DEBUGGING; } @@ -834,7 +866,7 @@ CALL { return CALL; } BY { return BY; } BOTTOM { return BOTTOM; } BEFORE { return BEFORE; } -BLOCK { return BLOCK; } +BLOCK { return BLOCK_kw; } BACKWARD { return BACKWARD; } AT { return AT; } @@ -851,8 +883,9 @@ ANUM { return ANUM; } ALTERNATE { return ALTERNATE; } ALTER { return ALTER; } ALSO { return ALSO; } -ALPHABET { return ALPHABET; } -ALPHABETIC { return ALPHABETIC; } + +ALPHABET { return ALPHABET; } +ALPHABETIC { return ALPHABETIC; } ALPHABETIC-LOWER { return ALPHABETIC_LOWER; } ALPHABETIC-UPPER { return ALPHABETIC_UPPER; } ALPHANUMERIC { return ALPHANUMERIC; } @@ -869,11 +902,7 @@ ACCESS { return ACCESS; } ACCEPT { return ACCEPT; } DELETE { return DELETE; } -EJECT{DOTEOL}? { - if( ! dialect_ibm() ) { - dialect_error(yylloc, "EJECT is not ISO syntax,", "ibm"); - } - } +EJECT{DOTEOL}? { dialect_ok(yylloc, IbmEjectE, "EJECT"); } INSERTT { return INSERTT; } LABEL { return LABEL; } PROCESS { return PROCESS; } @@ -950,7 +979,9 @@ USE({SPC}FOR)? { return USE; } return NUMSTR; } - PIC(TURE)?({SPC}IS)?[[:space:]]{BLANK_OEOL} { + PIC(TURE)?({SPC}IS)?{SPC}{PICTURE} { + auto pos = validate_picture(); + myless(pos); yy_push_state(picture); return PIC; } ANY { return ANY; } @@ -963,31 +994,28 @@ USE({SPC}FOR)? { return USE; } COMP(UTATIONAL)?-X { return ucomputable(FldNumericBin5, 0xFF); } COMP(UTATIONAL)?-6 { return ucomputable(FldPacked, 0); } COMP(UTATIONAL)?-5 { return ucomputable(FldNumericBin5, 0); } - COMP(UTATIONAL)?-4 { return scomputable(FldNumericBinary, 0); } + COMP(UTATIONAL)?-4 { return ucomputable(FldNumericBinary, 0); } COMP(UTATIONAL)?-3 { return PACKED_DECIMAL; } COMP(UTATIONAL)?-2 { return ucomputable(FldFloat, 8); } COMP(UTATIONAL)?-1 { return ucomputable(FldFloat, 4); } COMP(UTATIONAL)? { return ucomputable(FldNumericBinary, 0); } - BINARY { return scomputable(FldNumericBinary, 0); } - - BINARY-CHAR{SIGNED} { return scomputable(FldNumericBin5, 1); } - BINARY-CHAR{UNSIGNED} { return ucomputable(FldNumericBin5, 1); } - BINARY-CHAR { return scomputable(FldNumericBin5, 1); } - BINARY-SHORT{SIGNED} { return scomputable(FldNumericBin5, 2); } - BINARY-SHORT{UNSIGNED} { return ucomputable(FldNumericBin5, 2); } - BINARY-SHORT { return scomputable(FldNumericBin5, 2); } - BINARY-LONG{SIGNED} { return scomputable(FldNumericBin5, 4); } - BINARY-LONG{UNSIGNED} { return ucomputable(FldNumericBin5, 4); } - BINARY-LONG { return scomputable(FldNumericBin5, 4); } - BINARY-{DBLLONG}{SIGNED} { return scomputable(FldNumericBin5, 8); } - BINARY-{DBLLONG}{UNSIGNED} { return ucomputable(FldNumericBin5, 8); } - BINARY-{DBLLONG} { return scomputable(FldNumericBin5, 8); } + BINARY { return ucomputable(FldNumericBinary, 0); } + + BINARY-CHAR { return bcomputable(FldNumericBin5, 1); } + BINARY-SHORT { return bcomputable(FldNumericBin5, 2); } + BINARY-LONG { return bcomputable(FldNumericBin5, 4); } + BINARY-DOUBLE { return bcomputable(FldNumericBin5, 8); } + BINARY-LONG-LONG { dialect_ok(yylloc, MfBinaryLongLong, "BINARY-LONG-LONG"); + return bcomputable(FldNumericBin5, 8); + } + BIT { not_implemented("USAGE type: BIT"); return BIT; } + FLOAT-BINARY-32 { return ucomputable(FldFloat, 4); } FLOAT-BINARY-64 { return ucomputable(FldFloat, 8); } FLOAT-BINARY-128 { return ucomputable(FldFloat, 16); } - FLOAT-DECIMAL-(16|34) { not_implemented("USAGE type: FLOAT_DECIMAL"); + FLOAT-DECIMAL-(16|34) { not_implemented("USAGE type: %<FLOAT_DECIMAL%>"); return FLOAT_DECIMAL; // causes syntax error } /* 21) The representation and length of a data item described with USAGE @@ -997,6 +1025,9 @@ USE({SPC}FOR)? { return USE; } FLOAT-LONG { return ucomputable(FldFloat, 8); } FLOAT-SHORT { return ucomputable(FldFloat, 4); } + SIGNED { return SIGNED_kw; } + UNSIGNED { return UNSIGNED_kw; } + INDEX { return INDEX; } MESSAGE-TAG { not_implemented("USAGE type: MESSAGE-TAG"); } NATIONAL { not_implemented("USAGE type: NATIONAL"); @@ -1009,9 +1040,8 @@ USE({SPC}FOR)? { return USE; } PROGRAM-POINTER { yylval.field_attr = prog_ptr_e; return POINTER; } POINTER { yylval.field_attr = none_e; return POINTER; } - PROCEDURE-POINTER { if( dialect_gcc() ) { - error_msg(yylloc, "%s requires -dialect ibm or mf", yytext); - } + PROCEDURE-POINTER { + dialect_ok(yylloc, IbmProcedurePointer, yytext); yylval.field_attr = prog_ptr_e; return POINTER; // return it anyway } @@ -1039,7 +1069,7 @@ USE({SPC}FOR)? { return USE; } AS { return AS; } ASCENDING { return ASCENDING; } BLANK { return BLANK; } - BLOCK { return BLOCK; } + BLOCK { return BLOCK_kw; } BY { return BY; } BYTE-LENGTH { return BYTE_LENGTH; } CHARACTER { return CHARACTER; } @@ -1051,13 +1081,11 @@ USE({SPC}FOR)? { return USE; } DEPENDING { return DEPENDING; } DESCENDING { return DESCENDING; } DISPLAY { return DISPLAY; } - EJECT{DOTEOL}? { - if( ! dialect_ibm() ) { - dialect_error(yylloc, "EJECT is not ISO syntax,", "ibm"); - } - auto len = yyleng - 1; - if( yytext[len] == '\f' ) myless(--len); - } + EJECT{DOTEOL}? { + dialect_ok(yylloc, IbmEjectE, "EJECT"); + auto len = yyleng - 1; + if( yytext[len] == '\f' ) myless(--len); + } EXTERNAL { return EXTERNAL; } FALSE { return FALSE_kw; } FROM { return FROM; } @@ -1111,22 +1139,26 @@ USE({SPC}FOR)? { return USE; } FD/[[:blank:]]+ { parsing.need_level(false); return FD; } SD/[[:blank:]]+ { parsing.need_level(false); return SD; } - {NAME} { // NAME here is never a token name + {NAME} { // NAME here might be a token alias + int token; + if( 0 != (token = binary_integer_usage(yytext)) ) { + return token; + } if( is_integer_token() ) return numstr_of(yytext); ydflval.string = yylval.string = xstrdup(yytext); - auto token = typed_name(yytext); + token = typed_name(yytext); return token == NAME88? NAME : token; } - Z?[''] { yylval.literal.set_prefix(yytext, yyleng-1); + {PREFIX}?[''] { yylval.literal.set_prefix(yytext, yyleng-1); yy_push_state(quoted1); } - Z?[""] { yylval.literal.set_prefix(yytext, yyleng-1); + {PREFIX}?[""] { yylval.literal.set_prefix(yytext, yyleng-1); yy_push_state(quoted2); } N?X/{hexseq} { yylval.literal.set_prefix(yytext, yyleng); yy_push_state(hex_state); } N?X{nonseq} { dbgmsg("invalid hexadecimal value: %s", yytext); return NO_CONDITION; } - [[:blank:]]*\r?\n {} + [[:blank:]]*{EOL} {} WORKING-STORAGE{SPC}SECTION { return WORKING_STORAGE_SECT; } LOCAL-STORAGE{SPC}SECTION { return LOCAL_STORAGE_SECT; } @@ -1196,7 +1228,12 @@ USE({SPC}FOR)? { return USE; } {NP}V?/[,.]? { yylval.number = ndigit(yyleng); return picset(PIC_P); } {N9}*V/{N9}* { yylval.number = ndigit(yyleng - 1); return picset(NINEV); } {N9}/{N9}*[,.]? { yylval.number = ndigit(yyleng); return picset(NINES); } - P+/[,.]?\r?\n { yylval.number = yyleng; return picset(PIC_P); } + P+/[,.]?{EOL} { yylval.number = yyleng; return picset(PIC_P); } + + 1{1,31}/({COUNT}|[(]{NAME}[)]) { + yy_push_state(picture_count); + yylval.string = xstrdup(yytext); return picset(ONES); } + 1{1,31} { yylval.string = xstrdup(yytext); return picset(ONES); } {ALNUM}/{COUNT}({ALNUM}{COUNT}?)+ { yy_push_state(picture_count); @@ -1260,12 +1297,12 @@ USE({SPC}FOR)? { return USE; } } /* CDF REPLACING needs quotes to distinquish strings from identifiers. */ - Z?['']{STRING1}[''] { auto *s = xstrdup(yytext); + {PREFIX}?['']{STRING1}[''] { auto *s = xstrdup(yytext); std::replace(s, s + strlen(s), '\'', '"'); ydflval.string = s; update_location_col(s); return LITERAL; } - Z?[""]{STRING}[""] { ydflval.string = xstrdup(yytext); + {PREFIX}?[""]{STRING}[""] { ydflval.string = xstrdup(yytext); update_location_col(yytext); return LITERAL; } [=]{4} { static char nullstring[] = ""; @@ -1290,7 +1327,7 @@ USE({SPC}FOR)? { return USE; } [""]{SPC}[&]{SPC}[""''] { if( yytext[yyleng - 1] == '\'' ) BEGIN(quoted1); } - [""]-{OSPC}(\r?\n{OSPC})+[""] /* continue ... */ + [""]-{OSPC}({EOL}{OSPC})+[""] /* continue ... */ [""] { char *s = xstrdup(tmpstring? tmpstring : "\0"); yylval.literal.set_data(strlen(s), s); @@ -1307,7 +1344,7 @@ USE({SPC}FOR)? { return USE; } ['']{SPC}[&]{SPC}[""''] { if( yytext[yyleng - 1] == '"' ) BEGIN(quoted2); } - ['']-{OSPC}(\r?\n{OSPC})+[''] /* continue ... */ + ['']-{OSPC}({EOL}{OSPC})+[''] /* continue ... */ [''] { char *s = xstrdup(tmpstring? tmpstring : "\0"); yylval.literal.set_data(strlen(s), s); @@ -1355,46 +1392,36 @@ USE({SPC}FOR)? { return USE; } } <program_id_state>{ - ^[[:blank:]]+ - ^{BLANK_EOL} + {BLANK_OEOL} (IS)?[[:space:]] + AS/{SPC} { myless(0); yy_pop_state(); } /* => ident_state */ - COMMON/[.]|{SPC}[[:alnum:].] { return COMMON; } - INITIAL/[.]|{SPC}[[:alnum:].] { return INITIAL_kw; } - RECURSIVE { return RECURSIVE; } - PROGRAM/[.]|{SPC}[[:alnum:].] { return PROGRAM_kw; } - - INITIAL { pop_return INITIAL_kw; } - COMMON { pop_return COMMON; } - PROGRAM { pop_return PROGRAM; } + INITIAL { return INITIAL_kw; } + COMMON { return COMMON; } + RECURSIVE { return RECURSIVE; } + PROGRAM { return PROGRAM_kw; } - AS/{SPC} { myless(0); yy_pop_state(); } /* => ident_state */ - [[:blank:]]*{DOTSEP}[[:blank:].]+{EOL} { pop_return '.'; } - {DOTEOL} { pop_return '.'; } + {DOTSEP} { pop_return '.'; } } -<name_state>{ - ^[[:blank:]]+ - ^{BLANK_EOL} - {NAME}/{OSPC}[.] { yy_pop_state(); - yylval.string = xstrdup(yytext); return NAME; } - {NAME} { yy_pop_state(); - yylval.string = xstrdup(yytext); return NAME; } - - Z?[''] { yylval.literal.set_prefix(yytext, yyleng-1); - yy_push_state(quoted1); } - Z?[""] { yylval.literal.set_prefix(yytext, yyleng-1); - yy_push_state(quoted2); } +<name_state>{ /* Either pop from here, or let the quoted state pop */ + {BLANK_OEOL} - [.]/[[:blank:]]+. { return *yytext; } + {NAME} { yy_pop_state(); + yylval.string = xstrdup(yytext); + return NAME; + } + {PREFIX}?[''] { yylval.literal.set_prefix(yytext, yyleng-1); + BEGIN(quoted1); } + {PREFIX}?[""] { yylval.literal.set_prefix(yytext, yyleng-1); + BEGIN(quoted2); } - [[:blank:]]*{DOTSEP}[[:blank:].]+{EOL} { - yy_pop_state(); myless(0); } - {DOTEOL} { yy_pop_state(); myless(0); } + . { myless(0); yy_pop_state(); + /* Should not happen for valid inputs. */ } } <dot_state>{ [[:blank:]]*[.][[:blank:].]+{EOL} { pop_return '.'; } - [[:blank:]]*[.] { pop_return '.'; } + [[:blank:]]*[.]+ { pop_return '.'; } } <date_state>{ @@ -1426,11 +1453,11 @@ USE({SPC}FOR)? { return USE; } BX/{hexseq} { yylval.numstr.radix = hexadecimal_e; yy_push_state(numstr_state); } - Z?[''] { yylval.literal.set_prefix(yytext, yyleng-1); + {PREFIX}?[''] { yylval.literal.set_prefix(yytext, yyleng-1); yy_push_state(quoted1); } - Z?[""] { yylval.literal.set_prefix(yytext, yyleng-1); + {PREFIX}?[""] { yylval.literal.set_prefix(yytext, yyleng-1); yy_push_state(quoted2); } - Z?[""]/{STRING}[""] { yylval.literal.set_prefix(yytext, yyleng-1); + {PREFIX}?[""]/{STRING}[""] { yylval.literal.set_prefix(yytext, yyleng-1); yy_push_state(quoted2); } {INTEGERZ}/[[:punct:]][[:space:]]{BLANK_OEOL} { return numstr_of(yytext); } @@ -1472,22 +1499,27 @@ USE({SPC}FOR)? { return USE; } <cdf_state,procedure_div>{ (IS{SPC})?"<" { return '<'; } (IS{SPC})?"<=" { return LE; } - (IS{SPC})?"=" { return '='; } + (IS{SPC})?"=" { static char eq[] = "="; + ydflval.string = yylval.string = eq; + return EQ; } (IS{SPC})?"<>" { return NE; } (IS{SPC})?">=" { return GE; } (IS{SPC})?">" { return '>'; } - {LESS_THAN} { return '<'; } + {LESS_THAN} { return '<'; } {LESS_THAN}{SPC}{OR_EQUAL}/[[:space:]] { return LE; } - (IS{SPC})?EQUALS?({SPC}TO)?/[[:space:]] { return '='; } + (IS{SPC})?EQUALS?({SPC}TO)?/[[:space:]] { + static char eq[] = "EQUAL"; + ydflval.string = yylval.string = eq; + return EQ; } {GREATER_THAN}{SPC}{OR_EQUAL}/[[:space:]] { return GE; } - {GREATER_THAN} { return '>'; } + {GREATER_THAN} { return '>'; } - {ISNT}{SPC}">=" { return '<'; } - {ISNT}{SPC}">" { return LE; } - {ISNT}{SPC}"=" { return NE; } - {ISNT}{SPC}"<" { return GE; } - {ISNT}{SPC}"<=" { return '>'; } + {ISNT}{OSPC}">=" { verify_ws(yytext[yyleng - 3]); return '<'; } + {ISNT}{OSPC}">" { verify_ws(yytext[yyleng - 2]); return LE; } + {ISNT}{OSPC}"=" { verify_ws(yytext[yyleng - 2]); return NE; } + {ISNT}{OSPC}"<" { verify_ws(yytext[yyleng - 2]); return GE; } + {ISNT}{OSPC}"<=" { verify_ws(yytext[yyleng - 3]); return '>'; } {ISNT}{SPC}GREATER{SPC}(THAN)?{SPC}{OR_EQUAL}/[[:space:]] { return '<'; } {ISNT}{SPC}GREATER{SPC}(THAN)? { return LE; } @@ -1495,13 +1527,35 @@ USE({SPC}FOR)? { return USE; } {ISNT}{SPC}LESS{SPC}(THAN)? { return GE; } {ISNT}{SPC}LESS{SPC}(THAN)?{SPC}{OR_EQUAL}/[[:space:]] { return '>'; } - [*]{2}{SPC}[+] { return POW; } - "**" { return POW; } + [*]{2} { return POW; } + + /* + * "A boolean operator specifies the type of boolean operation to be performed + * on one or two operands, for a unary operator or binary operator, + * respectively." + * Binary boolean operators + * B-AND B-OR B-XOR + * Unary boolean operator + * B-NOT + * Boolean shift operators + * B-SHIFT-L B-SHIFT-LC B-SHIFT-R B-SHIFT-RC + */ + +B-AND +B-OR +B-XOR +B-NOT +B-SHIFT-L +B-SHIFT-LC +B-SHIFT-R +B-SHIFT-RC + } <procedure_div>{ (ID|IDENTIFICATION|ENVIRONMENT|DATA|PROCEDURE){SPC}DIVISION { - myless(0); yy_pop_state(); } + myless(0); BEGIN(INITIAL); } + PROGRAM-ID{OSPC}{DOTSEP} { myless(0); BEGIN(INITIAL); } EXIT{SPC}/(PROGRAM|SECTION|PARAGRAPH|PERFORM) { return EXIT; } @@ -1540,9 +1594,9 @@ USE({SPC}FOR)? { return USE; } NOT{SPC}(ON{SPC})?EXCEPTION { yylval.number = NOT; return EXCEPTION; } - (ON{SPC})?OVERFLOW { yylval.number = OVERFLOW; return OVERFLOW; } + (ON{SPC})?OVERFLOW { yylval.number = OVERFLOW_kw; return OVERFLOW_kw; } NOT{SPC}(ON{SPC})?OVERFLOW { - yylval.number = NOT; return OVERFLOW; } + yylval.number = NOT; return OVERFLOW_kw; } (AT{SPC})?END/[[:space:]] { yylval.number = END; return END; } @@ -1568,6 +1622,7 @@ USE({SPC}FOR)? { return USE; } DELIMITER { return DELIMITER; } ENVIRONMENT { return ENVIRONMENT; } + /* After name state, pop out of procedure_div state. */ END{SPC}PROGRAM { yy_push_state(name_state); return program_level() > 1? END_SUBPROGRAM : END_PROGRAM; } @@ -1594,28 +1649,16 @@ USE({SPC}FOR)? { return USE; } FUNCTION { yy_push_state(function); return FUNCTION; } - SECTION{OSPC}[.]{SPC}/USE[[:space:]] { yylval.string = NULL; return SECTION; } + SECTION{OSPC}[.]+{SPC}/USE[[:space:]] { yylval.string = NULL; return SECTION; } - {NAME}{OSPC}[.]({SPC}(EJECT|SKIP[123]))*{SPC}EXIT{OSPC}/{DOTSEP} { + [.]+({SPC}(EJECT|SKIP[123]))*{SPC}EXIT{OSPC}/{DOTSEP} { // EXIT format-1 is a "continue" statement - yylval.string = xstrdup(yytext); - auto p = strchr(yylval.string, '.'); - assert(p); - assert( ISSPACE(p[1]) ); - *p = '\0'; - while( p > yylval.string && ISSPACE(p[-1]) ) { - *--p = '\0'; - } - - int token = keyword_tok(yylval.string); - if( token ) return token; - if( is_integer_token() ) return numstr_of(yylval.string); - return typed_name(yylval.string); } {NAME}/{OSPC}{DOTSEP} { assert(YY_START == procedure_div); - int token = keyword_tok(yytext); - if( token ) return token; + int token; + if( 0 != (token = binary_integer_usage(yytext)) ) return token; + if( 0 != (token = keyword_tok(yytext)) ) return token; if( is_integer_token() ) return numstr_of(yytext); ydflval.string = yylval.string = xstrdup(yytext); @@ -1643,16 +1686,17 @@ USE({SPC}FOR)? { return USE; } p += 2; while( ISSPACE(*p) ) p++; cbl_name_t name2; - std::transform( p, p + sizeof(name2), name2, - []( char ch ) { - switch(ch) { - case '-': - case '_': return ch; - default: - if( ISALNUM(ch) ) return ch; - } - return '\0'; - } ); + const char *pend = p + sizeof(name2); + char *pout = name2; + while( p < pend ) { + char ch = *p++; + if( ISALNUM(ch) || ch == '-' || ch == '_' ) { + *pout++ = ch; + } else { + *pout++ = '\0'; + break; + } + } symbol_elem_t *e = symbol_file(PROGRAM, name2); /* * For NAME IN FILENAME, we want the parser to handle it. @@ -1755,8 +1799,8 @@ USE({SPC}FOR)? { return USE; } 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 = xstrdup(f->data.initial); + type = date_time_fmt(f->data.original()); + yylval.string = xstrdup(f->data.original()); } } else { yylval.string = xstrdup(yytext); @@ -1777,126 +1821,128 @@ USE({SPC}FOR)? { return USE; } } <function>{ - - - ABS{OSPC}/[(]? { pop_return ABS; } - ACOS{OSPC}/[(]? { pop_return ACOS; } - ANNUITY{OSPC}/[(]? { pop_return ANNUITY; } - ASIN{OSPC}/[(]? { pop_return ASIN; } - ATAN{OSPC}/[(]? { pop_return ATAN; } - BASECONVERT{OSPC}/[(]? { pop_return BASECONVERT; } - BIT-OF{OSPC}/[(]? { pop_return BIT_OF; } - BIT-TO-CHAR{OSPC}/[(]? { pop_return BIT_TO_CHAR; } - BOOLEAN-OF-INTEGER{OSPC}/[(]? { pop_return BOOLEAN_OF_INTEGER; } - BYTE-LENGTH{OSPC}/[(]? { pop_return BYTE_LENGTH; } - CHAR-NATIONAL{OSPC}/[(]? { pop_return CHAR_NATIONAL; } - CHAR{OSPC}/[(]? { pop_return CHAR; } - COMBINED-DATETIME{OSPC}/[(]? { pop_return COMBINED_DATETIME; } - CONCAT{OSPC}/[(]? { pop_return CONCAT; } - CONTENT-LENGTH{OSPC}/[(]? { pop_return NO_CONDITION; /* GNU only*/ } - CONTENT-OF{OSPC}/[(]? { pop_return NO_CONDITION; /* GNU only*/ } - CONVERT{OSPC}/[(]? { pop_return CONVERT; } - COS{OSPC}/[(]? { pop_return COS; } - CURRENCY-SYBOL{OSPC}/[(]? { pop_return NO_CONDITION; /* GNU only*/ } - CURRENT-DATE{OSPC}/[(]? { pop_return CURRENT_DATE; } - DATE-OF-INTEGER{OSPC}/[(]? { pop_return DATE_OF_INTEGER; } - DATE-TO-YYYYMMDD{OSPC}/[(]? { pop_return DATE_TO_YYYYMMDD; } - DAY-OF-INTEGER{OSPC}/[(]? { pop_return DAY_OF_INTEGER; } - DAY-TO-YYYYDDD{OSPC}/[(]? { pop_return DAY_TO_YYYYDDD; } - DISPLAY-OF{OSPC}/[(]? { pop_return DISPLAY_OF; } - E{OSPC}/[(]? { pop_return E; } - - EXCEPTION-FILE-N{OSPC}/[(]? { pop_return EXCEPTION_FILE_N; } - EXCEPTION-FILE{OSPC}/[(]? { pop_return EXCEPTION_FILE; } - EXCEPTION-LOCATION-N{OSPC}/[(]? { pop_return EXCEPTION_LOCATION_N; } - EXCEPTION-LOCATION{OSPC}/[(]? { pop_return EXCEPTION_LOCATION; } - EXCEPTION-STATEMENT{OSPC}/[(]? { pop_return EXCEPTION_STATEMENT; } - EXCEPTION-STATUS{OSPC}/[(]? { pop_return EXCEPTION_STATUS; } - - EXP{OSPC}/[(]? { pop_return EXP; } - EXP10{OSPC}/[(]? { pop_return EXP10; } - FACTORIAL{OSPC}/[(]? { pop_return FACTORIAL; } - FIND-STRING{OSPC}/[(]? { pop_return FIND_STRING; } - - FORMATTED-CURRENT-DATE{OSPC}/[(]? { BEGIN(datetime_fmt); return FORMATTED_CURRENT_DATE; } - FORMATTED-DATE{OSPC}/[(]? { BEGIN(datetime_fmt); return FORMATTED_DATE; } - FORMATTED-DATETIME{OSPC}/[(]? { BEGIN(datetime_fmt); return FORMATTED_DATETIME; } - FORMATTED-TIME{OSPC}/[(]? { BEGIN(datetime_fmt); return FORMATTED_TIME; } - FRACTION-PART{OSPC}/[(]? { pop_return FRACTION_PART; } - - HEX-OF{OSPC}/[(]? { pop_return HEX_OF; } - HEX-TO-CHAR{OSPC}/[(]? { pop_return HEX_TO_CHAR; } - HIGHEST-ALGEBRAIC{OSPC}/[(]? { pop_return HIGHEST_ALGEBRAIC; } - - INTEGER{OSPC}/[(]? { pop_return INTEGER; } - INTEGER-OF-BOOLEAN{OSPC}/[(]? { pop_return INTEGER_OF_BOOLEAN; } - INTEGER-OF-DATE{OSPC}/[(]? { pop_return INTEGER_OF_DATE; } - INTEGER-OF-DAY{OSPC}/[(]? { pop_return INTEGER_OF_DAY; } - INTEGER-OF-FORMATTED-DATE{OSPC}/[(]? { BEGIN(datetime_fmt); return INTEGER_OF_FORMATTED_DATE; } - INTEGER-PART{OSPC}/[(]? { pop_return INTEGER_PART; } - LENGTH{OSPC}/[(]? { pop_return LENGTH; } - LOCALE-COMPARE{OSPC}/[(]? { pop_return LOCALE_COMPARE; } - LOCALE-DATE{OSPC}/[(]? { pop_return LOCALE_DATE; } - LOCALE-TIME{OSPC}/[(]? { pop_return LOCALE_TIME; } - LOCALE-TIME-FROM-SECONDS{OSPC}/[(]? { pop_return LOCALE_TIME_FROM_SECONDS; } - LOG{OSPC}/[(]? { pop_return LOG; } - LOG10{OSPC}/[(]? { pop_return LOG10; } - LOWER-CASE{OSPC}/[(]? { pop_return LOWER_CASE; } - LOWEST-ALGEBRAIC{OSPC}/[(]? { pop_return LOWEST_ALGEBRAIC; } - MAX{OSPC}/[(]? { pop_return MAXX; } - MEAN{OSPC}/[(]? { pop_return MEAN; } - MEDIAN{OSPC}/[(]? { pop_return MEDIAN; } - MIDRANGE{OSPC}/[(]? { pop_return MIDRANGE; } - MIN{OSPC}/[(]? { pop_return MINN; } - MOD{OSPC}/[(]? { pop_return MOD; } - MODULE-NAME{OSPC}/[(]? { pop_return MODULE_NAME; } - NATIONAL-OF{OSPC}/[(]? { pop_return NATIONAL_OF; } - NUMVAL{OSPC}/[(]? { pop_return NUMVAL; } - NUMVAL-C{OSPC}/[(]? { pop_return NUMVAL_C; } - NUMVAL-F{OSPC}/[(]? { pop_return NUMVAL_F; } - ORD{OSPC}/[(]? { pop_return ORD; } - ORD-MAX{OSPC}/[(]? { pop_return ORD_MAX; } - ORD-MIN{OSPC}/[(]? { pop_return ORD_MIN; } - PI{OSPC}/[(]? { pop_return PI; } - PRESENT-VALUE{OSPC}/[(]? { pop_return PRESENT_VALUE; } + ABS/{NONWORD} { pop_return ABS; } + ACOS/{NONWORD} { pop_return ACOS; } + ANNUITY/{NONWORD} { pop_return ANNUITY; } + ASIN/{NONWORD} { pop_return ASIN; } + ATAN/{NONWORD} { pop_return ATAN; } + BASECONVERT/{NONWORD} { pop_return BASECONVERT; } + BIT-OF/{NONWORD} { pop_return BIT_OF; } + BIT-TO-CHAR/{NONWORD} { pop_return BIT_TO_CHAR; } + BOOLEAN-OF-INTEGER/{NONWORD} { pop_return BOOLEAN_OF_INTEGER; } + BYTE-LENGTH/{NONWORD} { pop_return BYTE_LENGTH; } + CHAR-NATIONAL/{NONWORD} { pop_return CHAR_NATIONAL; } + CHAR/{NONWORD} { pop_return CHAR; } + COMBINED-DATETIME/{NONWORD} { pop_return COMBINED_DATETIME; } + CONCAT/{NONWORD} { pop_return CONCAT; } + CONTENT-LENGTH/{NONWORD} { pop_return NO_CONDITION; /* GNU only*/ } + CONTENT-OF/{NONWORD} { pop_return NO_CONDITION; /* GNU only*/ } + CONVERT/{NONWORD} { pop_return CONVERT; } + COS/{NONWORD} { pop_return COS; } + CURRENCY-SYBOL/{NONWORD} { pop_return NO_CONDITION; /* GNU only*/ } + CURRENT-DATE/{NONWORD} { pop_return CURRENT_DATE; } + DATE-OF-INTEGER/{NONWORD} { pop_return DATE_OF_INTEGER; } + DATE-TO-YYYYMMDD/{NONWORD} { pop_return DATE_TO_YYYYMMDD; } + DAY-OF-INTEGER/{NONWORD} { pop_return DAY_OF_INTEGER; } + DAY-TO-YYYYDDD/{NONWORD} { pop_return DAY_TO_YYYYDDD; } + DISPLAY-OF/{NONWORD} { pop_return DISPLAY_OF; } + E/{NONWORD} { pop_return E; } + + EXCEPTION-FILE-N/{NONWORD} { pop_return EXCEPTION_FILE_N; } + EXCEPTION-FILE/{NONWORD} { pop_return EXCEPTION_FILE; } + EXCEPTION-LOCATION-N/{NONWORD} { pop_return EXCEPTION_LOCATION_N; } + EXCEPTION-LOCATION/{NONWORD} { pop_return EXCEPTION_LOCATION; } + EXCEPTION-STATEMENT/{NONWORD} { pop_return EXCEPTION_STATEMENT; } + EXCEPTION-STATUS/{NONWORD} { pop_return EXCEPTION_STATUS; } + + EXP/{NONWORD} { pop_return EXP; } + EXP10/{NONWORD} { pop_return EXP10; } + FACTORIAL/{NONWORD} { pop_return FACTORIAL; } + FIND-STRING/{NONWORD} { pop_return FIND_STRING; } + + FORMATTED-CURRENT-DATE/{NONWORD} { BEGIN(datetime_fmt); + return FORMATTED_CURRENT_DATE; } + FORMATTED-DATE/{NONWORD} { BEGIN(datetime_fmt); return FORMATTED_DATE; } + FORMATTED-DATETIME/{NONWORD} { BEGIN(datetime_fmt); return FORMATTED_DATETIME; } + FORMATTED-TIME/{NONWORD} { BEGIN(datetime_fmt); return FORMATTED_TIME; } + FRACTION-PART/{NONWORD} { pop_return FRACTION_PART; } + + HEX-OF/{NONWORD} { pop_return HEX_OF; } + HEX-TO-CHAR/{NONWORD} { pop_return HEX_TO_CHAR; } + HIGHEST-ALGEBRAIC/{NONWORD} { pop_return HIGHEST_ALGEBRAIC; } + + INTEGER/{NONWORD} { pop_return INTEGER; } + INTEGER-OF-BOOLEAN/{NONWORD} { pop_return INTEGER_OF_BOOLEAN; } + INTEGER-OF-DATE/{NONWORD} { pop_return INTEGER_OF_DATE; } + INTEGER-OF-DAY/{NONWORD} { pop_return INTEGER_OF_DAY; } + INTEGER-OF-FORMATTED-DATE/{NONWORD} { BEGIN(datetime_fmt); + return INTEGER_OF_FORMATTED_DATE; } + INTEGER-PART/{NONWORD} { pop_return INTEGER_PART; } + LENGTH/{NONWORD} { pop_return LENGTH; } + LOCALE-COMPARE/{NONWORD} { pop_return LOCALE_COMPARE; } + LOCALE-DATE/{NONWORD} { pop_return LOCALE_DATE; } + LOCALE-TIME/{NONWORD} { pop_return LOCALE_TIME; } + LOCALE-TIME-FROM-SECONDS/{NONWORD} { pop_return LOCALE_TIME_FROM_SECONDS; } + LOG/{NONWORD} { pop_return LOG; } + LOG10/{NONWORD} { pop_return LOG10; } + LOWER-CASE/{NONWORD} { pop_return LOWER_CASE; } + LOWEST-ALGEBRAIC/{NONWORD} { pop_return LOWEST_ALGEBRAIC; } + MAX/{NONWORD} { pop_return MAXX; } + MEAN/{NONWORD} { pop_return MEAN; } + MEDIAN/{NONWORD} { pop_return MEDIAN; } + MIDRANGE/{NONWORD} { pop_return MIDRANGE; } + MIN/{NONWORD} { pop_return MINN; } + MOD/{NONWORD} { pop_return MOD; } + MODULE-NAME/{NONWORD} { pop_return MODULE_NAME; } + NATIONAL-OF/{NONWORD} { pop_return NATIONAL_OF; } + NUMVAL/{NONWORD} { pop_return NUMVAL; } + NUMVAL-C/{NONWORD} { pop_return NUMVAL_C; } + NUMVAL-F/{NONWORD} { pop_return NUMVAL_F; } + ORD/{NONWORD} { pop_return ORD; } + ORD-MAX/{NONWORD} { pop_return ORD_MAX; } + ORD-MIN/{NONWORD} { pop_return ORD_MIN; } + PI/{NONWORD} { pop_return PI; } + PRESENT-VALUE/{NONWORD} { pop_return PRESENT_VALUE; } RANDOM{OSPC}{PARENS} { pop_return RANDOM; } RANDOM{OSPC}[(] { pop_return RANDOM_SEED; } RANDOM { pop_return RANDOM; } - RANGE{OSPC}/[(]? { pop_return RANGE; } - REM{OSPC}/[(]? { pop_return REM; } - REVERSE{OSPC}/[(]? { pop_return REVERSE; } - SECONDS-FROM-FORMATTED-TIME{OSPC}/[(]? { BEGIN(datetime_fmt); + RANGE/{NONWORD} { pop_return RANGE; } + REM/{NONWORD} { pop_return REM; } + REVERSE/{NONWORD} { pop_return REVERSE; } + SECONDS-FROM-FORMATTED-TIME/{NONWORD} { BEGIN(datetime_fmt); return SECONDS_FROM_FORMATTED_TIME; } - SECONDS-PAST-MIDNIGHT{OSPC}/[(]? { pop_return SECONDS_PAST_MIDNIGHT; } - SIGN{OSPC}/[(]? { pop_return SIGN; } - SIN{OSPC}/[(]? { pop_return SIN; } - SMALLEST-ALGEBRAIC{OSPC}/[(]? { pop_return SMALLEST_ALGEBRAIC; } - SQRT{OSPC}/[(]? { pop_return SQRT; } - STANDARD-COMPARE{OSPC}/[(]? { pop_return STANDARD_COMPARE; } - STANDARD-DEVIATION{OSPC}/[(]? { pop_return STANDARD_DEVIATION; } - SUBSTITUTE{OSPC}/[(]? { pop_return SUBSTITUTE; } - SUM{OSPC}/[(]? { pop_return SUM; } - TAN{OSPC}/[(]? { pop_return TAN; } - TEST-DATE-YYYYMMDD{OSPC}/[(]? { pop_return TEST_DATE_YYYYMMDD; } - TEST-DAY-YYYYDDD{OSPC}/[(]? { pop_return TEST_DAY_YYYYDDD; } - TEST-FORMATTED-DATETIME{OSPC}/[(]? { BEGIN(datetime_fmt); return TEST_FORMATTED_DATETIME; } - TEST-NUMVAL{OSPC}/[(]? { pop_return TEST_NUMVAL; } - TEST-NUMVAL-C{OSPC}/[(]? { pop_return TEST_NUMVAL_C; } - TEST-NUMVAL-F{OSPC}/[(]? { pop_return TEST_NUMVAL_F; } - TRIM{OSPC}/[(]? { pop_return TRIM; } - ULENGTH{OSPC}/[(]? { pop_return ULENGTH; } - UPOS{OSPC}/[(]? { pop_return UPOS; } - UPPER-CASE{OSPC}/[(]? { pop_return UPPER_CASE; } - USUBSTR{OSPC}/[(]? { pop_return USUBSTR; } - USUPPLEMENTARY{OSPC}/[(]? { pop_return USUPPLEMENTARY; } - UUID4{OSPC}/[(]? { pop_return UUID4; } - UVALID{OSPC}/[(]? { pop_return UVALID; } - UWIDTH{OSPC}/[(]? { pop_return UWIDTH; } - VARIANCE{OSPC}/[(]? { pop_return VARIANCE; } - WHEN-COMPILED{OSPC}/[(]? { pop_return WHEN_COMPILED; } - YEAR-TO-YYYY{OSPC}/[(]? { pop_return YEAR_TO_YYYY; } + SECONDS-PAST-MIDNIGHT/{NONWORD} { pop_return SECONDS_PAST_MIDNIGHT; } + SIGN/{NONWORD} { pop_return SIGN; } + SIN/{NONWORD} { pop_return SIN; } + SMALLEST-ALGEBRAIC/{NONWORD} { pop_return SMALLEST_ALGEBRAIC; } + SQRT/{NONWORD} { pop_return SQRT; } + STANDARD-COMPARE/{NONWORD} { pop_return STANDARD_COMPARE; } + STANDARD-DEVIATION/{NONWORD} { pop_return STANDARD_DEVIATION; } + SUBSTITUTE/{NONWORD} { pop_return SUBSTITUTE; } + SUM/{NONWORD} { pop_return SUM; } + TAN/{NONWORD} { pop_return TAN; } + TEST-DATE-YYYYMMDD/{NONWORD} { pop_return TEST_DATE_YYYYMMDD; } + TEST-DAY-YYYYDDD/{NONWORD} { pop_return TEST_DAY_YYYYDDD; } + TEST-FORMATTED-DATETIME/{NONWORD} { BEGIN(datetime_fmt); return TEST_FORMATTED_DATETIME; } + TEST-NUMVAL/{NONWORD} { pop_return TEST_NUMVAL; } + TEST-NUMVAL-C/{NONWORD} { pop_return TEST_NUMVAL_C; } + TEST-NUMVAL-F/{NONWORD} { pop_return TEST_NUMVAL_F; } + TRIM/{NONWORD} { pop_return TRIM; } + ULENGTH/{NONWORD} { pop_return ULENGTH; } + UPOS/{NONWORD} { pop_return UPOS; } + UPPER-CASE/{NONWORD} { pop_return UPPER_CASE; } + USUBSTR/{NONWORD} { pop_return USUBSTR; } + USUPPLEMENTARY/{NONWORD} { pop_return USUPPLEMENTARY; } + UUID4/{NONWORD} { pop_return UUID4; } + UVALID/{NONWORD} { pop_return UVALID; } + UWIDTH/{NONWORD} { pop_return UWIDTH; } + VARIANCE/{NONWORD} { pop_return VARIANCE; } + WHEN-COMPILED/{NONWORD} { pop_return WHEN_COMPILED; } + YEAR-TO-YYYY/{NONWORD} { pop_return YEAR_TO_YYYY; } + + /* Matches above include NONWORD because the NAME tests below are otherwise longer, */ {NAME}{OSPC}/[(] { /* If /{OSPC}, "dangerous trailing context" "*/ auto name = null_trim(xstrdup(yytext)); @@ -1944,7 +1990,8 @@ BASIS { yy_push_state(basis); return BASIS; } {STRING} { yy_pop_state(); yypush_buffer_state( yy_create_buffer(yyin, YY_BUF_SIZE) ); if( (yyin = cdftext::lex_open(yytext)) == NULL ) { - yywarn("could not open BASIS file '%s'", yytext); + cbl_message(yylloc, LexIncludeE, + "could not open BASIS file '%s'", yytext); yyterminate(); } } @@ -1955,7 +2002,8 @@ BASIS { yy_push_state(basis); return BASIS; } } <procedure_div>{ - EQUALS?{OSPC}/[(] { return '='; } + EQUALS?{OSPC}/[(] { ydflval.string = yylval.string = xstrdup(yytext); + return EQ; } {NAME}{OSPC}/[(] { /* If /{OSPC}, "dangerous trailing context" "*/ if( is_integer_token() ) return numstr_of(yytext); @@ -1991,7 +2039,7 @@ BASIS { yy_push_state(basis); return BASIS; } } return token; } - [.][[:blank:].]+ { return '.'; } + [.]+[[:blank:].]+ { return '.'; } } <exception>{ @@ -2011,7 +2059,7 @@ BASIS { yy_push_state(basis); return BASIS; } return symbol_file(PROGRAM, yytext)? FILENAME : NAME; } [[:blank:]]+ - \r?\n { yy_pop_state(); } + {EOL} { yy_pop_state(); } } <raising>{ @@ -2032,49 +2080,56 @@ BASIS { yy_push_state(basis); return BASIS; } if( include_debug() ) myless(7); } } - ^[ ]*>>{OSPC}IF { yy_push_state(cdf_state); return CDF_IF; } - ^[ ]*>>{OSPC}ELSE { return CDF_ELSE; } - ^[ ]*>>{OSPC}END-IF { return CDF_END_IF; } + ^[ ]*>>{OBLANK}IF { yy_push_state(cdf_state); return CDF_IF; } + ^[ ]*>>{OBLANK}ELSE { return CDF_ELSE; } + ^[ ]*>>{OBLANK}END-IF { return CDF_END_IF; } - ^[ ]*[$]{OSPC}IF { if( ! dialect_mf() ) { - dialect_error(yylloc, yytext, "mf"); - } + ^[ ]*[$]{OBLANK}IF { dialect_ok(yylloc, MfCdfDollar, yytext); yy_push_state(cdf_state); return CDF_IF; } - ^[ ]*[$]{OSPC}ELSE { if( ! dialect_mf() ) { - dialect_error(yylloc, yytext, "mf"); - } + ^[ ]*[$]{OBLANK}ELSE { dialect_ok(yylloc, MfCdfDollar, yytext); return CDF_ELSE; } - ^[ ]*[$]{OSPC}END { if( ! dialect_mf() ) { - dialect_error(yylloc, yytext, "mf"); - } + ^[ ]*[$]{OBLANK}END { dialect_ok(yylloc, MfCdfDollar, yytext); return CDF_END_IF; } - ^[ ]*[$]{OSPC}SET({SPC}CONSTANT)? { - if( ! dialect_mf() ) dialect_error(yylloc, yytext, "mf"); + ^[ ]*[$]{OBLANK}SET({SPC}CONSTANT)? { + dialect_ok(yylloc, MfCdfDollar, yytext); yy_push_state(cdf_state); return CDF_DEFINE; } - ^[ ]*>>{OSPC}EVALUATE { return CDF_EVALUATE; } - ^[ ]*>>{OSPC}WHEN { return CDF_WHEN; } - ^[ ]*>>{OSPC}END-EVALUATE { return CDF_END_EVALUATE; } + ^[ ]*>>{OBLANK}EVALUATE { return CDF_EVALUATE; } + ^[ ]*>>{OBLANK}WHEN { return CDF_WHEN; } + ^[ ]*>>{OBLANK}END-EVALUATE { return CDF_END_EVALUATE; } - ^[ ]*>>{OSPC}CALL-CONVENTION{SPC}C { return CALL_VERBATIM; } - ^[ ]*>>{OSPC}CALL-CONVENTION{SPC}COBOL { return CALL_COBOL; } - ^[ ]*>>{OSPC}CALL-CONVENTION{SPC}VERBATIM { return CALL_VERBATIM; } + ^[ ]*>>{OBLANK}CALL-CONVENTION{BLANK}C { return CALL_VERBATIM; } + ^[ ]*>>{OBLANK}CALL-CONVENTION{BLANK}COBOL { return CALL_COBOL; } + ^[ ]*>>{OBLANK}CALL-CONVENTION{BLANK}VERBATIM { return CALL_VERBATIM; } - ^[ ]*>>{OSPC}DEFINE { yy_push_state(cdf_state); return CDF_DEFINE; } - ^[ ]*>>{OSPC}DISPLAY { return CDF_DISPLAY; } - ^[ ]*>>{OSPC}TURN { yy_push_state(exception); return TURN; } - ^[ ]*>>{OSPC}COBOL-WORDS { yy_push_state(cobol_words); return COBOL_WORDS; } + ^[ ]*>>{OBLANK}DEFINE { yy_push_state(cdf_state); return CDF_DEFINE; } + ^[ ]*>>{OBLANK}DISPLAY { return CDF_DISPLAY; } + ^[ ]*>>{OBLANK}TURN { yy_push_state(exception); return TURN; } + ^[ ]*>>{OBLANK}COBOL-WORDS { yy_push_state(cobol_words); return COBOL_WORDS; } - ^[ ]*>>{OSPC}{NAME} { + ^[ ]*>>{OBLANK}SOURCE{BLANK}FORMAT { return SOURCE_FORMAT; } + + ^[ ]*>>{OBLANK}PUSH { return CDF_PUSH; } + ^[ ]*>>{OBLANK}POP { return CDF_POP; } + + ^[ ]*>>{OBLANK}{NAME} { error_msg(yylloc, "unknown CDF token: %s", yytext); } + OTHER { return OTHER; } OVERRIDE { return OVERRIDE; } PARAMETER { return PARAMETER_kw; } THRU { return THRU; } TRUE { return TRUE_kw; } + + ALL { return ALL; } + CALL-CONVENTION { return CALL_CONVENTION; } + COBOL-WORDS { return COBOL_WORDS; } + DEFINE { return CDF_DEFINE; } + SOURCE{BLANK}FORMAT { return SOURCE_FORMAT; } + } <cobol_words>{ @@ -2089,39 +2144,45 @@ BASIS { yy_push_state(basis); return BASIS; } } <*>{ - {PUSH_FILE} { - yy_set_bol(true); - auto top_file = cobol_lineno_save(); - if( top_file ) { - if( yy_flex_debug ) dbgmsg(" saving line %4d of %s", - yylineno, top_file); - } - // "\f#file push <name>": name starts at offset 13. - char *filename = xstrdup(yytext); - filename[yyleng - 1] = '\0'; // kill the trailing formfeed - filename += 12; - if( yytext[0] != '\f' ) { - dbgmsg("logic warning: filename was adjusted to %s", --filename); + {PUSH_FILE} { + yy_set_bol(true); + auto top_file = cobol_lineno(yylineno); + if( top_file ) { + if( yy_flex_debug ) dbgmsg(" saving line %4d of %s", + yylineno, top_file); + } + // "\f#file push <name>": name starts at offset 13. + char *filename = xstrdup(yytext); + filename[yyleng - 1] = '\0'; // kill the trailing formfeed + filename += 12; + if( yytext[0] != '\f' ) { + dbgmsg("logic warning: filename was adjusted to %s", + --filename); + } + input_file_status.enter(filename); + yylineno = 1; + reset_location(); + } + + {POP_FILE} { + yy_set_bol(true); + input_file_status.leave(); + yylineno = cobol_lineno(); } - input_file_status.enter(filename); - } - - {POP_FILE} { - yy_set_bol(true); - input_file_status.leave(); - } - {LINE_DIRECTIVE} { cobol_fileline_set(yytext); } + {LINE_DIRECTIVE} { + yylineno = cobol_fileline_set(yytext); + reset_location(); + } } <*>OR { return OR; } <*>AND { return AND; } -<*>{DOTSEP}[[:blank:].]+$ { return '.'; } -<*>[*/+-]{SPC}[+] { return *yytext; } +<*>{DOTSEP} { return '.'; } <*>[().=*/+&-] { return *yytext; } <*>[[:blank:]]+ -<*>\r?\n +<*>{EOL} <*>{ {COMMA} @@ -2132,48 +2193,48 @@ BASIS { yy_push_state(basis); return BASIS; } <*>{ ACCEPT { return ACCEPT; } ACCESS { return ACCESS; } - ADD { return ADD; } + ADD { return ADD; } ADDRESS { return ADDRESS; } ADVANCING { return ADVANCING; } - AFTER { return AFTER; } - ALL { return ALL; } + AFTER { return AFTER; } + ALL { return ALL; } ALLOCATE { return ALLOCATE; } ALPHABET { return ALPHABET; } ALPHABETIC { return ALPHABETIC; } ALPHABETIC-LOWER { return ALPHABETIC_LOWER; } ALPHABETIC-UPPER { return ALPHABETIC_UPPER; } - ALPHANUMERIC { return ALPHANUMERIC; } + ALPHANUMERIC { return ALPHANUMERIC; } ALPHANUMERIC-EDITED { return ALPHANUMERIC_EDITED; } - ALSO { return ALSO; } + ALSO { return ALSO; } ALTERNATE { return ALTERNATE; } - AND { return AND; } - ANY { return ANY; } + AND { return AND; } + ANY { return ANY; } ANYCASE { return ANYCASE; } - ARE { return ARE; } - AREA { return AREA; } - AREAS { return AREAS; } - AS { return AS; } + ARE { return ARE; } + AREA { return AREA; } + AREAS { return AREAS; } + AS { return AS; } ASCENDING { return ASCENDING; } - ASSIGN { return ASSIGN; } - AT { return AT; } - BASED { return BASED; } + ASSIGN { return ASSIGN; } + AT { return AT; } + BASED { return BASED; } BEFORE { return BEFORE; } BINARY { return BINARY; } - BIT { return BIT; } - BLANK { return BLANK; } - BLOCK { return BLOCK; } + BIT { return BIT; } + BLANK { return BLANK; } + BLOCK { return BLOCK_kw; } BOTTOM { return BOTTOM; } - BY { return BY; } - CALL { return CALL; } + BY { return BY; } + CALL { return CALL; } CANCEL { return CANCEL; } - CF { return CF; } - CH { return CH; } + CF { return CF; } + CH { return CH; } CHARACTER { return CHARACTER; } CHARACTERS { return CHARACTERS; } - CLASS { return CLASS; } - CLOSE { return CLOSE; } - CODE { return CODE; } - COMMA { return COMMA; } + CLASS { return CLASS; } + CLOSE { return CLOSE; } + CODE { return CODE; } + COMMA { return COMMA; } COMMIT { return COMMIT; } COMMON { return COMMON; } CONDITION { return CONDITION; } @@ -2184,17 +2245,17 @@ BASIS { yy_push_state(basis); return BASIS; } CONTROL { return CONTROL; } CONTROLS { return CONTROLS; } CONVERTING { return CONVERTING; } - COPY { return COPY; } - COUNT { return COUNT; } + COPY { return COPY; } + COUNT { return COUNT; } CURRENCY { return CURRENCY; } - DATA { return DATA; } - DATE { return DATE; } - DAY { return DAY; } + DATA { return DATA; } + DATE { return DATE; } + DAY { return DAY; } DAY-OF-WEEK { return DAY_OF_WEEK; } - DE { return DE; } + DE { return DE; } DECIMAL-POINT { return DECIMAL_POINT; } DECLARATIVES { return DECLARATIVES; } - DEFAULT { return DEFAULT; } + DEFAULT { yylval.number = 'D'; return DEFAULT; } DELETE { return DELETE; } DELIMITED { return DELIMITED; } DELIMITER { return DELIMITER; } @@ -2203,12 +2264,12 @@ BASIS { yy_push_state(basis); return BASIS; } DETAIL { return DETAIL; } DISPLAY { return DISPLAY; } DIVIDE { return DIVIDE; } - DOWN { return DOWN; } + DOWN { return DOWN; } DUPLICATES { return DUPLICATES; } DYNAMIC { return DYNAMIC; } - EC { return EC; } - ELSE { return ELSE; } - END { return END; } + EC { return EC; } + ELSE { return ELSE; } + END { return END; } END-ACCEPT { return END_ACCEPT; } END-ADD { return END_ADD; } END-CALL { return END_CALL; } @@ -2226,117 +2287,117 @@ BASIS { yy_push_state(basis); return BASIS; } END-SUBTRACT { return END_SUBTRACT; } END-WRITE { return END_WRITE; } ENVIRONMENT { return ENVIRONMENT; } - EQUAL { return EQUAL; } - ERROR { return ERROR; } + EQUAL { ydflval.string = yylval.string = xstrdup(yytext); + return EQ; } + ERROR { return ERROR; } EVALUATE { return EVALUATE; } EXCEPTION { return EXCEPTION; } - EXIT { return EXIT; } + EXIT { return EXIT; } EXTEND { return EXTEND; } EXTERNAL { return EXTERNAL; } - FD { return FD; } - FINAL { return FINAL; } + FD { return FD; } + FINAL { return FINAL; } FINALLY { return FINALLY; } - FIRST { return FIRST; } + FIRST { return FIRST; } FOOTING { return FOOTING; } - FOR { return FOR; } - FREE { return FREE; } - FROM { return FROM; } + FOR { return FOR; } + FREE { return FREE; } + FROM { return FROM; } FUNCTION { return FUNCTION; } GENERATE { return GENERATE; } GIVING { return GIVING; } GLOBAL { return GLOBAL; } - GO { return GO; } + GO { return GO; } GOBACK { return GOBACK; } - GROUP { return GROUP; } + GROUP { return GROUP; } HEADING { return HEADING; } - IDENTIFICATION { return IDENTIFICATION_DIV; } - IF { return IF; } - IN { return IN; } - INDEX { return INDEX; } + IF { return IF; } + IN { return IN; } + INDEX { return INDEX; } INDEXED { return INDEXED; } INDICATE { return INDICATE; } INITIAL { return INITIAL; } INITIALIZE { return INITIALIZE; } INITIATE { return INITIATE; } - INPUT { return INPUT; } + INPUT { return INPUT; } INSPECT { return INSPECT; } INTERFACE { return INTERFACE; } - INTO { return INTO; } + INTO { return INTO; } INVOKE { return INVOKE; } - IS { return IS; } - KEY { return KEY; } - LAST { return LAST; } + IS { return IS; } + KEY { return KEY; } + LAST { return LAST; } LEADING { return LEADING; } - LEFT { return LEFT; } + LEFT { return LEFT; } LENGTH { return LENGTH; } - LIMIT { return LIMIT; } + LIMIT { return LIMIT; } LIMITS { return LIMITS; } LINAGE { return LINAGE; } - LINE { return LINE; } + LINE { return LINE; } LINE-COUNTER { return LINE_COUNTER; } - LINES { return LINES; } + LINES { return LINES; } LINKAGE { return LINKAGE; } LOCAL-STORAGE { return LOCAL_STORAGE; } LOCALE { return LOCALE; } LOCATION { return LOCATION; } - LOCK { return LOCK; } - MERGE { return MERGE; } - MODE { return MODE; } - MOVE { return MOVE; } + LOCK { return LOCK; } + MERGE { return MERGE; } + MODE { return MODE; } + MOVE { return MOVE; } MULTIPLY { return MULTIPLY; } NATIONAL { return NATIONAL; } NATIONAL-EDITED { return NATIONAL_EDITED; } NATIVE { return NATIVE; } NEGATIVE { return NEGATIVE; } NESTED { return NESTED; } - NEXT { return NEXT; } - NO { return NO; } - NOT { return NOT; } + NEXT { return NEXT; } + NO { return NO; } + NOT { return NOT; } NUMBER { return NUMBER; } NUMERIC { return NUMERIC; } NUMERIC-EDITED { return NUMERIC_EDITED; } OCCURS { return OCCURS; } - OF { return OF; } - OFF { return OFF; } + OF { return OF; } + OFF { return OFF; } OMITTED { return OMITTED; } - ON { return ON; } - OPEN { return OPEN; } + ON { return ON; } + OPEN { return OPEN; } OPTIONAL { return OPTIONAL; } OPTIONS { return OPTIONS; } - OR { return OR; } - ORDER { return ORDER; } - ORGANIZATION { return ORGANIZATION; } - OTHER { return OTHER; } + OR { return OR; } + ORDER { return ORDER; } + ORGANI[SZ]ATION { return ORGANIZATION; } + OTHER { return OTHER; } OUTPUT { return OUTPUT; } - OVERFLOW { return OVERFLOW; } + OVERFLOW { return OVERFLOW_kw; } OVERRIDE { return OVERRIDE; } PACKED-DECIMAL { return PACKED_DECIMAL; } - PAGE { return PAGE; } + PAGE { return PAGE; } PAGE-COUNTER { return PAGE_COUNTER; } PERFORM { return PERFORM; } - PF { return PF; } - PH { return PH; } - PIC { return PIC; } + PF { return PF; } + PH { return PH; } + PIC { return PIC; } PICTURE { return PICTURE; } - PLUS { return PLUS; } + PLUS { return PLUS; } POINTER { return POINTER; } POSITIVE { return POSITIVE; } PROCEDURE { return PROCEDURE; } - PROGRAM { return PROGRAM; } + PROGRAM { return PROGRAM_kw; } PROGRAM-ID { return PROGRAM_ID; } PROPERTY { return PROPERTY; } PROTOTYPE { return PROTOTYPE; } QUOTES { return QUOTES; } - RAISE { return RAISE; } + RAISE { return RAISE; } RAISING { return RAISING; } RANDOM { return RANDOM; } - RD { return RD; } - READ { return READ; } + RD { return RD; } + READ { return READ; } RECORD { return RECORD; } RECORDS { return RECORDS; } REDEFINES { return REDEFINES; } - REEL { return REEL; } + REEL { return REEL; } REFERENCE { return REFERENCE; } RELATIVE { return RELATIVE; } RELEASE { return RELEASE; } @@ -2350,77 +2411,77 @@ BASIS { yy_push_state(basis); return BASIS; } REPORTS { return REPORTS; } REPOSITORY { return REPOSITORY; } RESERVE { return RESERVE; } - RESET { return RESET; } + RESET { return RESET; } RESUME { return RESUME; } RETURN { return RETURN; } RETURNING { return RETURNING; } REWIND { return REWIND; } REWRITE { return REWRITE; } - RF { return RF; } - RH { return RH; } - RIGHT { return RIGHT; } + RF { return RF; } + RH { return RH; } + RIGHT { return RIGHT; } ROUNDED { return ROUNDED; } - RUN { return RUN; } - SAME { return SAME; } + RUN { return RUN; } + SAME { return SAME; } SCREEN { return SCREEN; } - SD { return SD; } + SD { return SD; } SEARCH { return SEARCH; } - SECTION { return SECTION; } + SECTION { yylval.string = NULL; return SECTION; } SELECT { return SELECT; } SENTENCE { return SENTENCE; } SEPARATE { return SEPARATE; } SEQUENCE { return SEQUENCE; } SEQUENTIAL { return SEQUENTIAL; } - SET { return SET; } + SET { return SET; } SHARING { return SHARING; } - SIGN { return SIGN; } - SIZE { return SIZE; } - SORT { return SORT; } + SIGN { return SIGN; } + SIZE { return SIZE; } + SORT { return SORT; } SORT-MERGE { return SORT_MERGE; } SOURCE { return SOURCE; } - SPACE { return SPACE; } + SPACE { return SPACE; } SPACES { return SPACES; } SPECIAL-NAMES { return SPECIAL_NAMES; } STANDARD { return STANDARD; } STANDARD-1 { return STANDARD_1; } - START { return START; } + START { return START; } STATUS { return STATUS; } - STOP { return STOP; } + STOP { return STOP; } SUBTRACT { return SUBTRACT; } - SUM { return SUM; } + SUM { return SUM; } SUPPRESS { return SUPPRESS; } SYMBOLIC { return SYMBOLIC; } TALLYING { return TALLYING; } TERMINATE { return TERMINATE; } - TEST { return TEST; } - THAN { return THAN; } - THEN { return THEN; } - THRU { return THRU; } - TIME { return TIME; } - TIMES { return TIMES; } - TO { return TO; } - TOP { return TOP; } + TEST { return TEST; } + THAN { return THAN; } + THEN { return THEN; } + THRU { return THRU; } + TIME { return TIME; } + TIMES { return TIMES; } + TO { return TO; } + TOP { return TOP; } TRAILING { return TRAILING; } - TYPE { return TYPE; } + TYPE { return TYPE; } TYPEDEF { return TYPEDEF; } - UNIT { return UNIT; } - UNTIL { return UNTIL; } - UP { return UP; } - UPON { return UPON; } - USAGE { return USAGE; } - USE { return USE; } - USING { return USING; } - VALUE { return VALUE; } + UNIT { return UNIT; } + UNTIL { return UNTIL; } + UP { return UP; } + UPON { return UPON; } + USAGE { return USAGE; } + USE { return USE; } + USING { return USING; } + VALUE { return VALUE; } VARYING { return VARYING; } - WHEN { return WHEN; } - WITH { return WITH; } + WHEN { return WHEN; } + WITH { return WITH; } WORKING-STORAGE { return WORKING_STORAGE; } - WRITE { return WRITE; } + WRITE { return WRITE; } ZERO | ZEROES | - ZEROS { return ZERO; } + ZEROS { return ZERO; } } <*>{ @@ -2457,28 +2518,13 @@ BASIS { yy_push_state(basis); return BASIS; } return NO_CONDITION; } -<<EOF>> { - - if( YY_START == quoted1 || YY_START == quoted2 ) { - error_msg(yylloc, "syntax error: unterminated string '%s'", +<quoted1,quoted2>{ + <<EOF>> { + error_msg(yylloc, "syntax error: unterminated string %<%s%>", tmpstring); - cbl_internal_error(""); - } - yypop_buffer_state(); - - if ( !YY_CURRENT_BUFFER ) { - return 0; - } - - if( ! wait_for_the_child() ) { - yyterminate(); - } - cobol_filename_restore(); - parser_leave_file(); - - if( yydebug ) yywarn("resume parsing '%s'", cobol_filename()); - yy_set_bol(true); - } + return NO_CONDITION; + } +} %% diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h index b9bbd30..e75bb38 100644 --- a/gcc/cobol/scan_ante.h +++ b/gcc/cobol/scan_ante.h @@ -149,7 +149,7 @@ numstr_of( const char string[], radix_t radix = decimal_e ) { } auto nx = std::count_if(input, p, fisdigit); if( 36 < nx ) { - error_msg(yylloc, "significand of %s has more than 36 digits (%zu)", input, nx); + error_msg(yylloc, "significand of %s has more than 36 digits (%ld)", input, (long)nx); return NO_CONDITION; } @@ -159,11 +159,11 @@ numstr_of( const char string[], radix_t radix = decimal_e ) { // exponent is implementor-defined." (We allow 9999.) nx = std::count_if(p, eoinput, fisdigit); if( 4 < nx ) { - error_msg(yylloc, "exponent %s more than 4 digits", ++p); + error_msg(yylloc, "exponent %qs more than 4 digits", ++p); return NO_CONDITION; } if( eoinput != std::find(p, eoinput, symbol_decimal_point()) ) { - error_msg(yylloc, "exponent includes decimal point", ++p); + error_msg(yylloc, "exponent %qs includes decimal point", ++p); return NO_CONDITION; } @@ -187,7 +187,7 @@ numstr_of( const char string[], radix_t radix = decimal_e ) { } } if( 1 < std::count(input, eoinput, symbol_decimal_point()) ) { - error_msg(yylloc, "invalid numeric literal", ++p); + error_msg(yylloc, "invalid numeric literal %qs", ++p); return NO_CONDITION; } @@ -214,7 +214,11 @@ struct cdf_status_t { const char *filename; int token; bool parsing; - cdf_status_t( int token = 0, bool parsing = true ) + cdf_status_t() + : lineno(yylineno), filename(cobol_filename()) + , token(0), parsing(true) + {} + cdf_status_t( int token, bool parsing ) : lineno(yylineno), filename(cobol_filename()) , token(token), parsing(parsing) {} @@ -291,7 +295,7 @@ static class parsing_status_t : public std::stack<cdf_status_t> { void splat() const { int i=0; for( const auto& status : c ) { - yywarn( "%4d\t%s", ++i, status.str() ); + dbgmsg( "%d %s", ++i, status.str() ); } } } parsing; @@ -301,7 +305,7 @@ void field_done() { orig_picture[0] = '\0'; parsing.need_level(true); } static int scanner_token() { if( parsing.empty() ) { - error_msg(yylloc, ">>ELSE or >>END-IF without >>IF"); + error_msg(yylloc, "%<>>ELSE%> or %<>>END-IF%> without %<>>IF%>"); return NO_CONDITION; } return parsing.top().token; @@ -312,34 +316,29 @@ bool scanner_normal() { return parsing.normal(); } void scanner_parsing( int token, bool tf ) { parsing.push( cdf_status_t(token, tf) ); - if( yydebug ) { - yywarn("%10s: parsing now %5s, depth %zu", - keyword_str(token), boolalpha(parsing.on()), parsing.size()); - parsing.splat(); - } + dbgmsg("%s: parsing now %s, depth %zu", + keyword_str(token), boolalpha(parsing.on()), parsing.size()); + parsing.splat(); } void scanner_parsing_toggle() { if( parsing.empty() ) { - error_msg(yylloc, ">>ELSE without >>IF"); + error_msg(yylloc, "%<>>ELSE%> without %<>>IF%>"); return; } parsing.top().toggle(); - if( yydebug ) { - yywarn("%10s: parsing now %5s", - keyword_str(CDF_ELSE), boolalpha(parsing.on())); - } + dbgmsg("%s: parsing now %s", + keyword_str(CDF_ELSE), boolalpha(parsing.on())); } void scanner_parsing_pop() { if( parsing.empty() ) { - error_msg(yylloc, ">>END-IF without >>IF"); + error_msg(yylloc, "%<>>END-IF%> without %<>>IF%>"); return; } parsing.pop(); - if( yydebug ) { - yywarn("%10s: parsing now %5s, depth %zu", - keyword_str(CDF_END_IF), boolalpha(parsing.on()), parsing.size()); - parsing.splat(); - } + dbgmsg("%s: parsing now %s, depth %zu", + keyword_str(CDF_END_IF), boolalpha(parsing.on()), + parsing.size()); + parsing.splat(); } @@ -351,6 +350,10 @@ static void level_found() { if( scanner_normal() ) parsing.need_level(false); } +/* + * Trim the scanned location by the amount about to re-scanned. + * Must be a macro because it expands yyless. + */ #define myless(N) \ do { \ auto n(N); \ @@ -368,8 +371,9 @@ class enter_leave_t { public: enter_leave_t() : entering(NULL), leaving(NULL), filename(NULL) {} enter_leave_t( parser_enter_file_f *entering, const char *filename ) - : entering(entering), leaving(NULL), filename(filename) {} - enter_leave_t(parser_leave_file_f *leaving) + : entering(entering), leaving(NULL), filename(filename) + {} + explicit enter_leave_t(parser_leave_file_f *leaving) : entering(NULL), leaving(leaving), filename(NULL) {} void notify() { @@ -381,9 +385,9 @@ class enter_leave_t { gcc_assert(leaving == NULL); } if( leaving ) { - auto name = cobol_filename_restore(); + cobol_filename_restore(); if( yy_flex_debug ) dbgmsg("resuming line %4d of %s", - yylineno, name? name : "<none>"); + yylineno, cobol_filename()); leaving(); gcc_assert(entering == NULL); } @@ -397,7 +401,7 @@ static class input_file_status_t { inputs.push( enter_leave_t(parser_enter_file, filename) ); } void leave() { - inputs.push( parser_leave_file ); + inputs.push( enter_leave_t(parser_leave_file) ); } void notify() { while( ! inputs.empty() ) { @@ -410,27 +414,61 @@ static class input_file_status_t { void input_file_status_notify() { input_file_status.notify(); } -void cdf_location_set(YYLTYPE loc); +/* + * parse.y and cdf.y each define a 4-integer struct to hold a token's location. + * parse.y uses YYLTYPE yylloc; + * cdf.y uses YDFLLTYPE ydflloc; + * + * The structs have identical definitions with different types and of course + * names. We define "conversion" between them for convenience. + * + * Each parser expects its location value to be updated whenever it calls + * yylex(). Therefore, here in the lexer we set both locations as each token + * is scanned, so that both parsers see the same location. + */ +static YDFLTYPE +ydfltype_of( const YYLTYPE& loc ) { + YDFLTYPE output { + loc.first_line, loc.first_column, + loc.last_line, loc.last_column }; + return output; +} +/* + * After the input filename and yylineno are set, update the location of the + * scanned token. + */ static void -update_location() { +update_location( const YYLTYPE *ploc = nullptr ) { YYLTYPE loc = { yylloc.last_line, yylloc.last_column, yylineno, yylloc.last_column + yyleng }; + if( ploc ) loc = *ploc; - auto nline = std::count(yytext, yytext + yyleng, '\n'); - if( nline ) { - char *p = static_cast<char*>(memrchr(yytext, '\n', yyleng)); + const char *p = static_cast<char*>(memrchr(yytext, '\n', yyleng)); + if( p ) { loc.last_column = (yytext + yyleng) - p; } yylloc = loc; - cdf_location_set(loc); - location_dump(__func__, __LINE__, "yylloc", yylloc); + ydflloc = ydfltype_of(yylloc); + + dbgmsg(" SC: %s location (%d,%d) to (%d,%d)", + start_condition_is(), + yylloc.first_line, yylloc.first_column, + yylloc.last_line, yylloc.last_column); } static void +reset_location() { + static const YYLTYPE loc { yylineno, 1, yylineno, 1 }; + update_location(&loc); +} + +#define YY_USER_ACTION update_location(); + +static void trim_location( int nkeep) { gcc_assert( 0 <= nkeep && nkeep <= yyleng ); struct { char *p, *pend; @@ -438,14 +476,16 @@ trim_location( int nkeep) { } rescan = { yytext + nkeep, yytext + yyleng }; auto nline = std::count(rescan.p, rescan.pend, '\n'); - dbgmsg("%s:%d: yyless(%d), rescan '%.*s' (%zu lines, %d bytes)", + dbgmsg("%s:%d: yyless(%d), rescan '%.*s' (" HOST_SIZE_T_PRINT_UNSIGNED + " lines, " HOST_SIZE_T_PRINT_UNSIGNED " bytes)", __func__, __LINE__, nkeep, int(rescan.size()), rescan.p, - nline, rescan.size()); + (fmt_size_t)nline, (fmt_size_t)rescan.size()); if( nline ) { gcc_assert( yylloc.first_line + nline <= yylloc.last_line ); - yylloc.last_line =- int(nline); + yylloc.last_line -= int(nline); + gcc_assert( yylloc.first_line <= yylloc.last_line ); char *p = static_cast<char*>(memrchr(rescan.p, '\n', rescan.size())); yylloc.last_column = rescan.pend - ++p; return; @@ -473,7 +513,8 @@ update_location_col( const char str[], int correction = 0) { #define YY_USER_INIT do { \ static YYLTYPE ones = {1,1, 1,1}; \ - yylloc = ones; \ + yylloc = ones; \ + ydflloc = ydfltype_of(yylloc); \ } while(0) /* @@ -482,21 +523,21 @@ update_location_col( const char str[], int correction = 0) { * updates neither yylval nor yylloc. That job is left to the actions. * * The parser relies on yylex to set yylval and yylloc each time it is - * called. It apparently maintains a separate copy for each term, and uses + * called. It maintains a separate copy for each term, and uses * YYLLOC_DEFAULT() to update the location of nonterminals. */ #define YY_DECL int lexer(void) -#define YY_USER_ACTION \ - update_location(); \ - if( yy_flex_debug ) dbgmsg("SC: %s", start_condition_is() ); - # define YY_INPUT(buf, result, max_size) \ { \ if( 0 == (result = lexer_input(buf, max_size, yyin)) ) \ result = YY_NULL; \ } +#define bcomputable(T, C) \ + yylval.computational.type=T, \ + yylval.computational.capacity=C, \ + yylval.computational.signable=true, BINARY_INTEGER #define scomputable(T, C) \ yylval.computational.type=T, \ yylval.computational.capacity=C, \ @@ -510,6 +551,116 @@ static char *tmpstring = NULL; #define PROGRAM current_program_index() +// map of alias => canonical +static std::map <std::string, std::string> keyword_aliases; + +const std::string& +keyword_alias_add( const std::string& keyword, const std::string& alias ) { + auto p = keyword_aliases.find(alias); + if( p != keyword_aliases.end() ) return p->second; // error: do not overwrite + return keyword_aliases[alias] = keyword; +} + +/* + * Because numeric USAGE types don't have distinct tokens and may have aliases, + * we keep a table of their canonical names, which we use if we encounter an + * alias. + */ +struct bint_t { + int token; + cbl_field_type_t type; + uint32_t capacity; // zero means capacity depends on PICTURE + bool signable; +}; +static const std::map <std::string, bint_t > binary_integers { + { "BINARY", { COMPUTATIONAL, FldNumericBinary, 0, false } }, + { "COMP", { COMPUTATIONAL, FldNumericBinary, 0, false } }, + { "COMPUTATIONAL", { COMPUTATIONAL, FldNumericBinary, 0, false } }, + { "COMP-4", { COMPUTATIONAL, FldNumericBinary, 0, false } }, + { "COMPUTATIONAL-4", { COMPUTATIONAL, FldNumericBinary, 0, false } }, + + { "BINARY-CHAR", { BINARY_INTEGER, FldNumericBin5, 1, true } }, + { "BINARY-SHORT", { BINARY_INTEGER, FldNumericBin5, 2, true } }, + { "BINARY-LONG", { BINARY_INTEGER, FldNumericBin5, 4, true } }, + { "BINARY-DOUBLE", { BINARY_INTEGER, FldNumericBin5, 8, true } }, + { "BINARY-LONG-LONG", { BINARY_INTEGER, FldNumericBin5, 8, true } }, + + { "COMP-5", { COMPUTATIONAL, FldNumericBin5, 0, false } }, + { "COMPUTATIONAL-5", { COMPUTATIONAL, FldNumericBin5, 0, false } }, + { "COMP-X", { COMPUTATIONAL, FldNumericBin5, 0xFF, false } }, + { "COMPUTATIONAL-X", { COMPUTATIONAL, FldNumericBin5, 0xFF, false } }, + + { "COMP-1", { COMPUTATIONAL, FldFloat, 4, false } }, + { "COMPUTATIONAL-1", { COMPUTATIONAL, FldFloat, 4, false } }, + { "FLOAT-BINARY-32", { COMPUTATIONAL, FldFloat, 4, false } }, + { "FLOAT-SHORT", { COMPUTATIONAL, FldFloat, 4, false } }, + + { "COMP-2", { COMPUTATIONAL, FldFloat, 8, false } }, + { "COMPUTATIONAL-2", { COMPUTATIONAL, FldFloat, 8, false } }, + { "FLOAT-BINARY-64", { COMPUTATIONAL, FldFloat, 8, false } }, + { "FLOAT-LONG", { COMPUTATIONAL, FldFloat, 8, false } }, + { "FLOAT-BINARY-128", { COMPUTATIONAL, FldFloat, 16, false } }, + { "FLOAT-EXTENDED", { COMPUTATIONAL, FldFloat, 16, false } }, + + { "COMP-6", { COMPUTATIONAL, FldPacked, 0, false } }, + { "COMPUTATIONAL-6", { COMPUTATIONAL, FldPacked, 0, false } }, +}; + +static int +binary_integer_usage( const char name[]) { + // uname can't be cbl_name_t, because at this point name[] might have more + // than sizeof(cbl_name_t) characters. The length check comes later. + char *uname = xstrdup(name); + std::transform(name, name + strlen(name), uname, ftoupper); + + dbgmsg("%s:%d: checking %s in %zu keyword_aliases", + __func__, __LINE__, uname, + keyword_aliases.size() ); + + std::string key = uname; + auto alias = keyword_aliases.find(key); + if( alias != keyword_aliases.end() ) key = alias->second; + + auto p = binary_integers.find(key); + if( p == binary_integers.end() ) return 0; + + yylval.computational.type = p->second.type; + yylval.computational.capacity = p->second.capacity; + yylval.computational.signable = p->second.signable; + dbgmsg("%s:%d: %s has type %d", __func__, __LINE__, + uname, p->second.type ); + free(uname); + return p->second.token; +} + +static void +verify_ws( const YYLTYPE& loc, const char [] /* input[] */, char ch ) { + if( ! fisspace(ch) ) { + dialect_ok(loc, LexSeparatorE, "missing separator space"); + } +} +#define verify_ws(C) verify_ws(yylloc, yytext, C) + +int +binary_integer_usage_of( const char name[] ) { + cbl_name_t uname = {}; + std::transform(name, name + strlen(name), uname, ftoupper); + + auto p = binary_integers.find(uname); + if( p != binary_integers.end() ) { + int token = p->second.token; + switch( token ) { + case COMPUTATIONAL: + case BINARY_INTEGER: + return token; + default: + gcc_unreachable(); + assert(false); + } + } + return 0; +} + static uint32_t level_of( const char input[] ) { unsigned int output = 0; @@ -517,7 +668,7 @@ level_of( const char input[] ) { if( input[0] == '0' ) input++; if( 1 != sscanf(input, "%u", &output) ) { - yywarn( "%s:%d: invalid level '%s'", __func__, __LINE__, input ); + cbl_internal_error( "%s:%d: invalid level '%s'", __func__, __LINE__, input ); } return output; @@ -543,6 +694,387 @@ picset( int token ) { return token; } +/** +## Script and data to produce picture_t::followers. +## Based on ISO Table 10. +#! /usr/bin/awk -f + +BEGIN { + str = "B0/ , . + +- +- CR/DB cs cs Z* Z* + + cs cs 9 AX S V P P 1 N E" + split(str, cols) +} + +$1 ~ /CR|DB|cs/ { next } + +0 && !nlines++ { + for( i=0; i < length(cols); i++ ) { + print i, cols[i], "'" $i "'" + } +} + +$field == "x" { + if( ! nout++ ) { + printf "%2d: %5s: \"", field, cols[field - 1] + } + + gsub(/^ +| +$/, "", $1) + printf "%s", $1 +} + +END { + if( ! nout++ ) { + printf "%2d: %5s: \"", field, cols[field - 1] + } + print "\"" +} + +B x x x - x - - x - x x x x x x x x - x - x - x +0 x x x - x - - x - x x x x x x x x - x - x - x +/ x x x - x - - x - x x x x x x x x - x - x - x +, x x x - x - - x - x x x x x x x - - x - x +. x x - - x - - x - x - x - x - x ++ - - - - - - - - - - - - - - - - - - - - - - - x ++ +– ++ x x x - - - - x x x x - - x x x - - x x x +CR x x x - - - - x x x x - - x x x - - x x x +DB x x x - - - - x x x x - - x x x - - x x x +cs - - - - x +cs x x x - x - - - - x x - - - - x - - x x x + +Z x x - - x - - x - x +* x x - - x - - x - x +Z x x x - x - - x - x x - - - - - - - x - x +* x x x - x - - x - x x - - - - - - - x - x ++ x x - - - - - x - - - x +– x x - - - - - x - - - x ++ x x x - - - - x - - - x x - - - - - x +– x x x - - - - x - - - x x - - - - - x +cs x x - - x - - - - - - - - x +cs x x x - x - - - - - - - - x x - - - x + +9 x x x x x - - x - x - x - x - x x x x - x - - x +A x - - - - - - - - - - - - - - x x +X x - - - - - - - - - - - - - - x x +S +V x x - - x - - x - x - x - x - x - x - x +P x x - - x - - x - x - x - x - x - x - x +P - - - - x - - x - - - - - - - - - x x - x +1 - - - - - - - - - - - - - - - - - - - - - x +N x - - - - - - - - - - - - - - - - - - - - - x +E x x x - x - - - - - - - - - - x +**/ + +class picture_t { + static const char dot = '.', comma = ','; + + typedef std::vector<std::string> followings_t; + static const std::map <char, followings_t> followers; + + const char * const begin; + const char *p, *pend; + size_t pos; + struct exclusions_t { // Nonzero if set, > 1 is false. + // crdb means CR/DB or +/-. + // pluses means 2 or more consecutive '+'. + // minuses means 2 or more consecutive '-'. + // "21) The symbol 'Z' and the symbol '*' are mutually exclusive " + // stars means '*' or Z. + unsigned short int crdb, currency, dot, pluses, minuses, stars, zzz; + exclusions_t() + : crdb(0), currency(0), dot(0), pluses(0), minuses(0), stars(0) + {} + } exclusions; + YYLTYPE loc; + + bool is_crdb() const { // input must be uppercase for CR/DB + if( p[0] == 'C' || p[0] == 'D' ) { + char input[3] = { p[0], p[1] }; + return ( 0 == strcmp(input, "CR") || 0 == strcmp(input, "DB") ); + } + return false; + } + + const char * match_paren( const char *paren ) const { + gcc_assert(paren[0] == '('); // start with opening paren + paren = std::find_if( paren, pend, + []( char ch ) { + return ch == '(' || ch == ')'; + } ); + if( *paren == '(' ) return nullptr; // no nesting + if( paren == pend ) return nullptr; + return ++paren; + } + + const char * next_not( char ch ) const { + return std::find_if( p, pend, + [ch = TOUPPER(ch)]( char next ) { + return ch != next; + } ); + } + + const char * valid_next( const char *p, const std::string& valid ) const { + if( p == pend || p + 1 == pend ) return pend; + if( p[1] == '(' ) { + return match_paren(++p); + } + auto pv = std::find(valid.begin(), valid.end(), TOUPPER(p[1])); + return pv != valid.end()? ++p : nullptr; + } + const char * valid_next( const char *p, + bool first = true, char ch = '\0' ) const { + if( p == pend || p + 1 == pend ) return pend; + if( p[0] == '(' ) { + if( (p = match_paren(p)) == nullptr ) return nullptr; + } + if( p[0] == '(' ) return nullptr; // consecutive parentheses + + int index = first? 0 : 1; + if( !ch ) ch = *p; // use current character unless overridden + auto valid = followers.find(TOUPPER(ch)); + if( valid == followers.end() ) { + YYLTYPE loc(yylloc); + loc.first_column += int(p - begin); + error_msg( loc, "PICTURE: strange character %qc, giving up", ch ); + return nullptr; + } + return valid_next(p, valid->second[index]); + } + + const char * start() { // start modifies exclusions, but not p + auto pnext = p; + + switch(TOUPPER(p[0])) { + case comma: case dot: + // use decimal_is_comma() + // 4: .: "B0/,+Z*+-9E" + exclusions.dot++; + pnext = valid_next(p, "B0/,+Z*+-9E"); + break; + case '+': case '-': + // 6: +-: "B0/,.Z*Z*9VPPE" + exclusions.crdb++; + pnext = next_not(p[0]); + if( p + 1 < pnext ) { + exclusions.pluses++; + } + pnext = valid_next(--pnext, "B0/,.Z*Z*9VPPE"); + break; + case 'Z': case '*': + exclusions.stars++; + pnext = next_not(p[0]); + break; + case 'S': + // 19: S: "9VP" + pnext = valid_next(p, "9VP"); + break; + } + + /* + * "For fixed editing sign control, the currency symbol, when used, shall + * be either the leftmost symbol in character-string-1, optionally preceded + * by one of the symbols '+' or '-' " + */ + if( pnext ) { + if( p == pnext || p[0] == '+' || p[0] == '-' ) { + if( symbol_currency(*pnext) ) { + exclusions.currency++; + pnext = next_not(*pnext); + pnext = valid_next(--pnext, true, '$'); + } + } + } + + return pnext; + } + + const char * next() { // modify state; do not modify position + auto pnext = p; + auto loc(picture_t::loc); + loc.first_column += int(p - begin); + + if( is_crdb() ) { + if( exclusions.crdb++ ) { + error_msg( loc, "PICTURE: CR/DB and %c/%c may appear only once", '+', '-' ); + return nullptr; + } + if( p + 2 != pend ) { + error_msg( loc, "PICTURE: CR/DB must appear at the end" ); + return nullptr; + } + return pend; + } + + if( symbol_currency(p[0]) ) { + if( false && exclusions.currency++ ) { // not enforced + error_msg( loc, "PICTURE: CURRENCY SYMBOL sequence may appear at most once" ); + return nullptr; + } + return valid_next(p, ! exclusions.dot, '$'); + } + + switch(TOUPPER(p[0])) { + case '(': + return match_paren(p); + break; + case 'B': case '0': case '/': + pnext = valid_next(p); + break; + case comma: + if( decimal_is_comma() ) { + if( exclusions.dot++ ) { + error_msg( loc, "PICTURE: %qc: may appear at most once", p[0] ); + return nullptr; + } + pnext = valid_next(p, true, dot); + } else { + pnext = valid_next(p); + } + break; + case dot: + if( p + 1 == pend ) { + pnext = pend; + } else { + if( decimal_is_comma() ) { + pnext = valid_next(p, true, comma ); + } else { + if( exclusions.dot++ ) { + error_msg( loc, "PICTURE: %qc: may appear at most once", p[0] ); + return nullptr; + } + pnext = valid_next(p); + } + } + break; + + case '+': case '-': + // 7 is trailing sign; 13 & 14 are numeric. Leading sign handled by start(). + if( p + 1 == pend ) { + if( exclusions.crdb++ ) { + error_msg( loc, "PICTURE: %c/%c may appear at most once as a sign", '+', '-' ); + return nullptr; + } + pnext = pend; + } else { + pnext = next_not(p[0]); + if( p + 1 < pnext ) { + if( false && exclusions.pluses++ ) { // not enforced + error_msg( loc, "PICTURE: %qc: sequence may appear at most once", p[0] ); + return nullptr; + } + } + pnext = valid_next(pnext, ! exclusions.dot); + } + break; + + case 'Z': case '*': + if( false && exclusions.stars++ ) { // not enforced + error_msg( loc, "PICTURE: %qc: sequence may appear at most once", p[0] ); + return nullptr; + } + if( (pnext = next_not(p[0])) == nullptr ) return pnext; + pnext = valid_next(pnext, ! exclusions.dot); + break; + case 'P': + pnext = valid_next(pnext, ! exclusions.dot); + break; + case '9': + case 'A': case 'X': + case 'V': + case '1': + case 'N': + pnext = valid_next(p); + break; + case 'E': + pnext = valid_next(p, "+9"); + if( pnext && *pnext == '+' ) { + pnext = valid_next(p, "9"); + } + break; + default: + error_msg( loc, "PICTURE: %qc: invalid character", p[0] ); + return nullptr; + } + return pnext; + } + + public: + picture_t( const char *p, int len ) + : begin(p) + , p(p), pend(p + len) + , loc(yylloc) + { + assert(TOUPPER(*p) == 'P'); // as in PICTURE (or PICTURE IS) + // move p to start of picture string + while( (p = std::find_if(p, pend, fisspace)) != pend ) { + this->p = p = std::find_if(p, pend, + []( char ch ) { return ! fisspace(ch); } ); + } + assert(this->p != pend); + pos = this->p - begin; + } + + bool is_valid() { + if( !p ) return false; + if( (p = start()) == nullptr ) { + return false; + } + + while( p && p < pend) { + p = next(); + } + return p == pend; + } + + int starts_at() const { return pos; } +}; + +/* + * The Followers map gives 1 or 2 lists of valid characters following a + * character, the one in the key. If there are two lists, the correct one is + * determined by the caller based on the state of the picture string, i.e., + * what has been seen before. + */ +const std::map <char, picture_t::followings_t> picture_t::followers { + /* B0/ */ { 'B', {"B0/,.Z*+-9AXVPNE" } }, + /* B0/ */ { '0', {"B0/,.Z*+-9AXVPNE" } }, + /* B0/ */ { '/', {"B0/,.Z*+-9AXVPNE" } }, + /* , */ { ',', {"B0/,.Z*+-9VPE"} }, + /* . */ { '.', {"B0/,Z*+-9E"} }, + /* + { '+', "9" }, */ + /* +- */ { '+', {"B0/,.Z*9VPE", "" } }, + /* +- */ { '-', {"B0/,.Z*9VPE", "" } }, + /* CR/DB { 'C', "" }, */ + /* cs { 'c', "B0/,.Z*+-9VP" }, */ + /* cs { 'c', "+" }, */ + /* Z* */ { 'Z', {"B0/,.+Z*9VP", "B0/,+Z*"} }, + /* Z* */ { '*', {"B0/,.+Z*9VP", "B0/,+Z*"} }, + /* + */ { '+', {"B0/,.+-9VP", "B0/,+-"} }, + /* cs */ { '$', {"B0/,.+9VP", "B0/,+"} }, + /* 9 */ { '9', {"B0/,.+9AXVPE"} }, + /* AX */ { 'A', {"B0/9AX"} }, + /* AX */ { 'X', {"B0/9AX"} }, + /* S */ { 'S', {"9VP"} }, + /* V */ { 'V', {"B0/,+Z*+-9P"} }, + /* P */ { 'P', {"+VP", "B0/,+Z*9P"} }, + /* 1 */ { '1', {"1"} }, + /* N */ { 'N', {"B0/N"} }, + /* E */ { 'E', {"+9"} }, +}; + +/* + * Although picture_t::is_valid return a bool, it's not used. The validation + * routines emit messages where the error is detected. The entire string is + * subsequently parsed by the parser, which might otherwise accept an invalid + * string, but will usually emit a message of its own. + */ +static int +validate_picture() { + picture_t picture(yytext, yyleng); + picture.is_valid(); + return picture.starts_at(); +} + static inline bool is_integer_token( int *pvalue = NULL ) { int v, n = 0; @@ -559,7 +1091,7 @@ bool need_nume_set( bool tf ) { static int datetime_format_of( const char input[] ); static int symbol_function_token( const char name[] ) { - auto e = symbol_function( 0, name ); + const auto e = symbol_function( 0, name ); return e ? symbol_index(e) : 0; } @@ -633,6 +1165,10 @@ typed_name( const char name[] ) { { auto f = cbl_field_of(e); if( is_constant(f) ) { + if( f->data.initial ) { + int token = cbl_figconst_tok(f->data.initial); + if( token ) return token; + } int token = datetime_format_of(f->data.initial); if( token ) { yylval.string = xstrdup(f->data.initial); @@ -643,7 +1179,7 @@ typed_name( const char name[] ) { __attribute__((fallthrough)); case FldLiteralN: { - auto f = cbl_field_of(e); + const auto f = cbl_field_of(e); if( type == FldLiteralN ) { yylval.numstr.radix = f->has_attr(hex_encoded_e)? hexadecimal_e : decimal_e; @@ -677,7 +1213,7 @@ typed_name( const char name[] ) { return cbl_field_of(e)->level == 88? NAME88 : CLASS_NAME; break; default: - yywarn("%s:%d: invalid symbol type %s for symbol \"%s\"", + cbl_internal_error("%s:%d: invalid symbol type %s for symbol %qs", __func__, __LINE__, cbl_field_type_str(type), name); return NAME; } @@ -694,40 +1230,11 @@ tmpstring_append( int len ) { const char *extant = tmpstring == NULL ? "" : tmpstring; char *s = xasprintf("%s%.*s", extant, len, yytext); free(tmpstring); - if( yy_flex_debug && getenv(__func__) ) { - yywarn("%s: value is now '%s'", __func__, s); - } return tmpstring = s; } #define pop_return yy_pop_state(); return -static bool -wait_for_the_child(void) { - pid_t pid; - int status; - - if( (pid = wait(&status)) == -1 ) { - yywarn("internal error: no pending child CDF parser process"); - return false; - } - - if( WIFSIGNALED(status) ) { - yywarn( "process %d terminated by %s", pid, strsignal(WTERMSIG(status)) ); - return false; - } - if( WIFEXITED(status) ) { - if( WEXITSTATUS(status) != 0 ) { - yywarn("process %d exited with status %d", pid, status); - return false; - } - } - if( yy_flex_debug ) { - yywarn("process %d exited with status %d", pid, status); - } - return true; -} - static bool is_not = false; static uint64_t @@ -738,8 +1245,14 @@ integer_of( const char input[], bool is_hex = false) { if( input[0] == '0' ) input++; if( 1 != sscanf(input, fmt, &output) ) { - yywarn( "%s:%d: invalid integer '%s'", __func__, __LINE__, input ); + cbl_internal_error( "%s:%d: invalid integer '%s'", __func__, __LINE__, input ); } return output; } + + + + + + diff --git a/gcc/cobol/scan_post.h b/gcc/cobol/scan_post.h index dabb168..cb729b3 100644 --- a/gcc/cobol/scan_post.h +++ b/gcc/cobol/scan_post.h @@ -33,13 +33,14 @@ start_condition_str( int sc ) { const char *state = "???"; switch(sc) { case INITIAL: state = "INITIAL"; break; - case author_state: state = "author_state"; break; + case addr_of: state = "addr_of"; break; case basis: state = "basis"; break; case bool_state: state = "bool_state"; break; case cdf_state: state = "cdf_state"; break; case classify: state = "classify"; break; - case copy_state: state = "copy_state"; break; + case cobol_words: state = "cobol_words"; break; case comment_entries: state = "comment_entries"; break; + case copy_state: state = "copy_state"; break; case date_state: state = "date_state"; break; case datetime_fmt: state = "datetime_fmt"; break; case dot_state: state = "dot_state"; break; @@ -62,8 +63,8 @@ start_condition_str( int sc ) { case quoted2: state = "quoted2"; break; case quoteq: state = "quoteq"; break; case raising: state = "raising"; break; - case subscripts: state = "subscripts"; break; case sort_state: state = "sort_state"; break; + case subscripts: state = "subscripts"; break; } return state; } @@ -114,12 +115,13 @@ datetime_format_of( const char input[] ) { for( auto p = patterns; p < eopatterns; p++ ) { static const int cflags = REG_EXTENDED | REG_ICASE; - static char msg[80]; int erc; if( 0 != (erc = regcomp(&p->re, p->regex, cflags)) ) { + static char msg[80]; regerror(erc, &p->re, msg, sizeof(msg)); - yywarn("%s:%d: %s: %s", __func__, __LINE__, keyword_str(p->token), msg); + cbl_internal_error("%s:%d: %s: %s", __func__, __LINE__, + keyword_str(p->token), msg); } } } @@ -157,6 +159,8 @@ is_cdf_token( int token ) { case CDF_DISPLAY: case CDF_IF: case CDF_ELSE: case CDF_END_IF: case CDF_EVALUATE: case CDF_WHEN: case CDF_END_EVALUATE: + case CDF_PUSH: + case CDF_POP: return true; case CALL_COBOL: case CALL_VERBATIM: @@ -258,13 +262,12 @@ prelex() { while( is_cdf_token(token) ) { if( ! run_cdf(token) ) { - dbgmsg( ">>CDF parser failed" ); - return NO_CONDITION; + dbgmsg( ">>CDF parser failed, ydfchar %d", ydfchar ); } // Return the CDF's discarded lookahead token, if extant. token = ydfchar > 0? ydfchar : next_token(); if( token == NO_CONDITION && parsing.at_eof() ) { - return token = YYEOF; + return YYEOF; } // Reenter cdf parser only if next token could affect parsing state. @@ -291,12 +294,12 @@ prelex() { if( YY_START == field_state && level_needed() ) { switch( token ) { case NUMSTR: - if( yy_flex_debug ) yywarn("final token is NUMSTR"); + dbgmsg("final token is NUMSTR"); yylval.number = level_of(yylval.numstr.string); token = LEVEL; break; case YDF_NUMBER: - if( yy_flex_debug ) yywarn("final token is YDF_NUMBER"); + dbgmsg("final token is YDF_NUMBER"); yylval.number = ydflval.number; token = LEVEL; break; @@ -373,7 +376,7 @@ yylex(void) { token = prelex(); if( yy_flex_debug ) { if( parsing.in_cdf() ) { - dbgmsg( "%s:%d: %s routing %s to CDF parser", __func__, __LINE__, + dbgmsg( "%s:%d: <%s> routing %s to CDF parser", __func__, __LINE__, start_condition_is(), keyword_str(token) ); } else if( !parsing.on() ) { dbgmsg( "eating %s because conditional compilation is FALSE", @@ -399,3 +402,12 @@ yylex(void) { return token; } + +/* + * Token name<->string utilities + */ + +// tokens.h is generated as needed from parse.h with tokens.h.gen +current_tokens_t::tokenset_t::tokenset_t() { +#include "token_names.h" +}; diff --git a/gcc/cobol/show_parse.h b/gcc/cobol/show_parse.h index ad26584..a5f1467 100644 --- a/gcc/cobol/show_parse.h +++ b/gcc/cobol/show_parse.h @@ -42,9 +42,6 @@ // 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; @@ -57,11 +54,20 @@ extern bool cursor_at_sol; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +/* + * In syntax-only mode, return immediately. By using these macros, the parser + * can call code-generation functions unconditionally because it does not rely + * on the results. + */ #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) +#define RETURN_XX_PARSE_ONLY(XX) \ + do { if( mode_syntax_only() ) return XX; } while(0) + +#define SHOW_PARSE1 if(bSHOW_PARSE) +#define SHOW_PARSE RETURN_IF_PARSE_ONLY; if(bSHOW_PARSE) +#define SHOW_IF_PARSE(XX) RETURN_XX_PARSE_ONLY((XX)); 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 @@ -134,17 +140,23 @@ extern bool cursor_at_sol; fprintf(stderr, "%s", (b).field->name); \ if( (b).field->type == FldLiteralA || (b).field->type == FldLiteralN ) \ { \ - fprintf(stderr, " \"%s\"", (b).field->data.initial); \ + size_t nbytes; \ + const char *literal = __gg__iconverter((b).field->codeset.encoding, \ + DEFAULT_SOURCE_ENCODING, \ + (b).field->data.initial, \ + strlen((b).field->data.initial), \ + &nbytes); \ + fprintf(stderr, " \"%s\"", literal); \ } \ else \ { \ fprintf(stderr, "<%s>", cbl_field_type_str((b).field->type)); \ } \ } \ - if( (b).nsubscript) \ + if( (b).nsubscript()) \ { \ fprintf(stderr,"("); \ - for(size_t jjj=0; jjj<(b).nsubscript; jjj++) \ + for(size_t jjj=0; jjj<(b).nsubscript(); jjj++) \ { \ if(jjj) \ { \ @@ -170,11 +182,21 @@ extern bool cursor_at_sol; } \ else \ { \ - fprintf(stderr, " %p:%s (%s)", (void*)b, b->name, b->type_str()); \ + fprintf(stderr, " %p:%s (%s)", static_cast<void*>(b), b->name, b->type_str()); \ } \ show_parse_sol = false; \ } while(0); +// Use this version when b is known to be valid. This is necessary to quiet +// cppcheck nullPointerRedundantCheck warnings +#define SHOW_PARSE_LABEL_OK(a, b) \ + do \ + { \ + fprintf(stderr, "%s", a); \ + fprintf(stderr, " %p:%s (%s)", static_cast<void*>(b), b->name, b->type_str()); \ + show_parse_sol = false; \ + } while(0); + #define TRACE1 if(bTRACE1) #define TRACE1_HEADER do \ { \ @@ -205,6 +227,7 @@ extern bool cursor_at_sol; #define TRACE1_FIELD_VALUE(a, field, b) \ do \ { \ + gcc_assert(field); \ cursor_at_sol=false; \ if ( field->type == FldConditional ) \ { \ @@ -331,13 +354,13 @@ extern bool cursor_at_sol; else \ { \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal( (b).field->name ? (b).field->name:"")); \ - if( b.nsubscript ) \ + if( b.nsubscript() ) \ { \ gg_fprintf(trace_handle, 0, "("); \ - for(unsigned int i=0; i<b.nsubscript; i++) \ + 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 ) \ + if( i<b.nsubscript()-1 ) \ { \ gg_fprintf(trace_handle, 0, " "); \ } \ @@ -417,31 +440,51 @@ extern bool cursor_at_sol; } 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(); \ - } \ +// by non-null. (The useless calls to abort() are because cppcheck doesn't +// understand that gcc_unreachable doesn't return); + +// Use this after doing any SHOW_PARSE stuff, to avoid cppcheck complaints +// about nullPointerRedundantCheck +#define CHECK_FIELD(a) \ + do { \ + if(!a) \ + { \ + cbl_internal_error("%s: parameter %<" #a "%> is NULL", __func__); \ + } \ + if( !a->var_decl_node ) \ + { \ + cbl_internal_error("%s: parameter %<" #a "%> is variable " \ + "%s<%s> with NULL %<var_decl_node%>", \ + __func__, \ + a->name, \ + cbl_field_type_str(a->type) ); \ + } \ + } while(0); + +// This version is a bit more lax, for special cases +#define CHECK_FIELD2(a) \ + do { \ + if(!a) \ + { \ + cbl_internal_error("%s: parameter %<" #a "%> is NULL", __func__); \ + } \ + if( !a->var_decl_node && a->type != FldConditional && a->type != FldLiteralA) \ + { \ + cbl_internal_error("%s: parameter %<" #a "%> is variable " \ + "%s<%s> with NULL %<var_decl_node%>", \ + __func__, \ + a->name, \ + cbl_field_type_str(a->type) ); \ + } \ + } while(0); + + +#define CHECK_LABEL(a) \ + do{ \ + if(!a) \ + { \ + cbl_internal_error("%s: parameter %<" #a "%> is NULL", __func__); \ + } \ }while(0); #ifdef INCORPORATE_ANALYZER @@ -453,7 +496,7 @@ class ANALYZE int level; inline static int analyze_level=1; public: - ANALYZE(const char *func_) : func(func_) + ANALYZE(const char *func_) : func(func_) // cppcheck-suppress noExplicitConstructor { level = 0; if( getenv("Analyze") ) @@ -497,10 +540,11 @@ class ANALYZE } }; #else +// cppcheck-suppress ctuOneDefinitionRuleViolation class ANALYZE { public: - ANALYZE(const char *) + explicit ANALYZE(const char *) { } ~ANALYZE() diff --git a/gcc/cobol/structs.cc b/gcc/cobol/structs.cc index 39c8a42..2b13b1f 100644 --- a/gcc/cobol/structs.cc +++ b/gcc/cobol/structs.cc @@ -55,7 +55,6 @@ #include "cobol-system.h" #include "coretypes.h" #include "tree.h" -#define HOWEVER_GCC_DEFINES_TREE 1 #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -158,7 +157,6 @@ tree cblc_field_pp_type_node; tree cblc_file_type_node; tree cblc_file_p_type_node; tree cblc_goto_type_node; -tree cblc_int128_type_node; // The following functions return type_decl nodes for the various structures @@ -178,16 +176,18 @@ create_cblc_field_t() struct cblc_field_t *parent;// This field's immediate parent field size_t occurs_lower; // non-zero for a table size_t occurs_upper; // non-zero for a table - size_t attr; // See cbl_field_attr_t + uint64_t attr; // See cbl_field_attr_t signed char type; // A one-byte copy of cbl_field_type_t signed char level; // This variable's level in the naming heirarchy signed char digits; // Digits specified in PIC string; e.g. 5 for 99v999 signed char rdigits; // Digits to the right of the decimal point. 3 for 99v999 + cbl_encoding_t encoding; // Same as cbl_field_t::codeset::encoding + int alphabet; // Same as cbl_field_t::codeset::language } cblc_field_t; */ tree retval = NULL_TREE; retval = gg_get_filelevel_struct_type_decl( "cblc_field_t", - 16, + 17, UCHAR_P, "data", SIZE_T, "capacity", SIZE_T, "allocated", @@ -198,12 +198,13 @@ create_cblc_field_t() CHAR_P, "parent", SIZE_T, "occurs_lower", SIZE_T, "occurs_upper", - SIZE_T, "attr", + ULONGLONG, "attr", SCHAR, "type", SCHAR, "level", SCHAR, "digits", SCHAR, "rdigits", - INT, "dummy"); // Needed to make it an even number of 32-bit ints + INT, "encoding", + INT, "alphabet"); retval = TREE_TYPE(retval); return retval; @@ -218,6 +219,7 @@ create_cblc_file_t() typedef struct cblc_file_t { char *name; // This is the name of the structure; might be the name of an environment variable + size_t symbol_index; // The symbol table index of the related cbl_file_t structure char *filename; // The name of the file to be opened FILE *file_pointer; // The FILE *pointer cblc_field_t *default_record; // The record_area @@ -246,14 +248,17 @@ typedef struct cblc_file_t int recent_char; // This is the most recent char sent to the file int recent_key; cblc_file_prior_op_t prior_op; + int encoding; // Actually cbl_encoding_t + int alphabet; // Actually cbl_encoding_t int dummy // We need an even number of INT } cblc_file_t; */ tree retval = NULL_TREE; retval = gg_get_filelevel_struct_type_decl( "cblc_file_t", - 30, + 33, CHAR_P, "name", + SIZE_T, "symbol_table_index", CHAR_P, "filename", FILE_P, "file_pointer", cblc_field_p_type_node, "default_record", @@ -282,39 +287,13 @@ typedef struct cblc_file_t INT, "recent_char", INT, "recent_key", INT, "prior_op", + INT, "encoding", // Actually cbl_encoding_t + INT, "alphabet", INT, "dummy"); retval = TREE_TYPE(retval); return retval; } -static tree -create_cblc_int128_t() - { - /* - // GCC-13 can't initialize __int64 variables, which is something we need to - // be able to do. So, I created this union. The array can be initialized, - // and thus we do an end run around the problem. Annoying, but not fatally - // so. - - typedef union cblc_int128_t - { - unsigned char array16[16]; - __uint128 uval128; - __int128 sval128; - } cblc_int128_t; - */ - tree retval = NULL_TREE; - tree array_type = build_array_type_nelts(UCHAR, 16); - retval = gg_get_filelevel_union_type_decl( - "cblc_int128_t", - 3, - array_type, "array16" , - UINT128, "uval128" , - INT128, "sval128" ); - retval = TREE_TYPE(retval); - return retval; - } - void create_our_type_nodes() { @@ -327,7 +306,6 @@ create_our_type_nodes() cblc_field_pp_type_node = build_pointer_type(cblc_field_p_type_node); cblc_file_type_node = create_cblc_file_t(); cblc_file_p_type_node = build_pointer_type(cblc_file_type_node); - cblc_int128_type_node = create_cblc_int128_t(); } } diff --git a/gcc/cobol/structs.h b/gcc/cobol/structs.h index 618d8f0..47a78b4 100644 --- a/gcc/cobol/structs.h +++ b/gcc/cobol/structs.h @@ -55,7 +55,6 @@ extern GTY(()) tree cblc_field_pp_type_node; extern GTY(()) tree cblc_file_type_node; extern GTY(()) tree cblc_file_p_type_node; extern GTY(()) tree cblc_goto_type_node; -extern GTY(()) tree cblc_int128_type_node; extern void create_our_type_nodes(); diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc index 38c7a2e..a177fcd 100644 --- a/gcc/cobol/symbols.cc +++ b/gcc/cobol/symbols.cc @@ -28,8 +28,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// cppcheck-suppress-file duplicateBreak + +#include "config.h" #include <fstream> // Before cobol-system because it uses poisoned functions #include "cobol-system.h" + +#include "coretypes.h" +#include "tree.h" + #include <search.h> #include <iconv.h> #include "../../libgcobol/ec.h" @@ -40,6 +47,7 @@ #include "inspect.h" #include "../../libgcobol/io.h" #include "genapi.h" +#include "../../libgcobol/charmaps.h" #pragma GCC diagnostic ignored "-Wunused-result" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" @@ -51,7 +59,7 @@ class symbol_pair_t { const symbol_elem_t *first, *last; public: - symbol_pair_t( const symbol_elem_t * first, const symbol_elem_t * end = NULL ) + explicit symbol_pair_t( const symbol_elem_t * first, const symbol_elem_t * end = NULL ) : first(first), last(end) {} @@ -89,7 +97,7 @@ static struct symbol_table_t { exception_condition, very_true, very_false; registers_t() { file_status = linage_counter = return_code = - exception_condition = very_true = very_false = 0; + exception_condition = very_true = very_false = 0; } } registers; @@ -131,11 +139,9 @@ static struct symbol_table_t { static symbol_table_t& symbol_table_extend() { - static FILE *mapped; if( symbols.nelem == 0 ) { // first time: create file & set initial capacity - assert(mapped == NULL && symbols.fd == -1); - + FILE *mapped; if( (mapped = tmpfile()) == NULL ) { cbl_err( "could not create temporary file for symbol table"); } @@ -155,8 +161,8 @@ symbol_table_extend() { off_t len = symbols.size(); if( 0 != ftruncate(symbols.fd, len) ) { - cbl_err( "%s:%d:could not extend symbol table to %zu elements", - __func__, __LINE__, symbols.capacity); + cbl_err( "%s:%d: could not extend symbol table to %lu elements", + __func__, __LINE__, gb4(symbols.capacity)); } /* @@ -224,6 +230,12 @@ cbl_span_t::from_field() { assert(from); return from->field; } cbl_field_t * cbl_span_t::len_field() { assert(len); return len->field; } +cbl_ffi_arg_t::cbl_ffi_arg_t() + : optional(false) + , crv(by_reference_e) + , attr(none_of_e) +{} + cbl_ffi_arg_t:: cbl_ffi_arg_t( cbl_refer_t* refer, cbl_ffi_arg_attr_t attr ) : optional(false) @@ -245,127 +257,21 @@ cbl_ffi_arg_t( cbl_ffi_crv_t crv, if( refer && refer != refer->empty() ) delete refer; } -#define ERROR_FIELD(F, ...) \ - do{ \ - auto loc = symbol_field_location(field_index(F)); \ - error_msg(loc, __VA_ARGS__); \ +#define ERROR_FIELD(F, ...) \ + do{ \ + auto loc = symbol_field_location(field_index(F)); \ + error_msg(loc, __VA_ARGS__); \ } while(0) -cbl_field_t * -symbol_valid_udf_args( size_t function, std::list<cbl_refer_t> args ) { - auto L = cbl_label_of(symbol_at(function)); - if( ! L->returning ) { - dbgmsg("logic error: %s does not define RETURNING", L->name); - return NULL; - } - auto e = std::find_if( symbol_at(function), symbols_end(), - []( auto symbol ) { - if( symbol.type == SymDataSection ) { - auto section(symbol.elem.section); - return section.type == linkage_sect_e; - } - return false; - } ); - for( auto arg : args ) { - size_t iarg(1); - e++; // skip over linkage_sect_e, which appears after the function - if( e->type != SymField ) { - ERROR_FIELD(arg.field, - "FUNCTION %s has no defined parameter matching arg %zu, '%s'", - L->name, iarg, arg.field->name ); - return NULL; - } - - auto tgt = cbl_field_of(e); - - if( ! valid_move(tgt, arg.field) ) { - ERROR_FIELD(tgt, "FUNCTION %s arg %zu, '%s' cannot be passed to %s, type %s", - L->name, iarg, arg.field->pretty_name(), - tgt->pretty_name(), 3 + cbl_field_type_str(tgt->type) ); - return NULL; - } - } - return cbl_field_of(symbol_at(L->returning)); -} - static const struct cbl_occurs_t nonarray = cbl_occurs_t(); -static const struct cbl_field_t empty_float = { - 0, FldFloat, FldInvalid, - intermediate_e, - 0, 0, 0, nonarray, 0, "", - 0, cbl_field_t::linkage_t(), - {16, 16, 32, 0, NULL}, NULL }; - -static const struct cbl_field_t empty_comp5 = { - 0, FldNumericBin5, FldInvalid, - signable_e | intermediate_e, - 0, 0, 0, nonarray, 0, "", - 0, cbl_field_t::linkage_t(), - {16, 16, MAX_FIXED_POINT_DIGITS, 0, NULL}, NULL }; - #if 0 # define CONSTANT_E constant_e #else # define CONSTANT_E intermediate_e #endif -static struct cbl_field_t empty_literal = { - 0, FldInvalid, FldInvalid, CONSTANT_E, - 0, 0, 0, nonarray, 0, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; - -static const struct cbl_field_t empty_conditional = { - 0, FldConditional, FldInvalid, intermediate_e, - 0, 0, 0, nonarray, 0, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; - - -/** - * Debug register record - 01 DEBUG-ITEM. - 02 DEBUG-LINE PIC X(6). - 02 FILLER PIC X VALUE SPACE. - 02 DEBUG-NAME PIC X(30). - 02 FILLER PIC X VALUE SPACE. - 02 DEBUG-SUB-1 PIC S9999 SIGN IS LEADING SEPARATE CHARACTER. - 02 FILLER PIC X VALUE SPACE. - 02 DEBUG-SUB-2 PIC S9999 SIGN IS LEADING SEPARATE CHARACTER. - 02 FILLER PIC X VALUE SPACE. - 02 DEBUG-SUB-3 PIC S9999 SIGN IS LEADING SEPARATE CHARACTER. - 02 FILLER PIC X VALUE SPACE. - 02 DEBUG-CONTENTS PIC X(76). - **/ - -static cbl_field_t debug_registers[] = { - { 0, FldGroup, FldInvalid, global_e, 0,0,1, nonarray, 0, - "DEBUG-ITEM", 0, {}, {132,132,0,0, NULL}, NULL }, - { 0, FldAlphanumeric, FldInvalid, global_e, 0,0,2, nonarray, 0, - "DEBUG-LINE", 0, {}, {6,6,0,0, " "}, NULL }, - { 0, FldAlphanumeric, FldInvalid, 0, 0,0,2, nonarray, 0, - "FILLER", 0, {}, {1,1,0,0, " "}, NULL }, - { 0, FldAlphanumeric, FldInvalid, global_e, 0,0,2, nonarray, 0, - "DEBUG-NAME", 0, {}, {30,30,0,0, NULL}, NULL }, - { 0, FldAlphanumeric, FldInvalid, 0, 0,0,2, nonarray, 0, - "FILLER", 0, {}, {1,1,0,0, " "}, NULL }, - { 0, FldNumericDisplay, FldInvalid, signable_e | global_e | leading_e | separate_e, 0,0,2, nonarray, 0, - "DEBUG-SUB-1", 0, {}, {5,5,3,0, NULL}, NULL }, - { 0, FldAlphanumeric, FldInvalid, 0, 0,0,2, nonarray, 0, - "FILLER", 0, {}, {1,1,0,0, " "}, NULL }, - { 0, FldNumericDisplay, FldInvalid, signable_e | global_e | leading_e | separate_e, 0,0,2, nonarray, 0, - "DEBUG-SUB-2", 0, {}, {5,5,3,0, NULL}, NULL }, - { 0, FldAlphanumeric, FldInvalid, 0, 0,0,2, nonarray, 0, - "FILLER", 0, {}, {1,1,0,0, " "}, NULL }, - { 0, FldNumericDisplay, FldInvalid, signable_e | global_e | leading_e | separate_e, 0,0,2, nonarray, 0, - "DEBUG-SUB-3", 0, {}, {5,5,3,0, NULL}, NULL }, - { 0, FldAlphanumeric, FldInvalid, 0, 0,0,2, nonarray, 0, - "FILLER", 0, {}, {1,1,0,0, " "}, NULL }, - { 0, FldAlphanumeric, FldInvalid, signable_e | global_e, 0,0,2, nonarray, 0, - "DEBUG-CONTENTS", 0, {}, {76,76,0,0, NULL}, NULL }, -}; class group_size_t { size_t size; @@ -380,30 +286,14 @@ class group_size_t { enum { constq = constant_e | quoted_e }; -static cbl_field_t special_registers[] = { - { 0, FldNumericDisplay, FldInvalid, 0, 0, 0, 0, nonarray, 0, "_FILE_STATUS", - 0, {}, {2,2,2,0, NULL}, NULL }, - { 0, FldNumericBin5, FldInvalid, 0, 0, 0, 0, nonarray, 0, "UPSI-0", - 0, {}, {2,2,4,0, NULL}, NULL }, - { 0, FldNumericBin5, FldInvalid, 0, 0, 0, 0, nonarray, 0, "RETURN-CODE", - 0, {}, {2,2,4,0, NULL}, NULL }, - { 0, FldNumericBin5, FldInvalid, 0, 0, 0, 0, nonarray, 0, "LINAGE-COUNTER", - 0, {}, {2,2,4,0, NULL}, NULL }, - { 0, FldLiteralA, FldInvalid, 0, 0, 0, 0, nonarray, 0, "_dev_stdin", - 0, {}, {0,0,0,0, "/dev/stdin"}, NULL }, - { 0, FldLiteralA, FldInvalid, constq, 0, 0, 0, nonarray, 0, "_dev_stdout", - 0, {}, {0,0,0,0, "/dev/stdout"}, NULL }, - { 0, FldLiteralA, FldInvalid, constq, 0, 0, 0, nonarray, 0, "_dev_stderr", - 0, {}, {0,0,0,0, "/dev/stderr"}, NULL }, - { 0, FldLiteralA, FldInvalid, constq, 0, 0, 0, nonarray, 0, "_dev_null", - 0, {}, {0,0,0,0, "/dev/null"}, NULL }, - -}; - static symbol_elem_t -elementize( cbl_field_t& field ) { +elementize( const cbl_field_t& field ) { symbol_elem_t sym (SymField); sym.elem.field = field; + // Dubner did the following because he didn't feel like creating yet another + // cbl_field_t constructor that included the hardcoded encoding for the + // global special registers. + sym.elem.field.codeset.set(); return sym; } @@ -454,12 +344,6 @@ special_pair_cmp( const cbl_special_name_t& key, const cbl_special_name_t& elem ) { const bool matched = key.id == elem.id || 0 == strcasecmp(key.name, elem.name); - if( getenv(__func__) ) { - dbgmsg("%s:%d: key: id=%2d, %s", __func__, __LINE__, key.id, key.name); - dbgmsg("%s:%d: elem: id=%2d, %s => %s", __func__, __LINE__, - elem.id, elem.name, matched? "match" : "no match"); - } - return matched; } @@ -577,9 +461,6 @@ symbol_elem_cmp( const void *K, const void *E ) case SymDataSection: return k->elem.section.type == e->elem.section.type ? 0 : 1; break; - case SymFunction: - return strcmp(k->elem.function.name, e->elem.function.name); - break; case SymField: if( has_parent(k) && cbl_field_of(k)->parent != cbl_field_of(e)->parent ) { return 1; @@ -630,6 +511,9 @@ symbol_elem_cmp( const void *K, const void *E ) case SymSpecial: return special_pair_cmp(k->elem.special, e->elem.special)? 0 : 1; break; + case SymLocale: + return strcasecmp(k->elem.locale.name, e->elem.locale.name); + break; case SymAlphabet: return strcasecmp(k->elem.alphabet.name, e->elem.alphabet.name); break; @@ -796,9 +680,25 @@ symbol_special( size_t program, const char name[] ) } struct symbol_elem_t * +symbol_locale( size_t program, const char name[] ) +{ + cbl_locale_t locale(name); + assert(strlen(name) < sizeof locale.name); + strcpy(locale.name, name); + + struct symbol_elem_t key(SymLocale, program), *e; + key.elem.locale = locale; + + e = static_cast<struct symbol_elem_t *>(lfind( &key, symbols.elems, + &symbols.nelem, sizeof(key), + symbol_elem_cmp ) ); + return e; +} + +struct symbol_elem_t * symbol_alphabet( size_t program, const char name[] ) { - cbl_alphabet_t alphabet(YYLTYPE(), custom_encoding_e); + cbl_alphabet_t alphabet(YYLTYPE(), custom_encoding_e); // cppcheck-suppress syntaxError assert(strlen(name) < sizeof alphabet.name); strcpy(alphabet.name, name); @@ -865,7 +765,7 @@ cbl_field_attr_str( cbl_field_attr_t attr ) { case function_e: return "function"; case quoted_e: return "quoted"; case filler_e: return "filler"; - case _spare_e: return "temporary"; + case register_e: return "register"; case intermediate_e: return "intermediate"; case embiggened_e: return "embiggened"; case all_alpha_e: return "all_alpha"; @@ -884,7 +784,7 @@ cbl_field_attr_str( cbl_field_attr_t attr ) { case leading_e: return "leading"; case separate_e: return "separate"; case envar_e: return "envar"; - case dnu_1_e: return "dnu_1"; + case encoded_e: return "encoded"; case bool_encoded_e: return "bool"; case hex_encoded_e: return "hex"; case depends_on_e: return "depends_on"; @@ -905,24 +805,24 @@ cbl_field_t::size() const { return field_size(this); } -size_t +uint64_t cbl_field_t::set_attr( cbl_field_attr_t attr ) { if( attr == signable_e ) { if( ! has_attr(attr) && this->var_decl_node != NULL ) { parser_field_attr_set(this, attr); } } - return this->attr |= size_t(attr); + return this->attr |= uint64_t(attr); } -size_t +uint64_t cbl_field_t::clear_attr( cbl_field_attr_t attr ) { if( attr == signable_e ) { if( this->var_decl_node != nullptr && has_attr(attr) ) { parser_field_attr_set(this, attr, false); } } - return this->attr &= ~size_t(attr); + return this->attr &= ~uint64_t(attr); } static uint32_t @@ -978,13 +878,6 @@ update_block_offsets( struct symbol_elem_t *block) uint32_t offset = cbl_field_of(block)->offset; const uint32_t block_level = cbl_field_of(block)->level; - if( getenv(__func__) ) { - cbl_field_t *field = cbl_field_of(block); - dbgmsg( "%s: offset is %3zu for %2u %-30s #%3zu P%zu", - __func__, field->offset, field->level, field->name, - symbol_index(block), field->parent ); - } - struct symbol_elem_t *e = block; for( ++e; e < symbols_end(); e++ ) { if( e->type != SymField ) { @@ -1014,12 +907,6 @@ update_block_offsets( struct symbol_elem_t *block) offset += field_memsize(field); } - if( getenv(__func__) ) { - dbgmsg( "%s: offset is %3zu for %2u %-30s #%3zu P%zu", - __func__, field->offset, field->level, field->name, - symbol_index(e), field->parent ); - } - if( field->type == FldGroup ) { e = update_block_offsets(e) - 1; } @@ -1050,7 +937,7 @@ end_of_group( const cbl_field_t *group, const cbl_field_t *field ) { class eog_t { const cbl_field_t * group; public: - eog_t( const symbol_elem_t *e ) : group(cbl_field_of(e)) {} + explicit eog_t( const symbol_elem_t *e ) : group(cbl_field_of(e)) {} bool operator()( symbol_elem_t& e ) { return e.type == SymField && end_of_group(group, cbl_field_of(&e)); @@ -1070,7 +957,7 @@ end_of_group( size_t igroup ) { if( e->program != group->program ) return isym; if( e->type == SymLabel ) return isym; // end of data division if( e->type == SymField ) { - auto f = cbl_field_of(e); + const cbl_field_t * f = cbl_field_of(e); if( f->level == LEVEL77 || f->level == 66 ) return isym; if( f->level == 1 && f->parent != igroup ) { return isym; @@ -1081,7 +968,7 @@ end_of_group( size_t igroup ) { } eog_t eog(symbol_at(igroup)); - symbol_elem_t *e = std::find_if( symbols_begin(++igroup), symbols_end(), eog ); + const symbol_elem_t *e = std::find_if( symbols_begin(++igroup), symbols_end(), eog ); return e - symbols_begin(); } @@ -1105,8 +992,8 @@ symbol_field_capacity( const cbl_field_t *field ) { size_t size = std::accumulate( symbol_at(bog), symbol_at_impl(eog), 0, sym_field_size::capacity ); - if(true) dbgmsg("%s: %02u %s.data.capacity was computed as %zu", __func__, - field->level, field->name, size); + if(true) dbgmsg("%s: %02u %s.data.capacity was computed as " HOST_SIZE_T_PRINT_UNSIGNED, + __func__, field->level, field->name, (fmt_size_t)size); return size; } @@ -1120,14 +1007,15 @@ has_odo( const symbol_elem_t& e ) { struct cbl_field_t * symbol_find_odo_debug( cbl_field_t * field ) { size_t bog = field_index(field), eog = end_of_group(bog); - dbgmsg("%s: %s is #%zu - #%zu of %zu, ends at %s", __func__, - field->name, bog, eog, symbols.nelem, + dbgmsg("%s: %s is #" HOST_SIZE_T_PRINT_UNSIGNED " - #" HOST_SIZE_T_PRINT_UNSIGNED + " of " HOST_SIZE_T_PRINT_UNSIGNED ", ends at %s", __func__, + field->name, (fmt_size_t)bog, (fmt_size_t)eog, (fmt_size_t)symbols.nelem, eog == symbols.nelem? "[end]" : cbl_field_of(symbol_at(eog))->name ); auto e = std::find_if( symbol_at(bog), symbol_at_impl(eog, true), has_odo ); if( e != symbol_at_impl(eog, true) ) { - dbgmsg("%s: %s has ODO at #%zu (return '%s')", __func__, - field->name, symbol_index(e), + dbgmsg("%s: %s has ODO at #" HOST_SIZE_T_PRINT_UNSIGNED " (return '%s')", __func__, + field->name, (fmt_size_t)symbol_index(e), cbl_field_of(e)->name ); } return e == symbol_at_impl(eog, true)? NULL : cbl_field_of(e); @@ -1135,8 +1023,7 @@ symbol_find_odo_debug( cbl_field_t * field ) { // Return OCCURS DEPENDING ON table subordinate to field, if any. struct cbl_field_t * -symbol_find_odo( cbl_field_t * field ) { - if( getenv(__func__) ) return symbol_find_odo_debug(field); +symbol_find_odo( const cbl_field_t * field ) { size_t bog = field_index(field), eog = end_of_group(bog); auto e = std::find_if( symbol_at(bog), symbol_at_impl(eog, true), has_odo ); return e == symbol_at_impl(eog, true)? NULL : cbl_field_of(e); @@ -1152,8 +1039,8 @@ symbols_dump( size_t first, bool header ) { if( !yydebug ) return 0; if( header ) { - fprintf(stderr, "Symbol Table has %zu elements\n", - symbols_end() - symbols_begin()); + fprintf(stderr, "Symbol Table has " HOST_SIZE_T_PRINT_UNSIGNED " elements\n", + (fmt_size_t)(symbols_end() - symbols_begin())); } for( struct symbol_elem_t *e = symbols_begin(first); e < symbols_end(); e++ ) { @@ -1161,25 +1048,22 @@ symbols_dump( size_t first, bool header ) { switch(e->type) { case SymFilename: - s = xasprintf("%4zu %-18s %s", e->program, + s = xasprintf("%4" GCC_PRISZ "u %-18s %s", (fmt_size_t)e->program, "Filename", e->elem.filename); break; case SymDataSection: - s = xasprintf("%4zu %-18s line %d", e->program, + s = xasprintf("%4" GCC_PRISZ "u %-18s line %d", (fmt_size_t)e->program, cbl_section_of(e)->name(), cbl_section_of(e)->line); break; - case SymFunction: - s = xasprintf("%4zu %-15s %s", e->program, - "Function", e->elem.function.name); - break; case SymField: { auto field = cbl_field_of(e); char *odo_str = NULL; if( field->occurs.depending_on != 0 ) { - odo_str = xasprintf("odo %zu", field->occurs.depending_on ); + odo_str = xasprintf("odo " HOST_SIZE_T_PRINT_UNSIGNED, + (fmt_size_t)field->occurs.depending_on ); } ninvalid += cbl_field_of(e)->type == FldInvalid? 1 : 0; - s = xasprintf("%4zu %-18s %s (%s)", e->program, + s = xasprintf("%4" GCC_PRISZ "u %-18s %s (%s)", (fmt_size_t)e->program, cbl_field_type_str(cbl_field_of(e)->type) + 3, field_str(cbl_field_of(e)), odo_str? odo_str : @@ -1187,7 +1071,7 @@ symbols_dump( size_t first, bool header ) { } break; case SymLabel: - s = xasprintf("%4zu %-18s %s", e->program, + s = xasprintf("%4" GCC_PRISZ "u %-18s %s", (fmt_size_t)e->program, "Labe1l", e->elem.label.str()); if( LblProgram == cbl_label_of(e)->type ) { const auto& L = *cbl_label_of(e); @@ -1199,31 +1083,35 @@ symbols_dump( size_t first, bool header ) { } break; case SymSpecial: - s = xasprintf("%4zu %-18s id=%2d, %s", e->program, + s = xasprintf("%4" GCC_PRISZ "u %-18s id=%2d, %s", (fmt_size_t)e->program, "Special", e->elem.special.id, e->elem.special.name); break; case SymAlphabet: - s = xasprintf("%4zu %-18s encoding=%2d, '%s'", e->program, "Alphabet", + s = xasprintf("%4" GCC_PRISZ "u %-18s encoding=%2d, '%s'", + (fmt_size_t)e->program, "Alphabet", int(e->elem.alphabet.encoding), e->elem.alphabet.name); break; case SymFile: - s = xasprintf("%4zu %-18s %-20s", e->program, + s = xasprintf("%4" GCC_PRISZ "u %-18s %-20s", (fmt_size_t)e->program, "File", e->elem.file.name); { char same_as[26] = ""; if( cbl_file_of(e)->same_record_as > 0 ) { - sprintf(same_as, "s%3zu", cbl_file_of(e)->same_record_as); + sprintf(same_as, "s%3" GCC_PRISZ "u", + (fmt_size_t)cbl_file_of(e)->same_record_as); } const char *type = file_org_str(e->elem.file.org); char *part = s; - s = xasprintf("%s %-4s %s %s %s{%zu-%zu} status=#%zu", + s = xasprintf("%s %-4s %s %s %s{" HOST_SIZE_T_PRINT_UNSIGNED "-" + HOST_SIZE_T_PRINT_UNSIGNED "} status=#" + HOST_SIZE_T_PRINT_UNSIGNED, part, same_as, type, e->elem.file.keys_str(), cbl_file_of(e)->varies()? "varies " : "", - cbl_file_of(e)->varying_size.min, - cbl_file_of(e)->varying_size.max, - cbl_file_of(e)->user_status); + (fmt_size_t)cbl_file_of(e)->varying_size.min, + (fmt_size_t)cbl_file_of(e)->varying_size.max, + (fmt_size_t)cbl_file_of(e)->user_status); free(part); } break; @@ -1231,7 +1119,8 @@ symbols_dump( size_t first, bool header ) { dbgmsg("%s: cannot dump symbol type %d", __func__, e->type); continue; } - fprintf(stderr, "%4zu: %s\n", e - symbols_begin(), s); + fprintf(stderr, "%4" GCC_PRISZ "u: %s\n", + (fmt_size_t)(e - symbols_begin()), s); free(s); } return ninvalid; @@ -1311,7 +1200,7 @@ static struct symbol_elem_t * // If an 01 record exists for the FD/SD, use its capacity as the // default_record capacity. if( p != symbols_end() ) { - auto record = cbl_field_of(p); + const cbl_field_t * record = cbl_field_of(p); assert(record->level == 1); e = calculate_capacity(p); auto record_size = std::max(record->data.memsize, @@ -1343,7 +1232,8 @@ static struct symbol_elem_t * } if(yydebug && group->type != FldGroup) { - dbgmsg("Field #%zu '%s' is not a group", symbol_index(e), group->name); + dbgmsg("Field #" HOST_SIZE_T_PRINT_UNSIGNED " '%s' is not a group", + (fmt_size_t)symbol_index(e), group->name); symbols_dump(symbols.first_program, true); } if( group->type == FldInvalid ) return e; @@ -1373,10 +1263,6 @@ static struct symbol_elem_t * // Print accumulating details for one group to debug log. bool details = false; - if( yydebug ) { - const auto details_for = getenv("symbols_update"); - details = details_for && 0 == strcasecmp(details_for, group->name); - } // At end of group, members is a list of all immediate children, any // of which might have been redefined and so acquired a memsize. @@ -1402,7 +1288,7 @@ static struct symbol_elem_t * // If group has a parent that is a record area, expand it, too. if( 0 < group->parent ) { - auto redefined = symbol_redefines(group); + redefined = symbol_redefines(group); if( redefined && is_record_area(redefined) ) { if( redefined->data.capacity < group->data.memsize ) { redefined->data.capacity = group->data.memsize; @@ -1432,7 +1318,7 @@ static struct symbol_elem_t * switch( group->level ) { case 1: case 77: - if( dialect_mf() && is_table(group) ) { + if( is_table(group) ) { size_t elem_size = std::max(group->data.memsize, group->data.memsize); group->data.memsize = elem_size * group->occurs.ntimes(); } @@ -1448,23 +1334,6 @@ verify_block( const struct symbol_elem_t *block, if( e->type != SymField ) { continue; } - const struct cbl_field_t *field = cbl_field_of(e); - - if( getenv(__func__) ) { - if( e == block ) { - static const char ds[] = "--------------------------------"; - dbgmsg( "%17s %-3s %-3s %-18s %-3s %3s %-16s C/D/R = init\n" - "%.25s %-.3s %-.3s %-.18s %-.3s %.3s %-.16s %-.7s %-.16s", - "", "ndx", "off", "type", "par", "lvl", "name", - ds, ds, ds, ds, ds, ds, ds, ds, ds ); - } - dbgmsg( "%s:%d: %3zu %3zu %-18s %3zu %02d %-16s %2u/%u/%d = '%s'", - __func__, __LINE__, e - symbols.elems, field->offset, - cbl_field_type_str(field->type), - field->parent, field->level, field->name, - field->data.capacity, field->data.digits, field->data.rdigits, - field->data.initial? field->data.initial : "(none)" ); - } } } @@ -1500,19 +1369,18 @@ immediately_follows( const cbl_field_t *field ) { bool is_variable_length( const cbl_field_t *field ) { - bool odo = false; - std::find_if( symbol_at(field_index(field)) + 1, symbols_end(), - [&odo, field]( const auto& elem ) { - if( elem.type == SymField ) { - auto f = cbl_field_of(&elem); - if( f->level <= field->level ) return true; - if( f->occurs.depending_on ) { - odo = true; - return true; - } - } - return false; - } ); + // RENAMES may be included in end_of_group. + size_t isym = field_index(field), esym = end_of_group(isym); + bool odo = std::any_of( symbol_at(isym) + 1, symbol_at_impl(esym), + [field]( const auto& elem ) { + if( elem.type == SymField ) { + auto f = cbl_field_of(&elem); + if( field->level < f->level ) { // exclude RENAMES + return 0 < f->occurs.depending_on; + } + } + return false; + } ); return odo; } @@ -1524,7 +1392,7 @@ is_variable_length( const cbl_field_t *field ) { * occurs-depending table." */ cbl_field_t * -rename_not_ok( cbl_field_t *first, cbl_field_t *last) { +rename_not_ok( const cbl_field_t *first, const cbl_field_t *last) { symbol_elem_t *beg = symbol_at(field_index(first)), *end = symbol_at(field_index(last)); @@ -1592,11 +1460,11 @@ cbl_field_t::attr_str( const std::vector<cbl_field_attr_t>& attrs ) const const char *sep = ""; char *out = NULL; - for( auto attr : attrs ) { + for( auto attr_l : attrs ) { char *part = out; - if( has_attr(attr) ) { + if( has_attr(attr_l) ) { int erc = asprintf(&out, "%s%s%s", - part? part : "", sep, cbl_field_attr_str(attr)); + part? part : "", sep, cbl_field_attr_str(attr_l)); if( -1 == erc ) return part; free(part); sep = ", "; @@ -1607,7 +1475,7 @@ cbl_field_t::attr_str( const std::vector<cbl_field_attr_t>& attrs ) const char * field_str( const cbl_field_t *field ) { - static char string[3*sizeof(cbl_name_t)]; + static char string[4*sizeof(cbl_name_t)]; char *pend = string; char name[2*sizeof(cbl_name_t)] = ""; @@ -1619,17 +1487,23 @@ field_str( const cbl_field_t *field ) { for( size_t i=0; i < field->occurs.nkey; i++ ) { updown[i] = field->occurs.keys[i].ascending? 'A' : 'D'; } - snprintf(name, sizeof(name), "%s[%zu]%s", - field->name, field->occurs.ntimes(), updown.data()); + snprintf(name, sizeof(name), "%s[" HOST_SIZE_T_PRINT_UNSIGNED "]%s", + field->name, (fmt_size_t)field->occurs.ntimes(), updown.data()); } } + if( field->codeset.valid() ) { + strcat(name, " ("); + strcat(name, field->codeset.name()); + strcat(name, ") "); + } + pend += snprintf(pend, string + sizeof(string) - pend, - "%02d %-20s ", field->level, name); + "%02u %-20s ", field->level, name); char offset[32] = ""; if( field->level > 1 ) { - sprintf( offset, "off%3zu", field->offset ); + sprintf( offset, "off%3" GCC_PRISZ "u", (fmt_size_t)field->offset ); } char parredef = @@ -1637,41 +1511,66 @@ field_str( const cbl_field_t *field ) { if( 'r' == parredef && field->level == 0 ) parredef = 'p'; if( field->has_attr(typedef_e) ) parredef = 'T'; - const char *data = field->data.initial? field->data.initial : NULL; - if( data ) { - auto fig = cbl_figconst_of(data); + const char *init = field->data.initial? field->data.initial : NULL; + if( init ) { + auto fig = cbl_figconst_of(init); if( normal_value_e != fig ) { - data = cbl_figconst_str(fig); + init = cbl_figconst_str(fig); } else { - char *s; - auto n = asprintf(&s, "'%s'", data); - gcc_assert(n); - auto eodata = data + field->data.capacity; - if( eodata != std::find_if_not(data, eodata, fisprint) ) { - char *p = reinterpret_cast<char*>(xrealloc(s, n + 8 + 2 * field->data.capacity)); - if( is_elementary(field->type) && - field->type != FldPointer && p != NULL ) { - s = p; - p += n; - strcat( p, "(0x" ); - p += 3; - for( auto d=data; d < eodata; d++ ) { - p += sprintf(p, "%02x", *d); +#if 0 + // At this point, we might have to convert 'init' back to ASCII + char *false_init = static_cast<char *>(xmalloc(field->init.capacity+1)); + memcpy(false_init, field->init.initial, field->data.capacity); + false_data[field->data.capacity] = '\0'; + size_t charsout; + + cbl_encoding_t enc_from = field->codeset.encoding; + if( field->type == FldNumericDisplay ) + { + // Apparently we need to trace back the meaning of data.literal for + // field::type == FldNumericDisplay + enc_from = DEFAULT_SOURCE_ENCODING; + } + + init = __gg__iconverter(enc_from, + DEFAULT_SOURCE_ENCODING, + false_data, + field->data.capacity, + &charsout); +#endif + auto eoinit = init + strlen(init); + char *s = xasprintf("'%s'", init); + + // No NUL within the initial data. + auto ok = std::none_of( init, eoinit, + []( char ch ) { return ch == '\0'; } ); + assert(ok); + + // If any of the init are unprintable, provide a hex version. + if( ! std::all_of(init, eoinit, fisprint) ) { + if( is_elementary(field->type) && field->type != FldPointer ) { + const size_t len = strlen(s) + 8 + 2 * field->data.capacity; + s = reinterpret_cast<char*>(xrealloc(s, len)); + strcat( s, " (0x" ); + char *p = s + strlen(s); + for( auto d=init; d < eoinit; d++ ) { + p += sprintf(p, "%02x", static_cast<unsigned char>(*d)); } - strcat( p++, ")" ); + strcat( s, ")" ); + assert(strlen(s) < len); } } - data = s; + init = s; } } else { - data = "NULL"; + init = "NULL"; if( field->type == FldSwitch ) { - data = xasprintf("0x%02x", field->data.upsi_mask_of()->value); + init = xasprintf("0x%02x", field->data.upsi_mask_of()->value); } } if( field->level == 88 ) { const auto& dom = *field->data.domain_of(); - data = xasprintf("%s%s %s - %s%s", + init = xasprintf("%s%s %s - %s%s", dom.first.all? "A" : "", value_or_figconst_name(dom.first.name()) , dom.first.is_numeric? "(num)" : "", @@ -1690,20 +1589,20 @@ field_str( const cbl_field_t *field ) { intermediate_e, embiggened_e, all_alpha_e, all_x_e, all_ax_e, prog_ptr_e, scaled_e, refmod_e, based_e, any_length_e, /* global_e, external_e, */ blank_zero_e, /* linkage_e, local_e, */ leading_e, - separate_e, envar_e, dnu_1_e, bool_encoded_e, hex_encoded_e, + separate_e, envar_e, encoded_e, bool_encoded_e, hex_encoded_e, depends_on_e, /* initialized_e, */ has_value_e, ieeedec_e, big_endian_e, same_as_e, record_key_e, typedef_e, strongdef_e, }; pend += snprintf(pend, string + sizeof(string) - pend, - "%c%3zu %-6s %c%c%c %2u{%3u,%u,%d = %s} (%s), line %d", - parredef, field->parent, offset, + "%c%3" GCC_PRISZ "u %-6s %c%c%c %2u{%3u,%u,%d = %s} (%s), line %d", + parredef, (fmt_size_t)field->parent, offset, (field->attr & global_e)? 'G' : 0x20, (field->attr & external_e)? 'E' : 0x20, storage_type, field->data.memsize, field->data.capacity, field->data.digits, field->data.rdigits, - data, field->attr_str(attrs), field->line ); + init, field->attr_str(attrs), field->line ); return string; } @@ -1719,7 +1618,7 @@ struct capacity_of { capacity_of operator()( symbol_elem_t& elem ) { if( elem.type == SymField ) { - cbl_field_t *f = cbl_field_of(&elem); + const cbl_field_t *f = cbl_field_of(&elem); if( is_elementary(f->type) ) { capacity += field_size(f); } @@ -1731,13 +1630,15 @@ struct capacity_of { static void extend_66_capacity( cbl_field_t *alias ) { static_assert(sizeof(symbol_elem_t*) == sizeof(const char *), - "all pointers must be same size"); - assert(alias->data.picture); + "all pointers must be same size"); + assert(alias->level == 66); assert(alias->type == FldGroup); + assert(alias->data.picture); + // If data.picture is not NULL, it is the THRU symbol, see symbol_field_alias2. symbol_elem_t *e = symbol_at(alias->parent); symbol_elem_t *e2 = reinterpret_cast<symbol_elem_t*>(const_cast<char*>(alias->data.picture)); - assert(e < e2); + assert(symbol_index(e) < symbol_index(e2)); alias->data.picture = NULL; capacity_of cap; @@ -1752,6 +1653,9 @@ extend_66_capacity( cbl_field_t *alias ) { bool symbols_alphabet_set( size_t program, const char name[]) { + +//////// +// Older version struct alpha { void operator()( symbol_elem_t& elem ) const { if( elem.type == SymAlphabet ) { @@ -1761,7 +1665,7 @@ symbols_alphabet_set( size_t program, const char name[]) { }; // Define alphabets for codegen. - std::for_each(symbols_begin(), symbols_end(), alpha() ); + std::for_each(symbols_begin(program), symbols_end(), alpha() ); // Set collation sequence before parser_symbol_add. if( name ) { @@ -1772,6 +1676,38 @@ symbols_alphabet_set( size_t program, const char name[]) { parser_alphabet_use(*cbl_alphabet_of(e)); } return true; +// End older version +//////// + +//// // Define alphabets for codegen. +//// const cbl_alphabet_t *alphabet = nullptr; +//// bool supported = true; +//// +//// std::for_each( symbols_begin(program), symbols_end(), +//// [&alphabet, &supported]( const auto& sym ) { +//// if( sym.type == SymAlphabet ) { +//// alphabet = cbl_alphabet_of(&sym); +//// supported = __gg__encoding_iconv_valid(alphabet->encoding); +//// if( supported ) { +//// parser_alphabet( *alphabet ); +//// } +//// } +//// } ); +//// if( ! supported ) { +//// const char *encoding = __gg__encoding_iconv_name(alphabet->encoding); +//// cbl_unimplemented("alphabet %qs (as %qs)", alphabet->name, encoding); +//// return false; +//// } +//// +//// // Set collation sequence before parser_symbol_add.` +//// if( name ) { +//// symbol_elem_t *e = symbol_alphabet(program, name); +//// if( !e ) { +//// return false; +//// } +//// parser_alphabet_use(*cbl_alphabet_of(e)); +//// } +//// return true; } static std::ostream& @@ -1779,6 +1715,9 @@ operator<<( std::ostream& os, const cbl_occurs_bounds_t& bound ) { return os << bound.lower << ',' << bound.upper; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +// Keep this debugging function around for when it is needed static std::ostream& operator<<( std::ostream& os, const cbl_field_data_t& field ) { return os << field.memsize << ',' @@ -1802,16 +1741,7 @@ operator<<( std::ostream& os, const cbl_field_t& field ) { << ',' << field.line << ',' << field.data; } - -static void -write_field_csv( size_t isym, const cbl_field_t *field ) { - static std::ofstream os( getenv("GCOBOL_DATA") ); - assert(os.is_open()); - - if( symbols.first_program < isym) { - os << isym << "," << *field << std::endl; - } -} +#pragma GCC diagnostic pop static std::map<size_t, std::set<size_t>> same_record_areas; size_t parse_error_count(); @@ -1832,11 +1762,6 @@ symbols_update( size_t first, bool parsed_ok ) { struct symbol_elem_t *p, *pend; std::list<cbl_field_t*> shared_record_areas; - if( getenv(__func__) ) { - fprintf(stderr, "Initial"); - symbols_dump(std::max(first, symbols.first_program), true); - } - for( p = symbols_begin(first); p < symbols_end(); p++ ) { if( p->type == SymAlphabet ) continue; // Alphabets already processed. @@ -1858,8 +1783,7 @@ symbols_update( size_t first, bool parsed_ok ) { break; case 1: pend = calculate_capacity(p); - if( dialect_mf() && is_table(field) ) { - cbl_field_t *field = cbl_field_of(p); + if( is_table(field) ) { if( field->data.memsize < field->size() ) { field->data.memsize = field->size(); } @@ -1881,10 +1805,6 @@ symbols_update( size_t first, bool parsed_ok ) { // no special processing for other levels } - if( getenv("GCOBOL_DATA") ) { - write_field_csv( p - symbols_begin(), field ); - } - // Update ODO field in situ. if( is_table(field) ) { size_t& odo = field->occurs.depending_on; @@ -1902,7 +1822,7 @@ symbols_update( size_t first, bool parsed_ok ) { bool size_invalid = field->data.memsize > 0 && symbol_redefines(field); if( size_invalid ) { // redefine of record area is ok - auto redefined = symbol_redefines(field); + const cbl_field_t * redefined = symbol_redefines(field); size_invalid = ! is_record_area(redefined); } if( !field->is_valid() || size_invalid ) @@ -1925,8 +1845,8 @@ symbols_update( size_t first, bool parsed_ok ) { if( e == symbols_end() ) { // no field redefines the file's default record auto file = cbl_file_of(symbol_at(field->parent)); - ERROR_FIELD(field, "line %d: %s lacks a file description", - file->line, file->name); + ERROR_FIELD(field, "%s lacks a file description", + file->name); return 0; } } @@ -1937,28 +1857,25 @@ symbols_update( size_t first, bool parsed_ok ) { field->line, field->level_str(), field->name); } else { - dbgmsg("%s: error: data item %s #%zu '%s' capacity %u rejected", + dbgmsg("%s: error: data item %s #" HOST_SIZE_T_PRINT_UNSIGNED + " '%s' capacity %u rejected", __func__, 3 + cbl_field_type_str(field->type), - isym, field->name, field->data.capacity); + (fmt_size_t)isym, field->name, field->data.capacity); + gcc_unreachable(); } } return 0; } if(! (field->data.memsize == 0 || field_size(field) <= field->data.memsize) ) { - dbgmsg( "%s:%d: #%zu: invalid: %s", __func__, __LINE__, - symbol_index(p), field_str(cbl_field_of(p)) ); + dbgmsg( "%s:%d: #" HOST_SIZE_T_PRINT_UNSIGNED ": invalid: %s", __func__, __LINE__, + (fmt_size_t)symbol_index(p), field_str(cbl_field_of(p)) ); } assert(field->data.memsize == 0 || field_size(field) <= field_memsize(field)); assert( !(field->data.memsize > 0 && symbol_explicitly_redefines(field)) ); } - if( getenv(__func__) ) { - fprintf(stderr, "Pre"); - symbols_dump(std::max(first, symbols.first_program), true); - } - // A shared record area has no 01 child because that child redefines its parent. for( auto sharer : shared_record_areas ) { auto redefined = cbl_field_of(symbol_at(sharer->parent)); @@ -1973,6 +1890,15 @@ 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 the field is a constant for a figconstant, just use it. + if( field->level != 0 && field->has_attr(constant_e) ) { + auto fig = cbl_figconst_field_of(field->data.initial); + if( fig ) { + field->var_decl_node = fig->var_decl_node; + continue; + } + } + if( field->is_typedef() ) { auto isym = end_of_group( symbol_index(p) ); p = symbol_at(--isym); @@ -1980,7 +1906,7 @@ symbols_update( size_t first, bool parsed_ok ) { } // Verify REDEFINing field has no ODO components - auto parent = symbol_redefines(field); + const cbl_field_t * parent = symbol_redefines(field); if( parent && !is_record_area(parent) && is_variable_length(field) ) { ERROR_FIELD(field, "line %d: REDEFINES field %s cannot be variable length", field->line, field->name); @@ -1993,6 +1919,52 @@ symbols_update( size_t first, bool parsed_ok ) { field->line, field->level_str(), field->name); continue; } + if( is_numeric(field) && ! field->has_attr(constant_e) ) { + if( field->data.capacity == 0 ) { + ERROR_FIELD(field, "numeric %qs has USAGE that requires PICTURE %s", + field->name, field->data.initial); + } + } + + if( ! field->codeset.consistent() ) { + if( ! field->codeset.valid() ) { + switch(field->type) { + case FldForward: + case FldInvalid: + gcc_unreachable(); + case FldAlphaEdited: + case FldAlphanumeric: + case FldClass: + case FldDisplay: + case FldGroup: + case FldLiteralA: + case FldLiteralN: + case FldNumericDisplay: + case FldNumericEdited: + if( ! (field->has_attr(register_e) || field->has_attr(hex_encoded_e)) ) { + error_msg(symbol_field_location(field_index(field)), + "internal: %qs encoding not defined", field->name); + } + break; + case FldConditional: + case FldFloat: + case FldIndex: + case FldNumericBin5: + case FldNumericBinary: + case FldPacked: + case FldPointer: + case FldSwitch: + break; + } + } else { + if( ! (field->has_attr(register_e) || field->has_attr(hex_encoded_e)) ) { + error_msg(symbol_field_location(field_index(field)), + "internal: %qs encoding %qs inconsistent", + field->name, + cbl_alphabet_t::encoding_str(field->codeset.encoding) ); + } + } + } assert( ! field->is_typedef() ); @@ -2071,7 +2043,8 @@ symbol_field_forward( size_t index ) { assert( index < symbols.nelem ); symbol_elem_t *e = symbol_at(index); if( (e->type != SymField) ) { - dbgmsg("%s: logic error: #%zu is %s", __func__, index, symbol_type_str(e->type)); + dbgmsg("%s: logic error: #" HOST_SIZE_T_PRINT_UNSIGNED " is %s", + __func__, (fmt_size_t)index, symbol_type_str(e->type)); } assert(e->type == SymField); @@ -2169,15 +2142,15 @@ symbol_in_file( symbol_elem_t *e ) { } #endif -static struct cbl_field_t * -symbol_field_parent_set( struct cbl_field_t *field ) +static cbl_field_t * +symbol_field_parent_set( cbl_field_t *field ) { if( field->level == 01 ) return NULL; if( field->level == 77 ) return NULL; if( field->level == 78 ) return NULL; struct symbol_elem_t *e = symbols.elems + symbols.nelem - 1; - struct symbol_elem_t *first = symbols.elems + symbols.first_program; + const struct symbol_elem_t *first = symbols.elems + symbols.first_program; for( ; field->parent == 0 && e >= first; e-- ) { if( ! (e->type == SymField && cbl_field_of(e)->level > 0) ) { @@ -2215,6 +2188,9 @@ symbol_field_parent_set( struct cbl_field_t *field ) return NULL; } prior->type = FldGroup; + if( ! prior->codeset.set() ) { // needs attention + dbgmsg("'%s' is already National", prior->name); + } field->attr |= numeric_group_attrs(prior); } // verify level 88 domain value @@ -2255,7 +2231,7 @@ class parent_elem_set private: size_t parent_index; public: - parent_elem_set( size_t parent_index ) + explicit parent_elem_set( size_t parent_index ) : parent_index(parent_index) {} void operator()( struct symbol_elem_t& e ) { @@ -2272,8 +2248,10 @@ add_token( symbol_elem_t sym ) { return sym; } +const std::list<cbl_field_t> cdf_literalize(); + /* - * When adding registers, be sure to add a complementary cblc_field_t + * When adding special registers, be sure to create the actual cblc_field_t * in libgcobol/constants.cc. */ void @@ -2292,53 +2270,63 @@ symbol_table_init(void) { // These should match the definitions in libgcobol/constants.cc static cbl_field_t constants[] = { - { 0, FldAlphanumeric, FldInvalid, space_value_e | constq, 0, 0, 0, nonarray, 0, - "SPACE", 0, {}, {1,1,0,0, " \0\xFF"}, NULL }, - { 0, FldAlphanumeric, FldInvalid, space_value_e | constq , 0, 0, 0, nonarray, 0, - "SPACES", 0, {}, {1,1,0,0, " \0\xFF"}, NULL }, - { 0, FldAlphanumeric, FldInvalid, low_value_e | constq, 0, 0, 0, nonarray, 0, - "LOW_VALUES", 0, {}, {1,1,0,0, "L\0\xFF"}, NULL }, - { 0, FldAlphanumeric, FldInvalid, zero_value_e | constq, 0, 0, 0, nonarray, 0, - "ZEROS", 0, {}, {1,1,0,0, "0"}, NULL }, - { 0, FldAlphanumeric, FldInvalid, high_value_e | constq, 0, 0, 0, nonarray, 0, - "HIGH_VALUES", 0, {}, {1,1,0,0, "H\0\xFF"}, NULL }, + { FldAlphanumeric, space_value_e | int(constq) | register_e, + {1,1,0,0, " \0\xFF"}, 0, "SPACE" }, + { FldAlphanumeric, space_value_e | int(constq) | register_e, + {1,1,0,0, " \0\xFF"}, 0, "SPACES" }, + { FldAlphanumeric, low_value_e | int(constq) | register_e, + {1,1,0,0, "L\0\xFF"}, 0, "LOW_VALUES" }, + { FldAlphanumeric, zero_value_e | int(constq) | register_e, + {1,1,0,0, "0"}, 0, "ZEROS" }, + { FldAlphanumeric, high_value_e | int(constq) | register_e, + {1,1,0,0, "H\0\xFF"}, 0, "HIGH_VALUES" }, // IBM standard: QUOTE is a double-quote unless APOST compiler option - { 0, FldAlphanumeric, FldInvalid, quote_value_e | constq , 0, 0, 0, nonarray, 0, - "QUOTES", 0, {}, {1,1,0,0, "\"\0\xFF"}, NULL }, - { 0, FldPointer, FldPointer, constq , 0, 0, 0, nonarray, 0, - "NULLS", 0, {}, {8,8,0,0, zeroes_for_null_pointer}, NULL }, + { FldAlphanumeric, quote_value_e | int(constq) | register_e , + {1,1,0,0, "\"\0\xFF"}, 0, "QUOTES" }, + { FldPointer, int(constq) | register_e , + {8,8,0,0, zeroes_for_null_pointer}, 0, "NULLS" }, // IBM defines TALLY // 01 TALLY GLOBAL PICTURE 9(5) USAGE BINARY VALUE ZERO. - { 0, FldNumericBin5, FldInvalid, signable_e, 0, 0, 0, nonarray, 0, - "_TALLY", 0, {}, {16, 16, MAX_FIXED_POINT_DIGITS, 0, NULL}, NULL }, + { FldNumericBin5, signable_e | register_e, + {16, 16, MAX_FIXED_POINT_DIGITS, 0, NULL}, 0, "_TALLY" }, // 01 ARGI is the current index into the argv array - { 0, FldNumericBin5, FldInvalid, signable_e, 0, 0, 0, nonarray, 0, - "_ARGI", 0, {}, {16, 16, MAX_FIXED_POINT_DIGITS, 0, NULL}, NULL }, + { FldNumericBin5, signable_e | register_e, + {16, 16, MAX_FIXED_POINT_DIGITS, 0, NULL}, 0, "_ARGI" }, // These last two don't require actual storage; they get BOOL var_decl_node // in parser_symbol_add() - { 0, FldConditional, FldInvalid, constant_e , 0, 0, 0, nonarray, 0, - "_VERY_TRUE", 0, {}, {1,1,0,0, ""}, NULL }, - { 0, FldConditional, FldInvalid, constant_e , 0, 0, 0, nonarray, 0, - "_VERY_FALSE", 0, {}, {1,1,0,0, ""}, NULL }, + { FldConditional, constant_e | register_e , {1,1,0,0, ""}, 0, "_VERY_TRUE" }, + { FldConditional, constant_e | register_e , {1,1,0,0, ""}, 0, "_VERY_FALSE" }, }; for( struct cbl_field_t *f = constants; f < constants + COUNT_OF(constants); f++ ) { f->our_index = table.nelem; struct symbol_elem_t sym(SymField, 0); sym.elem.field = *f; + // The following makes these constants match the definitions in + // constants.cc. Consider expanding the constructor fo cbl_field_t to + // handle encoding. + sym.elem.field.codeset.encoding = iconv_CP1252_e; table.elems[table.nelem++] = sym; } static symbol_elem_t environs[] = { + { symbol_elem_t{ 0, cbl_special_name_t{0, CONSOLE_e, "CONSOLE", 0, "/dev/stdout"}} }, // stdout in DISPLAY; stdin in ACCEPT + + { symbol_elem_t{ 0, cbl_special_name_t{0, STDIN_e, "STDIN", 0, "/dev/stdin"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, SYSIN_e, "SYSIN", 0, "/dev/stdin"}} }, - { symbol_elem_t{ 0, cbl_special_name_t{0, SYSIPT_e, "SYSIPT", 0, "/dev/stdout"}} }, + { symbol_elem_t{ 0, cbl_special_name_t{0, SYSIPT_e, "SYSIPT", 0, "/dev/stdin"}} }, + + { symbol_elem_t{ 0, cbl_special_name_t{0, STDOUT_e, "STDOUT", 0, "/dev/stdout"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, SYSOUT_e, "SYSOUT", 0, "/dev/stdout"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, SYSLIST_e, "SYSLIST", 0, "/dev/stdout"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, SYSLST_e, "SYSLST", 0, "/dev/stdout"}} }, + { symbol_elem_t{ 0, cbl_special_name_t{0, SYSPUNCH_e, "SYSPUNCH", 0, "/dev/stderr"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, SYSPCH_e, "SYSPCH", 0, "/dev/stderr"}} }, - { symbol_elem_t{ 0, cbl_special_name_t{0, CONSOLE_e, "CONSOLE", 0, "/dev/stdout"}} }, + { symbol_elem_t{ 0, cbl_special_name_t{0, STDERR_e, "STDERR", 0, "/dev/stderr"}} }, + { symbol_elem_t{ 0, cbl_special_name_t{0, SYSERR_e, "SYSERR", 0, "/dev/stderr"}} }, + { symbol_elem_t{ 0, cbl_special_name_t{0, C01_e, "C01", 0, "/dev/null"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, C02_e, "C02", 0, "/dev/null"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, C03_e, "C03", 0, "/dev/null"}} }, @@ -2358,10 +2346,6 @@ symbol_table_init(void) { { symbol_elem_t{ 0, cbl_special_name_t{0, S04_e, "S04", 0, "/dev/null"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, S05_e, "S05", 0, "/dev/null"}} }, { symbol_elem_t{ 0, cbl_special_name_t{0, AFP_5A_e, "AFP-5A", 0, "/dev/null"}} }, - { symbol_elem_t{ 0, cbl_special_name_t{0, STDIN_e, "STDIN", 0, "/dev/stdin"}} }, - { symbol_elem_t{ 0, cbl_special_name_t{0, STDOUT_e, "STDOUT", 0, "/dev/stdout"}} }, - { symbol_elem_t{ 0, cbl_special_name_t{0, STDERR_e, "STDERR", 0, "/dev/stderr"}} }, - { symbol_elem_t{ 0, cbl_special_name_t{0, SYSERR_e, "SYSERR", 0, "/dev/stderr"}} }, }; struct symbol_elem_t *p = table.elems + table.nelem; @@ -2371,6 +2355,49 @@ symbol_table_init(void) { assert(table.nelem < table.capacity); + /** + * Debug register record + 01 DEBUG-ITEM. + 02 DEBUG-LINE PIC X(6). + 02 FILLER PIC X VALUE SPACE. + 02 DEBUG-NAME PIC X(30). + 02 FILLER PIC X VALUE SPACE. + 02 DEBUG-SUB-1 PIC S9999 SIGN IS LEADING SEPARATE CHARACTER. + 02 FILLER PIC X VALUE SPACE. + 02 DEBUG-SUB-2 PIC S9999 SIGN IS LEADING SEPARATE CHARACTER. + 02 FILLER PIC X VALUE SPACE. + 02 DEBUG-SUB-3 PIC S9999 SIGN IS LEADING SEPARATE CHARACTER. + 02 FILLER PIC X VALUE SPACE. + 02 DEBUG-CONTENTS PIC X(76). + **/ + + static cbl_field_t debug_registers[] = { + { FldGroup, register_e, + {132,132,0,0, NULL}, 1, "DEBUG-ITEM" }, + { FldAlphanumeric, register_e, + {6,6,0,0, " "}, 2, "DEBUG-LINE" }, + { FldAlphanumeric, register_e|filler_e, + {1,1,0,0, " "}, 2, "FILLER" }, + { FldAlphanumeric, register_e, + {30,30,0,0, NULL}, 2, "DEBUG-NAME" }, + { FldAlphanumeric, register_e|filler_e, + {1,1,0,0, " "}, 2, "FILLER" }, + { FldNumericDisplay, signable_e | register_e | leading_e | separate_e, + {5,5,4,0, NULL}, 2, "DEBUG-SUB-1" }, + { FldAlphanumeric, register_e|filler_e, + {1,1,0,0, " "}, 2, "FILLER" }, + { FldNumericDisplay, signable_e | register_e | leading_e | separate_e, + {5,5,4,0, NULL}, 2, "DEBUG-SUB-2" }, + { FldAlphanumeric, register_e|filler_e, + {1,1,0,0, " "}, 2, "FILLER" }, + { FldNumericDisplay, signable_e | register_e | leading_e | separate_e, + {5,5,4,0, NULL}, 2, "DEBUG-SUB-3" }, + { FldAlphanumeric, register_e | filler_e, + {1,1,0,0, " "}, 2, "FILLER" }, + { FldAlphanumeric, signable_e | register_e, + {76,76,0,0, NULL}, 2, "DEBUG-CONTENTS" }, + }; + // debug registers assert(table.nelem + COUNT_OF(debug_registers) < table.capacity); @@ -2388,6 +2415,17 @@ symbol_table_init(void) { std::for_each(debug_start+1, p, parent_elem_set(debug_start - table.elems)); // special registers + static cbl_field_t special_registers[] = { + { FldNumericDisplay, register_e, {2,2,2,0, NULL}, 0, "_FILE_STATUS" }, + { FldNumericBin5, register_e, {2,2,4,0, NULL}, 0, "UPSI-0" }, + { FldNumericBin5, signable_e|register_e, {2,2,4,0, NULL}, 0, "RETURN-CODE" }, + { FldNumericBin5, register_e, {2,2,4,0, NULL}, 0, "LINAGE-COUNTER" }, + { FldLiteralA, register_e, {0,0,0,0, "/dev/stdin"}, 0, "_dev_stdin" }, + { FldLiteralA, int(constq)|register_e, {0,0,0,0, "/dev/stdout"}, 0, "_dev_stdout" }, + { FldLiteralA, int(constq)|register_e, {0,0,0,0, "/dev/stderr"}, 0, "_dev_stderr" }, + { FldLiteralA, int(constq)|register_e, {0,0,0,0, "/dev/null"}, 0, "_dev_null" }, + }; + assert(table.nelem + COUNT_OF(special_registers) < table.capacity); p = table.elems + table.nelem; @@ -2397,6 +2435,34 @@ symbol_table_init(void) { table.nelem = p - table.elems; assert(table.nelem < table.capacity); + // xml registers + static cbl_field_t xml_registers[] = { + { FldNumericBin5, register_e, {4,4,9,0, "0"}, 1, "XML-CODE" }, + { FldAlphanumeric, register_e, {30,30,0,0, " "}, 1, "XML-EVENT" }, + { FldNumericBin5, register_e, {4,4,9,0, "0"}, 1, "XML-INFORMATION" }, + { FldAlphanumeric, register_e | based_e | any_length_e, {1,1,0,0, nullptr}, 1, "XML-NAMESPACE" }, + { FldAlphanumeric, register_e | based_e | any_length_e, {1,1,0,0, nullptr}, 1, "XML-NNAMESPACE" }, + { FldAlphanumeric, register_e | based_e | any_length_e, {1,1,0,0, nullptr}, 1, "XML-NAMESPACE-PREFIX" }, + { FldAlphanumeric, register_e | based_e | any_length_e, {1,1,0,0, nullptr}, 1, "XML-NNAMESPACE-PREFIX" }, + { FldAlphanumeric, register_e | based_e | any_length_e, {1,1,0,0, nullptr}, 1, "XML-TEXT" }, + { FldAlphanumeric, register_e | based_e | any_length_e, {1,1,0,0, nullptr}, 1, "XML-NTEXT" }, + }, * const eoxml = xml_registers + COUNT_OF(xml_registers); + + assert(table.nelem + COUNT_OF(xml_registers) < table.capacity); + + p = table.elems + table.nelem; + p = std::transform(xml_registers, eoxml, p, elementize); + table.nelem = p - table.elems; + assert(table.nelem < table.capacity); + + // Add any CDF values already defined as literals. + // After symbols are ready, the CDF adds them directly. + const std::list<cbl_field_t> cdf_values = cdf_literalize(); + table.nelem += cdf_values.size(); + assert(table.nelem < table.capacity); + + p = std::transform(cdf_values.begin(), cdf_values.end(), p, elementize); + // Initialize symbol table. symbols = table; @@ -2414,8 +2480,6 @@ symbol_table_init(void) { symbols.registers.return_code = symbol_index(symbol_field(0,0, "RETURN-CODE")); symbols.registers.very_true = symbol_index(symbol_field(0,0, "_VERY_TRUE")); symbols.registers.very_false = symbol_index(symbol_field(0,0, "_VERY_FALSE")); - - if( getenv(__func__) ) symbols_dump(0, true); } /* @@ -2499,7 +2563,7 @@ symbol_file_add( size_t program, cbl_file_t *file ) { return NULL; } - struct symbol_elem_t sym = { SymFile, program }; + symbol_elem_t sym{ SymFile, program }; sym.elem.file = *file; e = symbol_add(&sym); @@ -2512,16 +2576,23 @@ symbol_file_add( size_t program, cbl_file_t *file ) { return e; } -struct symbol_elem_t * -symbol_alphabet_add( size_t program, struct cbl_alphabet_t *alphabet ) { - struct symbol_elem_t sym{ SymAlphabet, program }; +symbol_elem_t * +symbol_locale_add( size_t program, const cbl_locale_t *locale ) { + symbol_elem_t sym{ SymLocale, program }; + sym.elem.locale = *locale; + return symbol_add(&sym); +} + +symbol_elem_t * +symbol_alphabet_add( size_t program, const cbl_alphabet_t *alphabet ) { + symbol_elem_t sym{ SymAlphabet, program }; sym.elem.alphabet = *alphabet; return symbol_add(&sym); } -size_t +uint64_t numeric_group_attrs( const cbl_field_t *field ) { - static const size_t inherit = signable_e | leading_e | separate_e | big_endian_e; + static const uint64_t inherit = signable_e | leading_e | separate_e | big_endian_e; static_assert(sizeof(cbl_field_t::type) < sizeof(inherit), "need bigger type"); assert(field); if( field->type == FldNumericDisplay || field->type == FldGroup ) { @@ -2557,7 +2628,7 @@ symbol_typedef_add( size_t program, struct cbl_field_t *field ) { auto e = symbols_end() - 1; assert( symbols_begin() < e ); if( e->type == SymField ) { - auto f = cbl_field_of(e); + const cbl_field_t * f = cbl_field_of(e); if( f == field ) return e; } @@ -2595,7 +2666,7 @@ struct symbol_elem_t * symbol_field_add( size_t program, struct cbl_field_t *field ) { field->our_index = symbols.nelem; - cbl_field_t *parent = symbol_field_parent_set( field ); + const cbl_field_t *parent = symbol_field_parent_set( field ); if( parent && parent->type == FldGroup) { // Inherit effects of parent's USAGE, as though it appeared 1st in the // member's definition. @@ -2603,35 +2674,19 @@ symbol_field_add( size_t program, struct cbl_field_t *field ) field->attr = inherit & parent->attr; field->attr |= numeric_group_attrs(parent); field->usage = parent->usage; + if( field->level == 66 || field->level == 88 ) { + field->codeset = parent->codeset; + } // BINARY-LONG, for example, sets capacity. if( is_numeric(parent->usage) && parent->data.capacity > 0 ) { field->type = parent->usage; field->data = parent->data; - field->data = 0.0; + field->data = 0; // cppcheck-suppress redundantAssignment + // // cppcheck doesn't understand multiple overloaded operator= field->data.initial = NULL; } } - char *s; - if( (s = getenv(__func__)) != NULL ) { - if( s[0] == 'D' ) { - for( struct symbol_elem_t *e = symbols_begin(); e < symbols_end(); e++ ) { - fprintf(stderr, "%zu: %s ", e - symbols.elems, symbol_type_str(e->type)); - if( e->type == SymField ) { - fprintf(stderr, "%s = %s", - cbl_field_of(e)->name, cbl_field_of(e)->data.initial); - } - fprintf(stderr, "\n"); - } - } - - dbgmsg( "%s:%d: %3zu %-18s %02d %-16s %u/%u/%d = '%s'", __func__, __LINE__, - field->offset, - cbl_field_type_str(field->type), field->level, field->name, - field->data.capacity, field->data.digits, field->data.rdigits, - field->data.initial? field->data.initial : "(none)" ); - } - if( is_forward(field) ) { auto *e = symbol_field( program, field->parent, field->name ); if( e ) { @@ -2746,6 +2801,7 @@ symbol_field( size_t program, size_t parent, const char name[] ) return p != end? &*p : NULL; } +// cppcheck-suppress-begin [CastIntegerToAddressAtReturn] obviously not true symbol_elem_t * symbol_register( const char name[] ) { @@ -2761,6 +2817,7 @@ symbol_register( const char name[] ) return p; } +// cppcheck-suppress-end [CastIntegerToAddressAtReturn] // Find current 01 record during Level 66 construction. const symbol_elem_t * @@ -2788,11 +2845,8 @@ symbol_field_forward_add( size_t program, size_t parent, auto e = symbol_field(program, parent, name); if( e ) return e; - struct cbl_field_t field = { 0, - FldForward, FldInvalid, 0, parent, 0, 0, - nonarray, line, "", - 0, cbl_field_t::linkage_t(), - {0,0,0,0, " "}, NULL }; + cbl_field_t field = { FldForward, 0, line }; + field.parent = parent; if( sizeof(field.name) < strlen(name) ) { dbgmsg("%s:%d: logic error: name %s too long", __func__, __LINE__, name); return NULL; @@ -2823,11 +2877,12 @@ symbol_literalA( size_t program, const char name[] ) struct symbol_elem_t * symbol_file( size_t program, const char name[] ) { size_t nelem = symbols.nelem; - struct symbol_elem_t key = { SymFile, program }, *e = &key; + symbol_elem_t key{ SymFile, program }, *e = &key; assert(strlen(name) < sizeof(key.elem.file.name)); strcpy(key.elem.file.name, name); + // cppcheck-suppress-begin [knownConditionTrueFalse] do { e = static_cast<struct symbol_elem_t *>(lfind( &key, symbols.elems, &nelem, sizeof(*e), @@ -2836,6 +2891,7 @@ symbol_file( size_t program, const char name[] ) { key.program = cbl_label_of(symbol_at(key.program))->parent; if( key.program == 0 ) break; // no file without a program } while( !e ); + // cppcheck-suppress-end [knownConditionTrueFalse] if( e ) { assert(e->type == SymFile); @@ -2943,7 +2999,7 @@ seek_parent( const symbol_elem_t *e, size_t level ) { struct symbol_elem_t * symbol_field_same_as( cbl_field_t *tgt, const cbl_field_t *src ) { if( target_in_src(tgt, src) ) { - ERROR_FIELD(tgt, "%s %s may not reference itself as part of %s %s", + ERROR_FIELD(tgt, "%s %s may not reference itself as part of %s %s", tgt->level_str(), tgt->name, src->level_str(), src->name); return NULL; } @@ -2976,6 +3032,7 @@ symbol_field_same_as( cbl_field_t *tgt, const cbl_field_t *src ) { cbl_field_t dup = {}; dup.parent = field_index(tgt); dup.line = tgt->line; + dup.codeset = tgt->codeset; elem_group_t group(++bog, eog); @@ -3029,7 +3086,7 @@ symbol_file_same_record_area( std::list<cbl_file_t*>& files ) { } static symbol_elem_t * -next_program( symbol_elem_t *elem ) { +next_program( const symbol_elem_t *elem ) { size_t start = elem? symbol_index(elem) : 0; symbol_elem_t * e = std::find_if( symbols_begin(start), symbols_end(), is_program ); @@ -3067,14 +3124,14 @@ is_numeric_constant( const char name[] ) { // get default record layout for a file struct cbl_field_t * -symbol_file_record( struct cbl_file_t *file ) { +symbol_file_record( const cbl_file_t *file ) { return cbl_field_of(symbol_at(file->default_record)); } class is_section { cbl_section_type_t section_type; public: - is_section( cbl_section_type_t sect ) : section_type(sect) {} + explicit is_section( cbl_section_type_t sect ) : section_type(sect) {} bool operator()( symbol_elem_t& e ) const { return e.type == SymDataSection && cbl_section_of(&e)->type == section_type; } @@ -3085,8 +3142,6 @@ static bool fd_record_size_cmp( const symbol_elem_t& a, const symbol_elem_t& b ) return cbl_field_of(&a)->data.capacity < cbl_field_of(&b)->data.capacity; } -cbl_file_key_t cbl_file_t::no_key; - /* * Find largest and smallest record defined for a file. The rule is: * cbl_file_t::varies() returns true if the record size varies, @@ -3143,12 +3198,6 @@ symbol_file_record_sizes( struct cbl_file_t *file ) { output.min = cbl_field_of(&*p.first)->data.capacity; output.max = cbl_field_of(&*p.second)->data.capacity; - if( yydebug && getenv(__func__) ) { - dbgmsg("%s: %s: min '%s' %zu, max '%s' %zu", __func__, file->name, - cbl_field_of(&*p.first)->name, output.min, - cbl_field_of(&*p.second)->name, output.max); - } - assert(output.min > 0 && "min record size is 0"); assert(output.min <= output.max); @@ -3195,15 +3244,141 @@ constant_of( size_t isym ) return field; } +cbl_locale_t::cbl_locale_t( const cbl_name_t name, const char iconv_name[] ) { + gcc_assert(strlen(name) < sizeof this->name); + strcpy(this->name, name); + + if( iconv_name ) { + encoding = __gg__encoding_iconv_type(iconv_name); + + strcpy(collation, "C"); + // If the iconv_name is prefixed by langauge_COUNTRY (e.g. en_US), capture that. + auto pend = iconv_name + strlen(iconv_name); + auto p = std::find(iconv_name, pend, '.'); + if( p < pend ) { + auto pend2 = std::copy(iconv_name, p, collation); + std::fill(pend2, collation + sizeof(collation), '\0'); + iconv_name = ++p; + } + encoding = __gg__encoding_iconv_type(iconv_name); + } +} + +cbl_alphabet_t::cbl_alphabet_t(const YYLTYPE& loc, size_t locale, cbl_name_t name ) + : loc(loc) + , locale(locale) + , low_index(0) + , high_index(255) + , last_index(0) +{ + if( locale > 0 ) { + encoding = cbl_locale_of(symbol_at(locale))->encoding; + } + memset(collation_sequence, 0xFF, sizeof(collation_sequence)); + if( name ) { // from Special-Names collation_sequence + assert(strlen(name) < sizeof(cbl_name_t)); + strcpy(this->name, name); + } +} + +/* + * As parsed, the alphabet reflects the encoding of the source code. If the + * program uses a different encoding for alphanumeric, convert the alphabet to + * that. + * + * Because a custom alphabet is rare and occurs at most only once per program, + * we don't attempt to avoid re-encoding. "Conversion" of ASCII to ASCII is at + * most 256 calls to iconv(3). + */ +void +cbl_alphabet_t::reencode() { + + const unsigned char * const pend = collation_sequence + sizeof(collation_sequence); + std::vector<char> tgt(256, (char)0xFF); + + /* Keep copies of low_index and last_index for use in run-time as LOW-VALUE + and HIGH-VALUE, which are kept as globals in the source-code codeset + and converted to the display encoding as necessary. */ + + low_char = low_index; + high_char = last_index; + + /* + * For now, assume CP1252 source-code encoding because we're not capturing it + * anywhere except in cbl_field_t::internalize(). The only known examples of + * a custom alphabet are from NIST, which of course are ASCII. + */ + const char *fromcode = __gg__encoding_iconv_name(CP1252_e); + const char *tocode = + __gg__encoding_iconv_name(current_encoding(display_encoding_e)); + iconv_t cd = iconv_open(tocode, fromcode); + +#if optimal_reencode + if( fromcode == tocode ) { // semantically + tgt.resize(0); + return tgt; // Return empty vector; caller copies zero bytes. + } +#endif + + /* + * Each position in the alphabet array represents a letter in the source-code + * encoding. The value at that position represents the letter's collation + * position, its sort order. For each letter in alphabet, determine value of + * that letter in the alphanumeric encoding, and set its collation position + * in that alphabet. + */ + for( const unsigned char *p = collation_sequence; p < pend; p++ ) { + if( *p == 0xFF ) continue; + unsigned char ch = p - collation_sequence; + unsigned char pos[8] = {}; + size_t inbytesleft = 1, outbytesleft = sizeof(pos); + char *inbuf = reinterpret_cast<char*>(&ch), + *outbuf = reinterpret_cast<char*>(pos); + + size_t n = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + + if( n == size_t(-1) ) { + error_msg(loc, "%s character '%c' (%x hex) cannot be converted to %s", + fromcode, ch, ch, tocode); + continue; + } + if( n != 0 ) { + dbgmsg("%s character '%c' (%x hex) irreversibly converted to %s", + fromcode, ch, ch, tocode); + } + assert(outbytesleft < sizeof(pos)); + n = sizeof(pos) - outbytesleft; + if( 1 < n ) { + error_msg(loc, "%s character '%c' (%x hex) requires %zu bytes as %s", + fromcode, ch, ch, n, tocode); + continue; + } + + if( ch == low_index ) { + low_index = pos[0]; + } + if( ch == last_index ) { + last_index = pos[0]; + } + if( ch == high_index ) { + high_index = pos[0]; + } + + tgt.at(pos[0]) = *p; + } + + std::copy(tgt.begin(), tgt.end(), collation_sequence); +} + bool cbl_alphabet_t::assign( const YYLTYPE& loc, unsigned char ch, unsigned char high_value ) { - if( alphabet[ch] == 0xFF || alphabet[ch] == high_value) { - alphabet[ch] = high_value; + if( collation_sequence[ch] == 0xFF || collation_sequence[ch] == high_value) { + collation_sequence[ch] = high_value; last_index = ch; return true; } - auto taken = alphabet[ch]; - error_msg(loc, "ALPHABET %s, character '%c' (X'%x') " + auto taken = collation_sequence[ch]; + error_msg(loc, "ALPHABET %s, character %<%c%> (X%'%x%') " "in position %d already defined at position %d", name, ISPRINT(ch)? ch : '?', ch, @@ -3215,7 +3390,7 @@ cbl_alphabet_t::assign( const YYLTYPE& loc, unsigned char ch, unsigned char high void cbl_alphabet_t::also( const YYLTYPE& loc, size_t ch ) { if( ch < 256 ) { - alphabet[ch] = alphabet[last_index]; + collation_sequence[ch] = collation_sequence[last_index]; if( ch == high_index ) high_index--; return; } // else it's a figurative constant ... @@ -3228,20 +3403,20 @@ cbl_alphabet_t::also( const YYLTYPE& loc, size_t ch ) { // last_index is already set; use it as the "last value before ALSO" if( attr & low_value_e ) { - alphabet[0] = alphabet[last_index]; + collation_sequence[0] = collation_sequence[last_index]; return; } if( attr & high_value_e ) { - alphabet[high_index--] = alphabet[last_index]; + collation_sequence[high_index--] = collation_sequence[last_index]; return; } if( attr & (space_value_e|quote_value_e) ) { ch = field->data.initial[0]; - alphabet[ch] = alphabet[last_index]; + collation_sequence[ch] = collation_sequence[last_index]; return; } if( attr & (zero_value_e) ) { - alphabet[0] = alphabet[last_index]; + collation_sequence[0] = collation_sequence[last_index]; error_msg(loc, "ALSO value '%s' is unknown", field->name); return; } @@ -3252,15 +3427,25 @@ using std::deque; static deque<cbl_field_t*> stack; static cbl_field_t * -new_temporary_impl( enum cbl_field_type_t type ) +new_temporary_impl( enum cbl_field_type_t type, const cbl_name_t name = nullptr ) { extern int yylineno; - static int nstack, nliteral; static const struct cbl_field_t empty_alpha = { - 0, FldAlphanumeric, FldInvalid, - intermediate_e, 0, 0, 0, nonarray, 0, "", - 0, cbl_field_t::linkage_t(), - {}, NULL }; + FldAlphanumeric, intermediate_e, + {MAXIMUM_ALPHA_LENGTH, + MAXIMUM_ALPHA_LENGTH, 0, 0, NULL} }; + static const struct cbl_field_t empty_float = { + FldFloat, intermediate_e, + {16, 16, 32, 0, NULL} }; + static const struct cbl_field_t empty_comp5 = { + FldNumericBin5, + signable_e | intermediate_e, + {16, 16, MAX_FIXED_POINT_DIGITS, 0, NULL} }; + static const struct cbl_field_t empty_conditional = { + FldConditional, intermediate_e, cbl_field_data_t{} }; + static struct cbl_field_t empty_literal = { + FldInvalid, CONSTANT_E, cbl_field_data_t{} }; + struct cbl_field_t *f = new cbl_field_t; f->type = type; @@ -3276,7 +3461,6 @@ new_temporary_impl( enum cbl_field_type_t type ) case FldSwitch: case FldDisplay: case FldPointer: - case FldBlob: break; case FldConditional: *f = empty_conditional; @@ -3301,15 +3485,17 @@ new_temporary_impl( enum cbl_field_type_t type ) f->line = yylineno; if( is_literal(type) ) { + static int nliteral = 0; snprintf(f->name, sizeof(f->name), "_literal%d",++nliteral); } else { + static int nstack = 0; snprintf(f->name, sizeof(f->name), "_stack%d",++nstack); - - if( getenv("symbol_temporaries_free") ) { - dbgmsg("%s: %s, %s", __func__, f->name, 3 + cbl_field_type_str(f->type)); - } } + f->data.initial = name; // capture e.g. the function name + + f->codeset.set(); + return f; } @@ -3322,13 +3508,17 @@ new_temporary_decl() { static inline cbl_field_t * parser_symbol_add2( cbl_field_t *field ) { + if( ! field->codeset.valid() ) { + dbgmsg( "%s:%d: %s (%s) has no encoding", __func__, __LINE__, + field->name, cbl_field_type_str(field->type) ); + } parser_symbol_add(field); return field; } static cbl_field_t * -new_literal_add( const char initial[], uint32_t len, enum cbl_field_attr_t attr ) { - static char empty[2] = "\0"; +new_literal_add( const char initial[], uint32_t len, + cbl_field_attr_t attr, cbl_encoding_t encoding ) { cbl_field_t *field = NULL; if( !(attr & quoted_e) ) { @@ -3340,21 +3530,39 @@ new_literal_add( const char initial[], uint32_t len, enum cbl_field_attr_t attr { field = new_temporary_impl(FldLiteralA); field->attr |= attr; - field->data.initial = len > 0? initial : empty; - field->data.capacity = len; - if( ! field->internalize() ) + if(len == 0) + { + // This will cover UTF-32, should that arise. + size_t nbytes = 4; + char *init = static_cast<char *>(xmalloc(nbytes)); + memset(init, 0, nbytes); + field->data.initial = init; + } + if(len) { - ERROR_FIELD(field, "inconsistent string literal encoding for '%s'", initial); + char *init = static_cast<char *>(xmalloc(len+4)); + memcpy(init, initial, len); + memset(init+len, 0, 4); + field->data.initial = init; } + field->data.capacity = len; } + if( ! field->has_attr(hex_encoded_e) ) { + // If the literal bore a prefix, set the encoding, + if( encoding != cbl_field_t::codeset_t::source_encoding->type ) { + field->codeset.set(encoding); + } + field->internalize(); + } + static size_t literal_count = 1; sprintf(field->name, - "%s%c_%zd", + "%s%c_" HOST_SIZE_T_PRINT_DEC, "_literal", field->type == FldLiteralA ? 'a' : 'n', - literal_count++); + (fmt_size_t)literal_count++); return parser_symbol_add2(field); } @@ -3362,35 +3570,40 @@ new_literal_add( const char initial[], uint32_t len, enum cbl_field_attr_t attr static temporaries_t temporaries; cbl_field_t * -temporaries_t::literal( const char value[], uint32_t len, cbl_field_attr_t attr ) { - auto key = literal_an(value, quoted_e == (attr & quoted_e)); +temporaries_t::literal( uint32_t len, const char value[], + cbl_field_attr_t attr, cbl_encoding_t encoding ) { + bool is_quoted2 = quoted_e == (attr & quoted_e); + bool is_verbatim = hex_encoded_e == (attr & hex_encoded_e); + auto key = literal_an(value, is_quoted2, is_verbatim); - if( 0 == (attr & hex_encoded_e) ) { + if( ! is_verbatim ) { // TODO: try without this test once National is ready auto p = literals.find(key); if( p != literals.end() ) { cbl_field_t *field = p->second; return field; } } - return literals[key] = new_literal_add(value, len, attr); + return literals[key] = new_literal_add(value, len, attr, encoding); } cbl_field_t * -new_literal( uint32_t len, const char initial[], enum cbl_field_attr_t attr ) { - return temporaries.literal(initial, len, attr); +new_literal( uint32_t len, const char initial[], + cbl_field_attr_t attr, cbl_encoding_t encoding ) { + return temporaries.literal(len, initial, attr, encoding); } void temporaries_t::dump() const { extern int yylineno; - char *output = xasprintf("%4d: %zu Literals", yylineno, literals.size()); + char *output = xasprintf("%4d: " HOST_SIZE_T_PRINT_UNSIGNED " Literals", + yylineno, (fmt_size_t)literals.size()); for( const auto& elem : used ) { if( ! elem.second.empty() ) { char *so_far = output; - output = xasprintf("%s, %zu %s", + output = xasprintf("%s, " HOST_SIZE_T_PRINT_UNSIGNED " %s", so_far, - elem.second.size(), + (fmt_size_t)elem.second.size(), 3 + cbl_field_type_str(elem.first)); free(so_far); } @@ -3400,14 +3613,6 @@ temporaries_t::dump() const { } temporaries_t::~temporaries_t() { - if( getenv( "symbol_temporaries_free" ) ) { - dbgmsg("%s: %zu literals", __func__, literals.size()); - for( const auto& elem : literals ) { - const literal_an& key(elem.first); - fprintf(stderr, "%c '%s'\n", key.is_quoted? 'Q' : ' ', key.value.c_str()); - } - dump(); - } } cbl_field_t * @@ -3439,11 +3644,11 @@ temporaries_t::reuse( cbl_field_type_t type ) { } cbl_field_t * -temporaries_t::acquire( cbl_field_type_t type ) { +temporaries_t::acquire( cbl_field_type_t type, const cbl_name_t name ) { cbl_field_t *field = reuse(type); if( !field ) { - field = new_temporary_impl(type); + field = new_temporary_impl(type, name); add(field); } return parser_symbol_add2(field); // notify of reuse @@ -3451,7 +3656,6 @@ temporaries_t::acquire( cbl_field_type_t type ) { void symbol_temporaries_free() { - if( getenv(__func__) ) temporaries.dump(); for( auto& elem : temporaries.used ) { const cbl_field_type_t& type(elem.first); temporaries_t::fieldset_t& used(elem.second); @@ -3477,27 +3681,51 @@ symbol_temporaries_free() { } cbl_field_t * -new_alphanumeric( size_t capacity ) { - cbl_field_t * field = new_temporary_impl(FldAlphanumeric); +new_alphanumeric( size_t capacity, const cbl_name_t name = nullptr ) { + cbl_field_t * field = new_temporary_impl(FldAlphanumeric, name); field->data.capacity = capacity; temporaries.add(field); return parser_symbol_add2(field); } +extern os_locale_t os_locale; + +const encodings_t cbl_field_t::codeset_t::source_encodings[2] = { + { false, iconv_UTF_8_e, "UTF-8" }, + { true, iconv_CP1252_e, "CP1252" }, +}; +const encodings_t * cbl_field_t::codeset_t::source_encoding = { + cbl_field_t::codeset_t::source_encodings +}; + +const encodings_t cbl_field_t::codeset_t::standard_internal = { + true, iconv_CP1252_e, "CP1252" +}; +#define standard_internal cbl_field_t::codeset_t::standard_internal + cbl_field_t * -new_temporary( enum cbl_field_type_t type, const char *initial ) { - if( ! initial ) { +new_temporary( enum cbl_field_type_t type, const char *initial, bool is_signed ) { + const bool force_unsigned = type == FldNumericBin5 && ! is_signed; + + if( ! initial && ! force_unsigned ) { assert( ! is_literal(type) ); // Literal type must have literal value. - return temporaries.acquire(type); + return temporaries.acquire(type, initial); } if( is_literal(type) ) { - auto field = temporaries.literal(initial, - type == FldLiteralA? quoted_e : none_e); + auto field = temporaries.literal(strlen(initial), initial, + type == FldLiteralA? quoted_e : none_e, + standard_internal.type); return field; } - cbl_field_t *field = new_temporary_impl(type); - field->data.capacity = strlen(field->data.initial = initial); - temporaries.add(field); + cbl_field_t *field = new_temporary_impl(type, initial); + + // don't reuse unsigned numeric + if( force_unsigned ) { + field->clear_attr(signable_e); + } else { + temporaries.add(field); + } + parser_symbol_add(field); return field; @@ -3536,12 +3764,38 @@ new_temporary_clone( const cbl_field_t *orig) { temporaries.add(field); } field->data = orig->data; - if( field->type == FldNumericBin5 ) field->type = orig->type; + if( field->type == FldNumericBin5 ) { + field->type = orig->type; + field->codeset = orig->codeset; + } field->attr = intermediate_e; return parser_symbol_add2(field); } +/* + * This set of ASCII-like encodings is incomplete and possibly wrong. A + * complete definition would better supported with a Boolean in enccodings_t. + * If it returns false pessimistically, the only consequence is inefficiency: + * the string is processed by iconv(3). + */ +bool +cbl_field_t::holds_ascii() const { + // True if the encoding is a superset of ASCII. + switch(codeset.encoding) { + case ASCII_e: + case CP1252_e: + case iso646_e: + return true; + default: + if( iconv_1026_e <= codeset.encoding && + codeset.encoding <= iconv_ANSI_X3_4_e ) { + return true; + } + } + return false; +} + bool cbl_field_t::is_ascii() const { return std::all_of( data.initial, @@ -3567,124 +3821,172 @@ cbl_field_t::is_ascii() const { * compilation, if it moves off the default, it adjusts only once, and * never reverts. */ -static const char standard_internal[] = "CP1252//"; -extern os_locale_t os_locale; - -static const char * -guess_encoding() { - static const char *fromcode; - - if( ! fromcode ) { - return fromcode = os_locale.assumed; - } - - if( fromcode == os_locale.assumed ) { - fromcode = os_locale.codeset; - if( 0 != strcmp(fromcode, "C") ) { // anything but that - return fromcode; - } - } - - return standard_internal; -} const char * cbl_field_t::internalize() { - static const char *tocode = standard_internal; - static const char *fromcode = guess_encoding(); - static iconv_t cd = iconv_open(tocode, fromcode); + /* The purpose of this routine is to return a nul-terminated string which + is data.initial converted from the source-code characters to the + codeset.encoding characters. + + The contract between this routine and the routines that call it is that + for alphanumeric types, data.initial shall have the same number of + characters as will be needed to fill data.capacity. + + Be aware that for PIC X(32) Z"foo", there are the characters "foo", + followed by a NUL, and then 28 spaces to fill it out. It turns out that + iconv, given a character count of 32, converts all 32, including the + embedded NUL. So, that case works even through strlen(initial) is + smaller than the length of initial, which is the same as capacity. + */ + + static const char *fromcode = codeset.source_encodings[0].name; static const size_t noconv = size_t(-1); + static std::unordered_map<std::string, iconv_t> tocodes; - // Sat Mar 16 11:45:08 2024: require temporary environment for testing - if( getenv( "INTERNALIZE_NO") ) return data.initial; + if( ! codeset.valid() ) { + dbgmsg("%s:%d: not converting %s", __func__, __LINE__, data.initial); + return data.initial; + } - bool using_assumed = fromcode == os_locale.assumed; + const char *tocode = __gg__encoding_iconv_name(codeset.encoding); + + std::string toname(tocode); + auto p = tocodes.find(toname); + if( p == tocodes.end() ) { + tocodes[toname] = iconv_open(tocode, fromcode); + } + iconv_t cd = tocodes[toname]; + + if (cd == (iconv_t)-1) { + cbl_message(ParIconvE, + "failed %<iconv_open%> tocode = %qs fromcode = %qs", + tocode, fromcode); + } if( fromcode == tocode || has_attr(hex_encoded_e) ) { return data.initial; } - if( is_ascii() ) return data.initial; + if( data.capacity == 0 ) { + assert(0 == strlen(data.initial)); + return data.initial; + } + if( holds_ascii() && is_ascii() ) { + if( type != FldNumericEdited ) { + if( ! data.initial_within_capacity() ) { + ERROR_FIELD(this, "%s %s VALUE %qs of %zu exceeds size %u", + cbl_field_t::level_str(level), name, data.initial, + strlen(data.initial), data.capacity ); + } + } + return data.initial; + } assert(data.capacity > 0); - std::vector<char> output(data.capacity + 2, '\0'); - char *out = output.data(); - char *in = const_cast<char*>(data.initial); - size_t n, inbytesleft = data.capacity, outbytesleft = output.size(); + // The final 2 bytes of the output are "!\0". It's a debugging sentinel. + size_t n; + size_t inbytesleft = data.capacity; + size_t outbytesleft = inbytesleft; if( !is_literal(this) && inbytesleft < strlen(data.initial) ) { inbytesleft = strlen(data.initial); } + if( type == FldNumericEdited ) { + outbytesleft = inbytesleft; + } + const unsigned int in_len = inbytesleft; + + char *in = const_cast<char*>(data.initial); + char *out = static_cast<char*>( xcalloc(1, outbytesleft + 2) ), *output = out; assert(fromcode != tocode); - while( (n = iconv( cd, &in, &inbytesleft, &out, &outbytesleft)) == noconv ) { - if( !using_assumed ) break; // change only once - fromcode = guess_encoding(); - cd = iconv_open(tocode, fromcode); - dbgmsg("%s: trying input encoding %s", __func__, fromcode); - if( fromcode == tocode ) break; - } + /* + * If we're currently assuming the source code is encoded according to the + * locale (the default), and there's an iconv failure, try once more using a + * different assumption, that the source code is encoded as CP1252. + * + * This heuristic means that some UTF-8 literals could be converted until a + * CP1252 byte is encountered. We could be stricter about that. + * + * Also possible is a failure to avoid iconv with fromcode and tocode denote + * the same encoding but with different spellings, e.g. CP1252 and CP1252//. + */ - if( n == noconv ) { - if( !using_assumed ) { - yywarn("failed to decode '%s' as %s", data.initial, fromcode); - return NULL; + do { + if( (n = iconv( cd, &in, &inbytesleft, &out, &outbytesleft)) == noconv ) { + if( fromcode == codeset.source_encodings[0].name ) { + codeset.source_encoding = &codeset.source_encodings[1]; + fromcode = codeset.source_encoding->name; + tocodes.clear(); + cd = tocodes[toname] = iconv_open(tocode, fromcode); + dbgmsg("%s: trying input encoding %s", __func__, fromcode); + if( fromcode == tocode ) return data.initial; // no conversion required. + n = noconv - 1; // try again + } } + if( n == 0 ) break; + } while( n != noconv ); + + if( n == noconv ) { + size_t i = in_len - inbytesleft; + yyerror("failed to encode %s %qs as %s (%zu of %u bytes left)", + fromcode, data.initial + i, tocode, inbytesleft, in_len); + if( false ) return NULL; return data.initial; } if( 0 < inbytesleft ) { // data.capacity + inbytesleft is not correct if the remaining portion has - // multibyte characters. But the fact reamins that the VALUE is too big. + // multibyte characters. But the fact remains that the VALUE is too big. ERROR_FIELD(this, "%s %s VALUE '%s' requires %zu bytes for size %u", cbl_field_t::level_str(level), name, data.initial, data.capacity + inbytesleft, data.capacity ); } // Replace data.initial only if iconv output differs. - if( 0 != memcmp(data.initial, output.data(), out - output.data()) ) { - assert(out <= output.data() + data.capacity); - - if( getenv(__func__) ) { - const char *eoi = data.initial + data.capacity, *p; - char nullitude[64] = "no null"; - if( (p = std::find(data.initial, eoi, '\0')) != eoi ) { - sprintf(nullitude, "NUL @ %zu", p - data.initial); - } - dbgmsg("%s:%d: before: %-15s %-20s: '%.*s'{%u}, %s", __func__, __LINE__, - 3 + cbl_field_type_str(type), name, - data.capacity, data.initial, data.capacity, nullitude); - } + if( 0 != memcmp(data.initial, output, out - output) ) { + assert(out <= output + data.capacity || type == FldNumericEdited); dbgmsg("%s: converted '%.*s' to %s", __func__, data.capacity, data.initial, tocode); - - int len = int(out - output.data()); - char *mem = static_cast<char*>( xcalloc(1, output.size()) ); - - // Set the new memory to all blanks, tacking a '!' on the end. - memset(mem, 0x20, output.size() - 1); - mem[ output.size() - 2] = '!'; + struct localspace_t { + char space[4]; + size_t len, erc; + explicit localspace_t( iconv_t cd ) { + static char input[1] = { 0x20 }; + size_t inbytesleft2 = sizeof(input), outbytesleft2 = sizeof(space); + char *in2 = input, *out2 = space; + + erc = iconv(cd, &in2, &inbytesleft2, &out2, &outbytesleft2); + len = out2 - space; + } + bool valid() const { return 0 < len && erc != size_t(-1); } + } spc(cd); + + if( ! spc.valid() ) { + dbgmsg("%s:%d: iconv failed for %s: %s", __func__, __LINE__, + tocode, xstrerror(errno)); + ERROR_FIELD(this, "iconv failed: %s", xstrerror(errno)); + return data.initial; + } + assert( 0 < spc.len && spc.valid() ); if( is_literal(this) ) { - data.capacity = len; // trailing '!' will be overwritten + data.capacity = out - output; // trailing '!' will be overwritten } - - memcpy(mem, output.data(), len); // copy only as much as iconv converted - - free(const_cast<char*>(data.initial)); - data.initial = mem; - - if( getenv(__func__) ) { - const char *eoi = data.initial + data.capacity, *p; - char nullitude[64] = "no null"; - if( (p = std::find(data.initial, eoi, '\0')) != eoi ) { - sprintf(nullitude, "NUL @ %zu", p - data.initial); - } - dbgmsg("%s:%d: after: %-15s %-20s: '%.*s'{%u}, %s", __func__, __LINE__, - "", name, - data.capacity, data.initial, data.capacity, nullitude); + // Pad with trailing blanks, tacking a '!' on the end. + for( const char *eout = output + data.capacity; + out < eout; + out += spc.len ) { + memcpy(out, spc.space, spc.len); } - + // Numeric literal strings may have leading zeros, making their length + // longer than their capacity. + out[0] = type == FldLiteralN? '\0' : '!'; + assert(out[1] == '\0'); + data.orig = data.initial; + data.initial = output; + } else { + free(output); } return data.initial; @@ -3703,8 +4005,8 @@ cbl_label_t::str() const { buf = xasprintf("%-12s %s top level [%s], line %d", type_str() + 3, name, mangled_name, line); } else { - buf = xasprintf("%-12s %s OF #%zu '%s' [%s], line %d", - type_str() + 3, name, parent, + buf = xasprintf("%-12s %s OF #" HOST_SIZE_T_PRINT_UNSIGNED " '%s' [%s], line %d", + type_str() + 3, name, (fmt_size_t)parent, cbl_label_of(symbol_at(parent))->name, mangled_name, line); } @@ -3734,12 +4036,9 @@ cbl_label_t::explicit_parent() const { } cbl_prog_hier_t::cbl_prog_hier_t() { - nlabel = std::count_if( symbols_begin(), symbols_end(), is_program ); - assert(nlabel >0); - labels = new cbl_prog_hier_t::program_label_t[nlabel]; - std::copy_if( symbols_begin(), symbols_end(), - labels, is_program ); + std::back_inserter(labels), is_program ); + assert(! labels.empty()); } /* @@ -3804,44 +4103,22 @@ common_callables_update( const size_t iprog ) { cbl_label_t * symbol_label_add( size_t program, cbl_label_t *input ) { - if( getenv(__func__) ) { - const cbl_label_t *L = input; - dbgmsg( "%s:%d: %-5s #%3zu %-9s '%s' of '%s' at line %d", __func__, __LINE__, - "input", - size_t(0), - L->type_str()+3, - L->name, - L->parent? cbl_label_of(symbol_at(L->parent))->name : "", - L->line ); - } - cbl_label_t *label = symbol_label(program, input->type, input->parent, input->name); if( label && label->type == LblNone ) { - const char *verb = "set"; label->type = input->type; label->parent = input->parent; label->line = input->line; - if( getenv(__func__) ) { - const cbl_label_t *L = label; - dbgmsg( "%s:%d: %-5s #%3zu %-9s '%s' of '%s' at line %d", - __func__, __LINE__, - verb, - symbol_elem_of(L) - symbols_begin(), - L->type_str()+3, - L->name, - L->parent? cbl_label_of(symbol_at(L->parent))->name : "", - L->line ); - } return label; } // Set the program's mangled name, dehyphenated and uniqified by parent index. if( input->type == LblProgram ) { char *psz = cobol_name_mangler(input->name); - input->mangled_name = xasprintf("%s.%zu", psz, input->parent); + input->mangled_name = xasprintf("%s." HOST_SIZE_T_PRINT_UNSIGNED, + psz, (fmt_size_t)input->parent); free(psz); } @@ -3854,59 +4131,45 @@ symbol_label_add( size_t program, cbl_label_t *input ) if( (e = symbol_add(&elem)) == NULL ) { cbl_errx("%s:%d: could not add '%s'", __func__, __LINE__, label->name); } + assert(e); common_callables_update( symbol_index(e) ); // restore munged line number unless symbol_add returned an existing label if( e->elem.label.line < 0 ) e->elem.label.line = -e->elem.label.line; - if( getenv(__func__) ) { - const cbl_label_t *L = cbl_label_of(e); - dbgmsg( "%s:%d: added #%3zu %-9s '%s' of '%s' at line %d", __func__, __LINE__, - e - symbols_begin(), - L->type_str()+3, - L->name, - L->parent? cbl_label_of(symbol_at(L->parent))->name : "", - L->line ); - } symbols.labelmap_add(e); return cbl_label_of(e); } /* - * Under ISO (and not IBM) Declaratives are followed by a Section name. When - * the first statement is parsed, verify, if Declaratives were used, that it + * Under ISO (and not IBM) Declaratives are followed by a Section name. If + * Declaratives were used, when the first statement is parsed verify that it * was preceeded by a Section name. */ bool -symbol_label_section_exists( size_t program ) { - auto pblob = std::find_if( symbols_begin(program), symbols_end(), - []( const auto& sym ) { - if( sym.type == SymField ) { - auto& f( sym.elem.field ); - return f.type == FldBlob; - } - return false; - } ); - if( pblob == symbols_end() ) return true; // Section name not required - - bool has_section = std::any_of( ++pblob, symbols_end(), - []( const auto& sym ) { - if( sym.type == SymLabel ) { - auto& L(sym.elem.label); - if( L.type == LblSection ) { - if( L.name[0] != '_' ) { // not implicit - return true; // Section name exists - } - } +symbol_label_section_exists( size_t eval_label_index ) { + auto eval = symbols_begin(eval_label_index); + /* cppcheck warns that the following statement depends on the order of + evaluation of side effects. Since this isn't my code, and since I don't + think the warning can be eliminated without rewriting it, I am just + supprressing it. + -- Bob Dubner, 2025-07-14 */ + // cppcheck-suppress unknownEvaluationOrder + bool has_section = std::any_of( ++eval, symbols_end(), + [program = eval->program]( const auto& sym ) { + if( program == sym.program && sym.type == SymLabel ) { + const auto& L(sym.elem.label); + // true if the symbol is an explicit label. + return L.type == LblSection && L.name[0] != '_'; } return false; } ); if( yydebug && ! has_section ) { - symbols_dump(program, true); + symbols_dump(eval_label_index, true); } - // Return true if no Declaratives, because the (non-)requirement is met. - // Return false if Declaratives exist, because no Section name was found. + // Return true if a user-defined SECTION was found after the Declaratives + // label section. return has_section; } @@ -3919,7 +4182,8 @@ symbol_program_add( size_t program, cbl_label_t *input ) // Set the program's mangled name, dehyphenated and uniqified by parent index. char *psz = cobol_name_mangler(input->name); - elem.elem.label.mangled_name = xasprintf("%s.%zu", psz, input->parent); + elem.elem.label.mangled_name = xasprintf("%s." HOST_SIZE_T_PRINT_UNSIGNED, + psz, (fmt_size_t)input->parent); free(psz); e = std::find_if( symbols_begin(program), symbols_end(), @@ -3961,11 +4225,6 @@ symbol_special_add( size_t program, struct cbl_special_name_t *special ) struct symbol_elem_t *e = symbol_special(program, special->name); if( e ) { - cbl_special_name_t *s = cbl_special_name_of(e); - if( getenv(__func__) ) { - dbgmsg("%s:%d matches %s %d (%s)", __func__, __LINE__, - special->name, int(s->id), s->name); - } return e; } assert(e == NULL); @@ -3976,11 +4235,6 @@ symbol_special_add( size_t program, struct cbl_special_name_t *special ) cbl_errx( "%s:%d: could not add '%s'", __func__, __LINE__, special->name); } - if( getenv(__func__) ) { - dbgmsg( "%s:%d: added special '%s'", __func__, __LINE__, - e->elem.special.name); - } - elem_key_t key(program, cbl_special_name_of(e)->name); symbols.specials[key] = symbol_index(e); @@ -4057,7 +4311,7 @@ expand_picture(const char *picture) { assert(strlen(picture) < PICTURE_MAX); // guaranteed by picset() in scanner size_t retval_length = PICTURE_MAX; - char *retval = (char *)xmalloc(retval_length); + char *retval = static_cast<char *>(xmalloc(retval_length)); size_t index = 0; int ch; @@ -4086,7 +4340,7 @@ expand_picture(const char *picture) if( index + repeat >= retval_length ) { retval_length <<= 1; - retval = (char *)xrealloc(retval, retval_length); + retval = static_cast<char *>(xrealloc(retval, retval_length)); } while(repeat--) @@ -4099,7 +4353,7 @@ expand_picture(const char *picture) if( index >= retval_length ) { retval_length <<= 1; - retval = (char *)xrealloc(retval, retval_length); + retval = static_cast<char *>(xrealloc(retval, retval_length)); } retval[index++] = ch; } @@ -4108,7 +4362,7 @@ expand_picture(const char *picture) if( index >= retval_length ) { retval_length <<= 1; - retval = (char *)xrealloc(retval, retval_length); + retval = static_cast<char *>(xrealloc(retval, retval_length)); } retval[index++] = '\0'; @@ -4131,7 +4385,6 @@ expand_picture(const char *picture) { pcurrency[i] = 'B'; } - dest_length += sign_length; } } @@ -4362,7 +4615,7 @@ symbol_program_callables( size_t program ) { if( e->type != SymLabel ) continue; if( e->elem.label.type != LblProgram ) continue; - auto prog = cbl_label_of(e); + const cbl_label_t * prog = cbl_label_of(e); if( program == symbol_index(e) && !prog->recursive ) continue; if( (self->parent == prog->parent && prog->common) || @@ -4396,6 +4649,7 @@ symbol_program_local( const char tgt_name[] ) { */ std::map<char, const char *> currencies; +// cppcheck-suppress-begin [nullPointerRedundantCheck] bool symbol_currency_add( const char symbol[], const char sign[] ) { // In service of CURRENCY sign PICTURE SYMBOL symbol @@ -4407,6 +4661,7 @@ symbol_currency_add( const char symbol[], const char sign[] ) { currencies[*symbol] = sign; return true; } +// cppcheck-suppress-end [nullPointerRedundantCheck] const char * symbol_currency( char sign ) { @@ -4414,6 +4669,11 @@ symbol_currency( char sign ) { if( currencies.size() == 0 ) { currencies['$'] = "$"; } + if( sign == '\0' ) { // default + auto result = currencies.begin(); + gcc_assert(result != currencies.end()); + return result->second; + } auto result = currencies.find(sign); return result == currencies.end()? NULL : result->second; } @@ -4429,24 +4689,19 @@ bool decimal_is_comma() { return decimal_point == ','; } /* * A cbl_occurs_key_t is part of a field definition, and comprises * size_t symbol indexes. A cbl_key_t is a list of field pointers, - * and can be created ad hoc to describe a sort. We can construct a + * and can be created ad hoc to describe a sort. We construct a * cbl_key_t from cbl_occurs_key_t. */ cbl_key_t:: cbl_key_t( const cbl_occurs_key_t& that ) : ascending(that.ascending) { - if( that.field_list.nfield == 0 ) { - *this = cbl_key_t(); - return; - } - - nfield = that.field_list.nfield; - fields = static_cast<cbl_field_t**>( xcalloc(nfield, - sizeof(*fields)) ); - for( size_t i=0; i < that.field_list.nfield; i++ ) { - fields[i] = cbl_field_of(symbol_at(that.field_list.fields[i])); - } + std::transform( that.field_list.fields, + that.field_list.fields + that.field_list.nfield, + std::back_inserter(fields), + []( size_t isym ) { + return cbl_field_of(symbol_at(isym)); + } ); } void @@ -4459,7 +4714,7 @@ cbl_occurs_t::key_alloc( bool ascending ) { } void -cbl_occurs_t::field_add( cbl_field_list_t& field_list, cbl_field_t *field ) { +cbl_occurs_t::field_add( cbl_field_list_t& field_list, const cbl_field_t *field ) { cbl_field_list_t list = field_list; size_t ifield = field_index(field); auto nbytes = sizeof(list.fields[0]) * (list.nfield + 1); @@ -4477,14 +4732,14 @@ cbl_occurs_t::key_field_add( cbl_field_t *field ) { } void -cbl_occurs_t::index_add( cbl_field_t *field ) { +cbl_occurs_t::index_add( const cbl_field_t *field ) { field_add(indexes, field); } class is_field_at { cbl_field_t *field; public: - is_field_at( cbl_field_t *field ) : field(field) {} + explicit is_field_at( cbl_field_t *field ) : field(field) {} bool operator()( size_t isym ) const { return field == field_at(isym); } @@ -4511,15 +4766,40 @@ cbl_occurs_t::subscript_ok( const cbl_field_t *subscript ) const { // It must be a number. if( subscript->type != FldLiteralN ) return false; - auto sub = subscript->data.value_of(); + // This only gets us int64_t, which is more than adequate for a table subscript + auto sub = real_to_integer (TREE_REAL_CST_PTR (subscript->data.value_of())); + REAL_VALUE_TYPE csub; + real_from_integer (&csub, VOIDmode, sub, SIGNED); - if( sub < 1 || sub != size_t(sub) ) { + if( sub < 1 + || !real_identical (&csub, + TREE_REAL_CST_PTR (subscript->data.value_of())) ) { return false; // zero/fraction invalid } if( bounds.fixed_size() ) { - return sub <= bounds.upper; + return (size_t)sub <= bounds.upper; } - return bounds.lower <= sub && sub <= bounds.upper; + return bounds.lower <= (size_t)sub && (size_t)sub <= bounds.upper; +} + +const cbl_field_t * +symbol_unresolved_file_key( const cbl_file_t * file, + const cbl_name_t key_field_name ) { + const symbol_elem_t *file_sym = symbol_elem_of(file); + size_t program = file_sym->program; + for( const symbol_elem_t *e = file_sym - 1; e->program == program; e-- ) { + if( e->type == SymFile ) break; + if( e->type == SymField ) { + auto f = cbl_field_of(e); + if( f->type == FldLiteralA ) break; + if( f->type == FldForward ) { + if( 0 == strcmp(key_field_name, f->name) ) { + return f; + } + } + } + } + return nullptr; } cbl_file_key_t:: @@ -4605,8 +4885,8 @@ symbol_forward_names( size_t ifield ) { for( auto sym = symbols_begin(ifield); sym && sym->type == SymField; ) { const cbl_field_t *field = cbl_field_of(sym); if( !(field->type == FldForward) ) { - dbgmsg("%s:%d: logic error, not FldForward: #%zu %s", - __func__, __LINE__, symbol_index(sym), field_str(field)); + dbgmsg("%s:%d: logic error, not FldForward: #" HOST_SIZE_T_PRINT_UNSIGNED " %s", + __func__, __LINE__, (fmt_size_t)symbol_index(sym), field_str(field)); } assert(field->type == FldForward); @@ -4629,8 +4909,9 @@ symbol_forward_to( size_t fwd ) { if( !elem.second ) { const auto& field = *cbl_field_of(symbols_begin(fwd)); if( yydebug ) - dbgmsg("%s:%d: no symbol found for #%zu %s %s", __func__, __LINE__, - fwd, cbl_field_type_str(field.type), field.name); + dbgmsg("%s:%d: no symbol found for #" HOST_SIZE_T_PRINT_UNSIGNED " %s %s", + __func__, __LINE__, + (fmt_size_t)fwd, cbl_field_type_str(field.type), field.name); return fwd; } @@ -4646,13 +4927,14 @@ cbl_file_key_t::deforward( size_t ifile ) { const auto file = cbl_file_of(symbol_at(ifile)); std::transform( fields, fields + nfield, fields, [ifile, file]( size_t fwd ) { - static std::map<size_t, int> keys; auto ifield = symbol_forward_to(fwd); const auto field = cbl_field_of(symbol_at(ifield)); if( is_forward(field) && yydebug ) { - dbgmsg("%s:%d: key %d: #%zu %s of %s is %s", "deforward", __LINE__, - keys[ifile]++, ifield, field->name, file->name, + static std::map<size_t, int> keys; + dbgmsg("%s:%d: key %d: #" HOST_SIZE_T_PRINT_UNSIGNED " %s of %s is %s", + "deforward", __LINE__, + keys[ifile]++, (fmt_size_t)ifield, field->name, file->name, cbl_field_type_str(field->type) + 3); } @@ -4661,7 +4943,7 @@ cbl_file_key_t::deforward( size_t ifile ) { if( ifield == fwd ) { ERROR_FIELD(field, "line %d: %s of %s " "is not defined", - file->line, field->name, file->name); + field->line, field->name, file->name); return ifield; } @@ -4690,9 +4972,13 @@ cbl_file_key_t::deforward( size_t ifile ) { // looked-up field must have same file as parent if( ! (parent != NULL && symbol_index(symbol_elem_of(parent)) == ifile) ) { - ERROR_FIELD(field, "line %d: %s of %s " - "is not defined in file description", - file->line, field->name, file->name); + const cbl_field_t *undefined = + symbol_unresolved_file_key(file, field->name); + int lineno = undefined? undefined->line : file->line; + ERROR_FIELD(undefined? undefined : field, + "line %d: %s of %s " + "is not defined in file description", + lineno, field->name, file->name); } return ifield; } ); @@ -4706,7 +4992,7 @@ cbl_file_key_t::str() const { *p++ = '['; for( auto f = fields; f < fields + nfield; f++) { - auto n = sprintf(p, "%s%zu", sep, *f); + auto n = sprintf(p, "%s" HOST_SIZE_T_PRINT_UNSIGNED, sep, (fmt_size_t)*f); p += n; sep = ", "; } @@ -4719,6 +5005,8 @@ cbl_file_key_t::str() const { */ void cbl_file_t::deforward() { + const size_t ifile( symbol_index(symbol_elem_of(this)) ); + if( user_status ) { user_status = symbol_forward_to(user_status); @@ -4730,33 +5018,18 @@ cbl_file_t::deforward() { } for( auto p = keys; p < keys + nkey; p++ ) { - p->deforward( symbol_index(symbol_elem_of(this)) ); + p->deforward(ifile); } } char * cbl_file_t::keys_str() const { - std::vector <char *> ks(nkey); - std::transform(keys, keys + nkey, ks.begin(), - []( const cbl_file_key_t& key ) { - return key.str(); - } ); - size_t n = 4 * nkey + std::accumulate(ks.begin(), ks.end(), 0, - []( int n, const char *s ) { - return n + strlen(s); - } ); - char *output = static_cast<char*>( xcalloc(1, n) ), *p = output; - const char *sep = ""; - - *p++ = '['; - for( auto k : ks ) { - p = stpcpy(p, sep); - p = stpcpy(p, k); - sep = ", "; - free(k); + std::string names = "["; + for( cbl_file_key_t *p = keys; p < keys + nkey; p++ ) { + names += p->str(); + names += p + 1 < keys + nkey ? "," : "]"; } - *p++ = ']'; - return output; + return xasprintf("%s", names.c_str()); } /* @@ -4818,11 +5091,13 @@ cbl_file_status_cmp( const void *K, const void *E ) { static long file_status_status_of( file_status_t status ) { size_t n = COUNT_OF(file_status_fields); - file_status_field_t *fs, key { status }; - - fs = (file_status_field_t*)lfind( &key, file_status_fields, - &n, sizeof(*fs), cbl_file_status_cmp ); + const file_status_field_t *fs, key { status }; + fs = static_cast<file_status_field_t*>(lfind( &key, + file_status_fields, + &n, + sizeof(*fs), + cbl_file_status_cmp )); return fs? (long)fs->status : -1; } @@ -4848,21 +5123,6 @@ ast_file_status_between( file_status_t lower, file_status_t upper ) { } bool -is_register_field(cbl_field_t *field) - { - // TRUE when the field is an executable-level global variable of the type we - // are calling a "register", like RETURN-CODE or UPSI or the like: - return - ( field->parent == 0 - && field->level == 0 - && !(field->attr & intermediate_e) - && !(field->attr & filler_e) - && field->type != FldClass - && field->type != FldBlob - ); - } - -bool has_value( cbl_field_type_t type ) { // Indicates that the field type contains data that can be expressed as // a numeric value @@ -4878,7 +5138,6 @@ has_value( cbl_field_type_t type ) { case FldForward: case FldSwitch: case FldDisplay: - case FldBlob: return false; case FldIndex: case FldPointer: diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h index c189412..e0a7195 100644 --- a/gcc/cobol/symbols.h +++ b/gcc/cobol/symbols.h @@ -32,11 +32,11 @@ #else #define _SYMBOLS_H_ -#include <assert.h> -#include <limits.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> +#include <cassert> +#include <climits> +#include <cstdint> +#include <cstdlib> +#include <cstring> #include <algorithm> #include <list> @@ -48,50 +48,61 @@ #define PICTURE_MAX 64 -// Define a tree type as void pointer outside the generator code. -#ifndef HOWEVER_GCC_DEFINES_TREE -typedef void *tree; -#endif - -#if ! (__HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT)) -static_assert( sizeof(output) == sizeof(long double), "long doubles?" ); - -static inline _Float128 -strtof128 (const char *__restrict __nptr, char **__restrict __endptr) { - return strtold(nptr, endptr); -} - -static inline int -strfromf128 (char *restrict string, size_t size, - const char *restrict format, _Float128 value) { - return strfroml(str, n, format, fp); -} -#endif - extern const char *numed_message; enum cbl_dialect_t { - dialect_gcc_e = 0x00, - dialect_ibm_e = 0x01, - dialect_mf_e = 0x02, - dialect_gnu_e = 0x04, + dialect_iso_e = 0x00, + dialect_gcc_e = 0x01, + dialect_ibm_e = 0x02, + dialect_mf_e = 0x04, + dialect_gnu_e = 0x08, }; -extern cbl_dialect_t cbl_dialect; +static inline const char * +cbl_dialect_str(cbl_dialect_t dialect) { + switch(dialect) { + case dialect_iso_e: return "iso"; + case dialect_gcc_e: return "gcc"; + case dialect_ibm_e: return "ibm"; + case dialect_mf_e: return "mf"; + case dialect_gnu_e: return "gnu"; + } + + switch(size_t(dialect)) { + case dialect_mf_e | dialect_gnu_e: return "mf or gnu"; + case dialect_ibm_e | dialect_mf_e | dialect_gnu_e: return "ibm or mf or gnu"; + } + + return "???"; +}; + +// Dialects may be combined. +extern unsigned int cbl_dialects; void cobol_dialect_set( cbl_dialect_t dialect ); -cbl_dialect_t dialect_is(); +// GCC dialect means no other dialects static inline bool dialect_gcc() { - return dialect_gcc_e == cbl_dialect; + return dialect_gcc_e == cbl_dialects; } - static inline bool dialect_ibm() { - return dialect_ibm_e == (cbl_dialect & dialect_ibm_e); + return dialect_ibm_e == (cbl_dialects & dialect_ibm_e); } static inline bool dialect_mf() { - return dialect_mf_e == (cbl_dialect & dialect_mf_e ); + return dialect_mf_e == (cbl_dialects & dialect_mf_e ); +} +static inline bool dialect_gnu() { + return dialect_gnu_e == (cbl_dialects & dialect_gnu_e ); } +static inline bool dialect_has( cbl_dialect_t dialect) { + return 0 < (cbl_dialects & dialect); +} + +#ifdef GCC_DIAGNOSTIC_H +bool cbl_diagnostic_kind( cbl_diag_id_t id, diagnostics::kind kind ); +bool cbl_dialect_kind( cbl_dialect_t dialect, diagnostics::kind kind ); +#endif + enum cbl_gcobol_feature_t { feature_gcc_e = 0x00, feature_internal_ebcdic_e = 0x01, @@ -135,7 +146,6 @@ is_numeric( cbl_field_type_t type ) { case FldSwitch: case FldDisplay: case FldPointer: // not numeric because not computable, only settable - case FldBlob: return false; // These types are computable or, in the case of FldIndex, may be // arbitrarily set and incremented. @@ -148,24 +158,20 @@ is_numeric( cbl_field_type_t type ) { case FldIndex: return true; } - yywarn( "%s:%d: invalid symbol_type_t %d", __func__, __LINE__, type ); + cbl_internal_error( "%s:%d: invalid %<symbol_type_t%> %d", __func__, __LINE__, type ); return false; } struct os_locale_t { char assumed[16]; - char *codeset; + const char *codeset; }; const char * cbl_field_attr_str( cbl_field_attr_t attr ); cbl_field_attr_t literal_attr( const char prefix[] ); -static inline bool -is_working_storage(uint32_t attr) { - return 0 == (attr & (linkage_e | local_e)); -} - +int cbl_figconst_tok( const char *value ); enum cbl_figconst_t cbl_figconst_of( const char *value ); const char * cbl_figconst_str( cbl_figconst_t fig ); @@ -189,7 +195,7 @@ class cbl_domain_elem_t { { if( value && ! is_numeric ) { auto s = consistent_encoding_check(loc, value); - if( s ) value = s; + if( s ) this->value = s; } } const char *name() const { return value; } @@ -235,21 +241,27 @@ bool decimal_is_comma(); enum symbol_type_t { SymFilename, - SymFunction, SymField, SymLabel, // section, paragraph, or label SymSpecial, SymAlphabet, SymFile, SymDataSection, + SymLocale, }; +// The ISO specification says alphanumeric literals have a maximum length of +// 8,191 characters. It seems to be silent on the length of alphanumeric data +// items. Our implementation requires a maximum length, so we chose to make it +// the same. +#define MAXIMUM_ALPHA_LENGTH 8192 + struct cbl_field_data_t { uint32_t memsize; // nonzero if larger subsequent redefining field uint32_t capacity, // allocated space digits; // magnitude: total digits (or characters) int32_t rdigits; // digits to the right - const char *initial, *picture; + const char *orig, *initial, *picture; enum etc_type_t { val88_e, upsi_e, value_e } etc_type; const char * @@ -270,34 +282,48 @@ struct cbl_field_data_t { val88_t() : false_value(NULL), domain(NULL) {} } val88; struct cbl_upsi_mask_t *upsi_mask; - _Float128 value; + tree value; - explicit etc_t( double v = 0.0 ) : value(v) {} + explicit etc_t( tree v = build_zero_cst (float128_type_node)) : value(v) {} } etc; - cbl_field_data_t( uint32_t memsize=0, uint32_t capacity=0 ) + cbl_field_data_t() + : memsize(0) + , capacity(0) + , digits(0) + , rdigits(0) + , orig(0) + , initial(0) + , picture(0) + , etc_type(value_e) + , etc() + {} + + cbl_field_data_t( uint32_t memsize, uint32_t capacity ) : memsize(memsize) , capacity(capacity) , digits(0) , rdigits(0) + , orig(0) , initial(0) , picture(0) , etc_type(value_e) - , etc(0) + , etc() {} cbl_field_data_t( uint32_t memsize, uint32_t capacity, - uint32_t digits, uint32_t rdigits, - const char *initial, - const char *picture = NULL ) + uint32_t digits, uint32_t rdigits, + const char *initial, + const char *picture = NULL ) : memsize(memsize) , capacity(capacity) , digits(digits) , rdigits(rdigits) + , orig(0) , initial(initial) , picture(picture) , etc_type(value_e) - , etc(0) + , etc() {} cbl_field_data_t( const cbl_field_data_t& that ) { @@ -328,17 +354,24 @@ struct cbl_field_data_t { etc_type = upsi_e; return etc.upsi_mask = mask; } - _Float128 value_of() const { + tree value_of() const { if( etc_type != value_e ) { dbgmsg("%s:%d: type is %s", __func__, __LINE__, etc_type_str()); } -//// assert(etc_type == value_e); return etc.value; } - _Float128& operator=( _Float128 v) { + tree& operator=( tree v) { etc_type = value_e; return etc.value = v; } + tree& operator=(int i) { + etc_type = value_e; + return etc.value = build_int_cst_type(integer_type_node, i); + } + + void set_real_from_capacity( REAL_VALUE_TYPE *r ) const { + real_from_integer (r, VOIDmode, capacity, SIGNED); + } time_now_f time_func; @@ -354,21 +387,24 @@ struct cbl_field_data_t { cbl_field_data_t& valify() { assert(initial); - const size_t len = strlen(initial); - std::string input(len + 1, '\0'); // add a NUL - std::copy(initial, initial + len, input.begin()); + std::string input(initial); if( decimal_is_comma() ) { std::replace(input.begin(), input.end(), ',', '.'); } - char *pend = NULL; + double d; + int n; + int erc = sscanf(input.c_str(), "%lf%n", &d, &n); - etc.value = strtof128(input.c_str(), &pend); - - if( pend != input.c_str() + len ) { + if( erc < 0 || size_t(n) != input.size() ) { dbgmsg("%s: error: could not interpret '%s' of '%s' as a number", - __func__, pend, initial); + __func__, initial + n, initial); } + + REAL_VALUE_TYPE r; + real_from_string (&r, input.c_str()); + r = real_value_truncate (TYPE_MODE (float128_type_node), r); + etc.value = build_real (float128_type_node, r); return *this; } cbl_field_data_t& valify( const char *input ) { @@ -378,6 +414,32 @@ struct cbl_field_data_t { return valify(); } + // If initial (of Numeric Edited) has any length but capacity, adjust it. + bool manhandle_initial() { + assert(capacity > 0); + assert(initial != nullptr); + if( capacity < strlen(initial) ) { + char *p = const_cast<char*>(initial); + p[capacity] = '\0'; + return true; + } + if( strlen(initial) < capacity ) { + auto tgt = reinterpret_cast<char *>( xmalloc(capacity + 1) ); + auto pend = tgt + capacity; + auto p = std::copy(initial, initial + strlen(initial), tgt); + std::fill(p, pend, 0x20); + p = pend - 1; + *p = '\0'; + initial = tgt; + } + return false; + } + bool initial_within_capacity() const { + return initial[capacity] == '\0' + || initial[capacity] == '!'; + } + const char *original() const { return orig? orig : initial; } + protected: cbl_field_data_t& copy_self( const cbl_field_data_t& that ) { memsize = that.memsize; @@ -390,14 +452,14 @@ struct cbl_field_data_t { switch(etc_type) { case value_e: - etc.value = that.etc.value; - break; + etc.value = that.etc.value; + break; case val88_e: - etc.val88 = that.etc.val88; - break; + etc.val88 = that.etc.val88; + break; case upsi_e: - etc.upsi_mask = that.etc.upsi_mask; - break; + etc.upsi_mask = that.etc.upsi_mask; + break; } return *this; } @@ -417,8 +479,11 @@ struct cbl_occurs_bounds_t { // variable size table. lower can be zero. size_t lower, upper; - cbl_occurs_bounds_t(size_t lower=0, size_t upper=0) + cbl_occurs_bounds_t() + : lower(0), upper(0) {} + explicit cbl_occurs_bounds_t(size_t lower, size_t upper=0) : lower(lower), upper(upper) {} + size_t ntimes() const { return upper; } @@ -451,12 +516,12 @@ struct cbl_occurs_t { void key_alloc( bool ascending ); void key_field_add( cbl_field_t *field ); - void index_add( cbl_field_t *field ); + void index_add( const cbl_field_t *field ); cbl_occurs_key_t * key_of( cbl_field_t *field ); bool subscript_ok( const cbl_field_t *subscript ) const; protected: - void field_add( cbl_field_list_t& fields, cbl_field_t *field ); + void field_add( cbl_field_list_t& fields, const cbl_field_t *field ); }; /* @@ -487,17 +552,101 @@ struct cbl_subtable_t { size_t offset, isym; }; +const char * __gg__encoding_iconv_name( cbl_encoding_t encoding ); +bool __gg__encoding_iconv_valid( cbl_encoding_t encoding ); + bool is_elementary( enum cbl_field_type_t type ); +// These were introduced to discourage the use of +// current_encoding('A') and current_encoding('N') +enum + { + display_encoding_e = 'A', + national_encoding_e = 'N' + }; +cbl_encoding_t current_encoding( char a_or_n ); + +/* In cbl_field_t: + * 'offset' is overloaded for FldAlphanumeric/temporary/intermediate variables + * For such variables, offset is a copy of the initial capacity. This is in + * support of the FUNCTION TRIM function, which both needs to be able to + * reduce the capacity of the target variable, and then to reset it back to + * the original value + */ + struct cbl_field_t { size_t offset; - enum cbl_field_type_t type, usage; - size_t attr; + cbl_field_type_t type, usage; + uint64_t attr; static_assert(sizeof(attr) == sizeof(cbl_field_attr_t), "wrong attr size"); size_t parent; // symbols[] index of our parent size_t our_index; // symbols[] index of this field, set in symbol_add() uint32_t level; - struct cbl_occurs_t occurs; + cbl_occurs_t occurs; + struct codeset_t { + static const encodings_t standard_internal, source_encodings[2], *source_encoding; + cbl_encoding_t encoding; + size_t alphabet; // unlikely + explicit codeset_t(cbl_encoding_t encoding = custom_encoding_e, + size_t alphabet = 0) // combination means "not set" + : encoding(encoding), alphabet(alphabet) + {} + bool valid() const { + return + (alphabet == 0 && encoding != custom_encoding_e) + || + (alphabet != 0 && encoding == custom_encoding_e); + } + bool consistent() const { + return valid() && ( encoding == current_encoding('A') + || + encoding == current_encoding('N') + || + encoding == UTF8_e ); + } + bool set( cbl_encoding_t encoding, size_t alphabet = 0 ) { + assert(valid_encoding(encoding)); + if( ! valid() ) { // setting first time + this->encoding = encoding; + this->alphabet = alphabet; + return valid(); + } + return this->encoding == encoding && this->alphabet == alphabet; + } + bool set( const char picture_fragment[] = nullptr) { + if( ! picture_fragment ) { + cbl_encoding_t enc = current_encoding('A'); + bool retval = set(enc); + return retval; + } + size_t len = strlen(picture_fragment); + std::vector<char> frag(len); + std::transform(picture_fragment, picture_fragment + len, + frag.begin(), ftoupper); + switch(frag[0]) { + case 'A': case 'X': case '9': + return set(current_encoding(display_encoding_e)); + case 'N': case 'U': + if( std::all_of(frag.begin(), frag.end(), + [first = frag[0]]( char ch ) { + return first == ch; + } ) ) { + // All N's indicates National; all U's indicates UTF-8. + auto enc = frag[0] == 'N' ? current_encoding(national_encoding_e) + : UTF8_e; + return set(enc); + } + return false; // They all must be the same. + } + gcc_unreachable(); + } + cbl_encoding_t set() const { + return valid()? encoding : cbl_encoding_t(-1); + } + const char *name() const { + return valid()? __gg__encoding_iconv_name(encoding) : "nocoding"; + } + } codeset; int line; // Where it appears in the file. cbl_name_t name; // Appears in the GIMPLE dump. size_t file; // nonzero if field is 01 record for a file @@ -506,19 +655,45 @@ struct cbl_field_t { cbl_ffi_crv_t crv; // Using by C/R/V in Linkage linkage_t() : optional(false), crv(by_default_e) {} } linkage; - struct cbl_field_data_t data; + cbl_field_data_t data; tree var_decl_node; // Reference to the pointer to the cblc_field_t structure tree data_decl_node; // Reference to the run-time data of the COBOL variable // // For linkage_e variables, data_decl_node is a pointer // // to the data, rather than the actual data - tree literal_decl_node; // This is a FLOAT128 version of data.value + cbl_field_t() + : offset(0), type(FldInvalid), usage(FldInvalid), attr(0) + , parent(0), our_index(0), level(0) + , line(0), name(""), file(0) + , var_decl_node(nullptr), data_decl_node(nullptr) + {} + + cbl_field_t( cbl_field_type_t type, uint64_t attr, + const cbl_field_data_t& data, + uint32_t level = 0, const cbl_name_t name = "", int line = 0 ) + : offset(0), type(type), usage(FldInvalid), attr(attr) + , parent(0), our_index(0), level(level) + , line(line), name(""), file(0), data(data) + , var_decl_node(nullptr), data_decl_node(nullptr) + { + gcc_assert(strlen(name) < sizeof this->name); + strcpy(this->name, name); + } + + cbl_field_t( cbl_field_type_t type, uint32_t level, int line, uint64_t attr = 0 ) + : offset(0), type(type), usage(FldInvalid), attr(attr) + , parent(0), our_index(0), level(level) + , line(line), name(""), file(0) + , var_decl_node(nullptr), data_decl_node(nullptr) + {} void set_linkage( cbl_ffi_crv_t crv, bool optional ) { linkage.optional = optional; linkage.crv = crv; assert(crv != by_content_e); } + bool holds_ascii() const; + inline bool is_typedef() const { return has_attr(typedef_e); } @@ -536,6 +711,10 @@ struct cbl_field_t { || type == FldLiteralN; } + bool is_zero() const { + return real_zerop(data.value_of()); + } + bool rename_level_ok() const { switch( level ) { case 0: @@ -558,10 +737,11 @@ struct cbl_field_t { attr |= same_as_e; data = that.data; - + codeset = that.codeset; + if( ! (is_typedef || that.type == FldClass) ) { data.initial = NULL; - data = _Float128(0.0); + data = build_zero_cst (float128_type_node); } return *this; } @@ -575,6 +755,10 @@ struct cbl_field_t { return type == FldNumericBinary || type == FldNumericBin5; } + HOST_WIDE_INT as_integer() const { + return real_to_integer( TREE_REAL_CST_PTR (data.value_of()) ); + } + void embiggen( size_t eight=8 ) { assert(gcobol_feature_embiggen() && is_numeric(type) && size() == 4); @@ -587,8 +771,8 @@ struct cbl_field_t { bool has_attr( cbl_field_attr_t attr ) const { return cbl_field_attr_t(this->attr & attr) == attr; } - size_t set_attr( cbl_field_attr_t attr ); - size_t clear_attr( cbl_field_attr_t attr ); + uint64_t set_attr( cbl_field_attr_t attr ); + uint64_t clear_attr( cbl_field_attr_t attr ); const char * attr_str( const std::vector<cbl_field_attr_t>& attrs ) const; bool is_justifiable() const { @@ -600,7 +784,6 @@ struct cbl_field_t { bool has_subordinate( const cbl_field_t *that ) const; const char * internalize(); - bool value_set( _Float128 value ); const char *value_str() const; bool is_key_name() const { return has_attr(record_key_e); } @@ -614,7 +797,7 @@ struct cbl_field_t { uint32_t size() const; // table capacity or capacity const char * pretty_name() const { - if( name[0] == '_' && data.initial ) return data.initial; + if( name[0] == '_' && data.original() ) return data.original(); return name; } static const char * level_str(uint32_t level ); @@ -623,6 +806,8 @@ struct cbl_field_t { } }; +const cbl_field_t * cbl_figconst_field_of( const char *value ); + // Necessary forward referencea struct cbl_label_t; struct cbl_refer_t; @@ -630,7 +815,7 @@ struct cbl_refer_t; struct cbl_span_t { cbl_refer_t *from, *len; - cbl_span_t( cbl_refer_t *from, cbl_refer_t *len = NULL ) + explicit cbl_span_t( cbl_refer_t *from, cbl_refer_t *len = NULL ) : from(from), len(len) {}; bool is_active() const { return !( from == NULL && len == NULL ); } @@ -644,50 +829,64 @@ struct cbl_refer_t { cbl_field_t *field; cbl_label_t *prog_func; bool all, addr_of; - uint32_t nsubscript; - cbl_refer_t *subscripts; // indices + std::vector<cbl_refer_t> subscripts; // indices cbl_span_t refmod; // substring bounds cbl_refer_t() - : field(NULL), prog_func(NULL) + : loc(), field(NULL), prog_func(NULL) , all(NULL), addr_of(false) - , nsubscript(0), subscripts(NULL), refmod(NULL) + , refmod(NULL) {} + // cppcheck-suppress noExplicitConstructor cbl_refer_t( cbl_field_t *field, bool all = false ) - : field(field), prog_func(NULL) + : loc(), field(field), prog_func(NULL) , all(all), addr_of(false) - , nsubscript(0), subscripts(NULL), refmod(NULL) + , refmod(NULL) {} cbl_refer_t( const YYLTYPE& loc, cbl_field_t *field, bool all = false ) : loc(loc), field(field), prog_func(NULL) , all(all), addr_of(false) - , nsubscript(0), subscripts(NULL), refmod(NULL) + , refmod(NULL) {} cbl_refer_t( cbl_field_t *field, cbl_span_t& refmod ) - : field(field), prog_func(NULL) + : loc(), field(field), prog_func(NULL) , all(false), addr_of(false) - , nsubscript(0), subscripts(NULL), refmod(refmod) + , refmod(refmod) {} cbl_refer_t( cbl_field_t *field, - size_t nsubscript, cbl_refer_t *subscripts, + const std::vector<cbl_refer_t>& subscripts, cbl_span_t refmod = cbl_span_t(NULL) ) - : field(field), prog_func(NULL) + : loc(), field(field), prog_func(NULL) , all(false), addr_of(false) - , nsubscript(nsubscript) , subscripts( new cbl_refer_t[nsubscript] ) + , subscripts(subscripts) , refmod(refmod) - { - std::copy(subscripts, subscripts + nsubscript, this->subscripts); - } + {} explicit cbl_refer_t( cbl_label_t *prog_func, bool addr_of = true ) - : field(NULL), prog_func(prog_func) + : loc(), field(NULL), prog_func(prog_func) , all(false), addr_of(addr_of) - , nsubscript(0), subscripts(NULL), refmod(cbl_span_t(NULL)) + , refmod(cbl_span_t(NULL)) {} + cbl_refer_t( const cbl_refer_t& that ) = default; + + cbl_refer_t& operator=( const cbl_refer_t& that ) { + loc = that.loc; + field = that.field; + prog_func = that.prog_func; + all = that.all; + addr_of = that.addr_of; + subscripts = that.subscripts; + refmod = that.refmod; + return *this; + } + + cbl_refer_t duplicate() const { - return cbl_refer_t( field, nsubscript, subscripts, refmod ); + return cbl_refer_t( field, subscripts, refmod ); } + uint32_t nsubscript() const { return subscripts.size(); } + static cbl_refer_t *empty(); cbl_refer_t * name( const char name[] ) { @@ -698,8 +897,8 @@ struct cbl_refer_t { } bool is_pointer() const { return addr_of || field->type == FldPointer; } - bool is_reference() const { return nsubscript > 0 || refmod.is_active(); } - bool is_table_reference() const { return nsubscript > 0; } + bool is_reference() const { return nsubscript() > 0 || refmod.is_active(); } + bool is_table_reference() const { return nsubscript() > 0; } bool is_refmod_reference() const { return refmod.is_active(); } size_t subscripts_set( const std::list<cbl_refer_t>& subs ); @@ -764,7 +963,7 @@ struct field_key_t { } }; -bool valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ); +bool valid_move( const cbl_field_t *tgt, const cbl_field_t *src ); #define record_area_name_stem "_ra_" @@ -774,9 +973,6 @@ is_record_area( const cbl_field_t *field ) { return 0 == memcmp(field->name, stem, sizeof(stem)-1); } -bool -is_register_field(cbl_field_t *field); - static inline bool is_constant( const cbl_field_t *field ) { return field->has_attr(constant_e); @@ -793,17 +989,20 @@ symbol_field_type_update( cbl_field_t *field, cbl_field_type_t type, bool is_usage ); struct sort_key_t; +struct sort_key_t; struct cbl_key_t { bool ascending; - size_t nfield; - cbl_field_t **fields; + std::vector<const cbl_field_t*> fields; - cbl_key_t() : ascending(false), nfield(0), fields(0) {} - cbl_key_t( size_t nfield, cbl_field_t **fields, bool ascending = true ) - : ascending(ascending), nfield(nfield), fields(fields) {} - cbl_key_t( const sort_key_t& src ); + cbl_key_t() : ascending(true) {} + explicit cbl_key_t( sort_key_t src ); explicit cbl_key_t( const cbl_occurs_key_t& that ); + cbl_key_t( size_t nfield, cbl_field_t **fields, bool ascending = true ) + : ascending(ascending) + , fields(fields, fields + nfield) + {} + cbl_key_t& operator=( const sort_key_t& that ); }; enum cbl_label_type_t { @@ -824,6 +1023,7 @@ enum cbl_label_type_t { LblString, LblArith, LblCompute, + LblXml, }; struct cbl_proc_addresses_t { @@ -900,8 +1100,12 @@ struct cbl_substitute_t { subst_fl_t first_last; cbl_refer_t orig, replacement; - cbl_substitute_t( bool anycase = false, char first_last = 0, - cbl_refer_t *orig = NULL, cbl_refer_t *replacement = NULL ) + cbl_substitute_t() + : anycase(false) + , first_last(subst_all_e) + {} + cbl_substitute_t( bool anycase, char first_last, + cbl_refer_t *orig, cbl_refer_t *replacement ) : anycase(anycase) , first_last(subst_fl_t(first_last)) , orig( orig? *orig : cbl_refer_t() ) @@ -934,7 +1138,10 @@ struct cbl_num_result_t { enum cbl_round_t rounded; struct cbl_refer_t refer; - static cbl_refer_t refer_of( const cbl_num_result_t& res ) { return res.refer; } + static const cbl_refer_t& + refer_of( const cbl_num_result_t& res ) { + return res.refer; + } }; void parser_symbol_add( struct cbl_field_t *new_var ); @@ -946,8 +1153,9 @@ struct cbl_ffi_arg_t { cbl_ffi_arg_attr_t attr; cbl_refer_t refer; // refer::field == NULL is OMITTED - cbl_ffi_arg_t( cbl_refer_t* refer = NULL, - cbl_ffi_arg_attr_t attr = none_of_e ); + cbl_ffi_arg_t(); + cbl_ffi_arg_t( cbl_refer_t* refer, + cbl_ffi_arg_attr_t attr ); cbl_ffi_arg_t( cbl_ffi_crv_t crv, cbl_refer_t* refer, cbl_ffi_arg_attr_t attr = none_of_e ); @@ -1035,11 +1243,24 @@ struct cbl_arith_error_t { cbl_label_addresses_t bottom; }; +struct cbl_delete_file_t { + cbl_label_addresses_t over; + cbl_label_addresses_t exception; + cbl_label_addresses_t no_exception; + cbl_label_addresses_t bottom; +}; + struct cbl_compute_error_t { // This is an int. The value is a cbl_compute_error_code_t tree compute_error_code; }; +struct cbl_xml_parse_t { + cbl_label_addresses_t over; + cbl_label_addresses_t exception; + cbl_label_addresses_t no_exception; +}; + struct cbl_label_t { enum cbl_label_type_t type; size_t parent; @@ -1073,6 +1294,13 @@ struct cbl_label_t { // for parser_op/parser_assign error tracking struct cbl_compute_error_t *compute_error; + + // for parse_xml processing: + struct cbl_xml_parse_t *xml_parse; + + // For parser_file_delete_file + struct cbl_delete_file_t *delete_file; + } structs; bool is_function() const { return type == LblFunction; } @@ -1091,6 +1319,7 @@ struct cbl_label_t { case LblString: return "LblString"; case LblArith: return "LblArith"; case LblCompute: return "LblCompute"; + case LblXml: return "LblXml"; } gcc_unreachable(); } @@ -1145,30 +1374,48 @@ struct label_cmp_lessthan { size_t field_index( const cbl_field_t *f ); -cbl_field_t * new_temporary( enum cbl_field_type_t type, const char initial[] = NULL ); +cbl_field_t * new_temporary( enum cbl_field_type_t type, + const char initial[] = NULL, + bool attr = false ); cbl_field_t * new_temporary_like( cbl_field_t skel ); cbl_field_t * new_temporary_clone( const cbl_field_t *orig); cbl_field_t * keep_temporary( cbl_field_type_t type ); cbl_field_t * new_literal( uint32_t len, const char initial[], - enum cbl_field_attr_t attr = none_e ); + cbl_field_attr_t attr, + cbl_encoding_t encoding = ASCII_e ); + +static inline cbl_field_t * +new_literal( uint32_t len, const char initial[] ) { + return new_literal(len, initial, none_e); +} void symbol_temporaries_free(); class temporaries_t { friend void symbol_temporaries_free(); struct literal_an { - bool is_quoted; + bool is_quoted, is_verbatim; // verbatim: don't use codeset std::string value; - literal_an( const char value[] = "???", bool is_quoted = false ) - : is_quoted(is_quoted), value(value) {} + literal_an() : is_quoted(false), is_verbatim(false), value("???") {} + literal_an( const char value[], bool is_quoted, bool is_verbatim = false ) + : is_quoted(is_quoted), is_verbatim(is_verbatim), value(value) {} + literal_an( const literal_an& that ) + : is_quoted(that.is_quoted), + is_verbatim(that.is_verbatim), + value(that.value) + {} literal_an& operator=( const literal_an& that ) { is_quoted = that.is_quoted; + is_verbatim = that.is_verbatim; value = that.value; return *this; } bool operator<( const literal_an& that ) const { if( value == that.value ) { // alpha before numeric + if( is_quoted == that.is_quoted ) { // verbatim before not + return (is_verbatim? 0 : 1) < (that.is_verbatim? 0 : 1); + } return (is_quoted? 0 : 1) < (that.is_quoted? 0 : 1); } return value < that.value; @@ -1181,9 +1428,10 @@ class temporaries_t { fieldmap_t used, freed; public: - cbl_field_t * literal( const char value[], uint32_t len, cbl_field_attr_t attr = none_e ); + cbl_field_t * literal( uint32_t len, const char value[], + cbl_field_attr_t attr, cbl_encoding_t encoding ); cbl_field_t * reuse( cbl_field_type_t type ); - cbl_field_t * acquire( cbl_field_type_t type ); + cbl_field_t * acquire( cbl_field_type_t type, const cbl_name_t name = nullptr ); cbl_field_t * add( cbl_field_t *field ); bool keep( cbl_field_t *field ) { return 1 == used[field->type].erase(field); } void dump() const; @@ -1264,7 +1512,8 @@ struct function_descr_t { static function_descr_t init( const char name[] ) { function_descr_t descr = {}; if( -1 == snprintf( descr.name, sizeof(descr.name), "%s", name ) ) { - dbgmsg("name truncated to '%s' (max %zu characters)", name); + dbgmsg("name truncated to '%s' (max " HOST_SIZE_T_PRINT_UNSIGNED + " characters)", name, (fmt_size_t)sizeof(descr.name)); } return descr; // truncation also reported elsewhere ? } @@ -1283,7 +1532,6 @@ struct function_descr_t { case FldForward: case FldIndex: case FldSwitch: - case FldBlob: return '?'; case FldPointer: return 'O'; @@ -1334,14 +1582,18 @@ struct cbl_section_t { } gcc_unreachable(); } - uint32_t attr() const { - switch(type) { - case file_sect_e: - case working_sect_e: return 0; - case linkage_sect_e: return linkage_e; - case local_sect_e: return local_e; - } - gcc_unreachable(); +}; + +struct cbl_locale_t { + cbl_name_t name; + cbl_encoding_t encoding; + cbl_name_t collation; + + explicit cbl_locale_t(const cbl_name_t name, + const char iconv_name[] = nullptr ); + + bool operator<( const cbl_locale_t& that ) const { + return strcmp(name, that.name) < 0; } }; @@ -1355,62 +1607,95 @@ struct cbl_special_name_t { char * hex_decode( const char text[] ); +/* + * An alphabet may just name an encoding, which implies binary collation. + * + * An alphabet may reference a Special-Names LOCALE, which defines an encoding + * and a collation (perhaps by default). + * + * During Special-Names parsing, an Alphabet may reference an as-yet undefined + * LOCALE with an as-yet unknown encoding. As a placeholder it inserts a named, + * undefined cbl_locale_t symbol, which the Alphabet references. If that + * locale is never defined, the encoding remains unknown, resulting in an error + * diagnostic at the end of Special-Names. + * + * For a custom alphabet of single-byte encoding, cbl_alphabet_t::collation_sequence + * holds the collation position of each encoded value. + * If 'A' sorts first (after LOW-VALUE), then collation_sequence['A'] == 1. + * If the encoding is ASCII, then 'A' is 65 and collation_sequence[ 65] == 1. + * If the encoding is EBCDIC CP1140, then 'A' is 193 and collation_sequence[193] == 1. + */ struct cbl_alphabet_t { YYLTYPE loc; cbl_name_t name; cbl_encoding_t encoding; - unsigned char low_index, high_index, last_index, alphabet[256]; + size_t locale; // index to cbl_locale_t symbol + unsigned char low_index, high_index, last_index, collation_sequence[256]; + unsigned char low_char, high_char; cbl_alphabet_t() : loc { 1,1, 1,1 } , encoding(ASCII_e) + , locale(0) , low_index(0) , high_index(255) , last_index(0) + , low_char(0) + , high_char(0) { memset(name, '\0', sizeof(name)); - memset(alphabet, 0xFF, sizeof(alphabet)); + memset(collation_sequence, 0xFF, sizeof(collation_sequence)); } cbl_alphabet_t(const YYLTYPE& loc, cbl_encoding_t enc) : loc(loc) , encoding(enc) + , locale(0) , low_index(0) , high_index(255) , last_index(0) + , low_char(0) + , high_char(0) { memset(name, '\0', sizeof(name)); - memset(alphabet, 0xFF, sizeof(alphabet)); + memset(collation_sequence, 0xFF, sizeof(collation_sequence)); } + cbl_alphabet_t(const YYLTYPE& loc, size_t locale, cbl_name_t name ); + cbl_alphabet_t( const YYLTYPE& loc, const cbl_name_t name, unsigned char low_index, unsigned char high_index, - unsigned char alphabet[] ) + unsigned char collation_sequence[] ) : loc(loc) , encoding(custom_encoding_e) + , locale(0) , low_index(low_index), high_index(high_index) , last_index(high_index) + , low_char(low_index) + , high_char(high_index) { assert(strlen(name) < sizeof(this->name)); strcpy(this->name, name); - std::copy(alphabet, alphabet + sizeof(this->alphabet), this->alphabet); + std::copy(collation_sequence, + collation_sequence + sizeof(this->collation_sequence), + this->collation_sequence); } unsigned char low_value() const { - return alphabet[low_index]; + return collation_sequence[low_index]; } unsigned char high_value() const { - return alphabet[high_index]; + return collation_sequence[high_index]; } void add_sequence( const YYLTYPE& loc, const unsigned char seq[] ) { if( low_index == 0 ) low_index = seq[0]; - unsigned char high_value = last_index > 0? alphabet[last_index] + 1 : 0; + unsigned char last = last_index > 0? collation_sequence[last_index] + 1 : 0; for( const unsigned char *p = seq; !end_of_string(p); p++ ) { - assign(loc, *p, high_value++); + assign(loc, *p, last++); } } @@ -1418,15 +1703,16 @@ struct cbl_alphabet_t { add_interval( const YYLTYPE& loc, unsigned char low, unsigned char high ) { if( low_index == 0 ) low_index = low; - unsigned char high_value = alphabet[last_index]; + unsigned char last = collation_sequence[last_index]; for( unsigned char ch = low; ch < high; ch++ ) { - assign(loc, ch, high_value++); + assign(loc, ch, last++); } } void also( const YYLTYPE& loc, size_t ch ); bool assign( const YYLTYPE& loc, unsigned char ch, unsigned char value ); + void reencode(); static const char * encoding_str( cbl_encoding_t encoding ) { @@ -1434,22 +1720,31 @@ struct cbl_alphabet_t { case ASCII_e: return "ascii"; case iso646_e: return "iso646"; case EBCDIC_e: return "ebcdic"; + case UTF8_e: return "utf8"; case custom_encoding_e: return "custom"; + default: + { + auto p = __gg__encoding_iconv_name( encoding ); + if( p ) return p; + } } return "???"; } void dump() const { - yywarn("'%s': %s, '%c' to '%c' (low 0x%02x, high 0x%02x)", - name, encoding_str(encoding), - low_index, last_index, low_index, high_index); + dbgmsg("%s: '%s', '%c' to '%c' (low 0x%x, high 0x%x)", + name, encoding_str(encoding), + low_index, last_index, low_index, high_index); if( encoding == custom_encoding_e ) { fprintf(stderr, "\t" " 0 1 2 3 4 5 6 7" " 8 9 A B C C E F"); unsigned int row = 0; - for( auto p = alphabet; p < alphabet + sizeof(alphabet); p++ ) { - if( (p - alphabet) % 16 == 0 ) fprintf(stderr, "\n%4X\t", row++); + for( auto p = collation_sequence; + p < collation_sequence + sizeof(collation_sequence); p++ ) { + if( (p - collation_sequence) % 16 == 0 ) { + fprintf(stderr, "\n%4X\t", row++); + } fprintf(stderr, "%3u ", *p); } fprintf(stderr, "\n"); @@ -1463,14 +1758,6 @@ struct cbl_alphabet_t { } }; -// a function pointer -typedef void ( *cbl_function_ptr ) ( void ); - -struct cbl_function_t { - char name[NAME_MAX]; - cbl_function_ptr func; -}; - static inline const char * file_org_str( enum cbl_file_org_t org ) { switch ( org ) { @@ -1509,9 +1796,19 @@ struct cbl_file_key_t { cbl_name_t name; size_t leftmost; // START or READ named leftmost field in key size_t nfield; - size_t *fields; + size_t *fields; // cppcheck-suppress unsafeClassCanLeak + + cbl_file_key_t() + : unique(true) + , leftmost(0) + , nfield(0) + , fields(nullptr) + { + memset(name, '\0', sizeof(name)); + } - cbl_file_key_t( size_t field = 0, bool unique = true ) + // Construct a key of length 1 having a single field. + explicit cbl_file_key_t( size_t field, bool unique = true ) : unique(unique) , leftmost(0) , nfield(1) @@ -1520,20 +1817,34 @@ struct cbl_file_key_t { fields[0] = field; memset(name, '\0', sizeof(name)); } - cbl_file_key_t( const cbl_file_key_t *that ) - : unique(that->unique) - , leftmost(that->leftmost) - , nfield(that->nfield) - { - memcpy(name, that->name, sizeof(name)); - fields = new size_t[nfield]; - std::copy( that->fields, that->fields + that->nfield, fields ); - } cbl_file_key_t( cbl_name_t name, const std::list<cbl_field_t *>& fields, bool is_unique ); + // The copy constructor and assignment operator exist to quell reports from + // cppcheck. When these objects are copied, the copy still points to the + // original data. + cbl_file_key_t( const cbl_file_key_t& that ) + : unique(that.unique) + , leftmost(that.leftmost) + , nfield(that.nfield) + // cppcheck-suppress copyCtorPointerCopying + , fields(that.fields) + { + strcpy(name, that.name); + } + ~cbl_file_key_t() {} + cbl_file_key_t& operator=( const cbl_file_key_t& that ) { + unique = that.unique; + leftmost = that.leftmost; + nfield = that.nfield; + // cppcheck-suppress copyCtorPointerCopying + fields = that.fields; + strcpy(name, that.name); + return *this; + } + uint32_t size(); void deforward( size_t ifile ); char * str() const; @@ -1547,12 +1858,12 @@ struct cbl_file_key_t { struct cbl_file_lock_t { bool multiple; enum lock_mode_t { unlocked_e, manual_e, record_e, automatic_e } mode; + cbl_file_lock_t() : multiple(false), mode(unlocked_e) {} bool mode_set( int token ); bool locked() const { return mode != unlocked_e; } }; struct cbl_file_t { - static cbl_file_key_t no_key; enum cbl_file_org_t org; enum file_entry_type_t entry_type; uint32_t attr; @@ -1573,21 +1884,45 @@ struct cbl_file_t { size_t user_status; // index into symbol table for file status size_t vsam_status; // index into symbol table for vsam status PIC X(6) size_t record_length; // DEPENDS ON + struct codeset_t { + cbl_encoding_t encoding; + size_t alphabet; // unlikely + explicit codeset_t(cbl_encoding_t encoding = CP1252_e, size_t alphabet = 0) + : encoding(encoding), alphabet(alphabet) + {} + } codeset; int line; cbl_name_t name; cbl_sortreturn_t *addresses; // Used during parser_return_start, et al. tree var_decl_node; // GENERIC tag for the run-time FIELD structure cbl_file_t() - : org(file_disorganized_e), - access(file_access_seq_e) + : org(file_disorganized_e) + , entry_type(fd_e) + , attr(0), reserve(0), same_record_as(0) + , padding('\0') + , optional(false) + , varying_size{ false, 0, 0 } + , access(file_access_seq_e) + , filename(0) + , default_record(0) + , nkey(0) + , keys(nullptr) + , password(0), user_status(0), vsam_status(0), record_length(0) + , line(0) + , addresses(nullptr) + , var_decl_node(nullptr) { - keys = &no_key; + memset(name, '\0', sizeof(name)); } bool varies() const { return varying_size.min != varying_size.max; } bool validate() const; void deforward(); + cbl_file_key_t * keys_update( cbl_file_key_t * keys ) { + if( this->keys ) delete[] this->keys; + return this->keys = keys; + } char * keys_str() const; int key_one( cbl_field_t *field ) const { auto ekey = keys + nkey, p = ekey; @@ -1626,20 +1961,18 @@ struct symbol_elem_t { size_t program; union symbol_elem_u { char *filename; - cbl_function_t function; cbl_field_t field; cbl_label_t label; cbl_special_name_t special; + cbl_locale_t locale; cbl_alphabet_t alphabet; cbl_file_t file; cbl_section_t section; - symbol_elem_u() { - static const cbl_field_t empty = {}; - field = empty; - } + symbol_elem_u() : field() {} } elem; - symbol_elem_t( symbol_type_t type = SymField, size_t program = 0 ) + symbol_elem_t() : type(SymField), program(0) {} + explicit symbol_elem_t( symbol_type_t type, size_t program = 0 ) : type(type), program(program) {} @@ -1680,9 +2013,6 @@ struct symbol_elem_t { case SymFilename: elem.filename = that.elem.filename; break; - case SymFunction: - elem.function = that.elem.function; - break; case SymField: elem.field = that.elem.field; break; @@ -1692,6 +2022,9 @@ struct symbol_elem_t { case SymSpecial: elem.special = that.elem.special; break; + case SymLocale: + elem.locale = that.elem.locale; + break; case SymAlphabet: elem.alphabet = that.elem.alphabet; break; @@ -1712,6 +2045,7 @@ static inline symbol_elem_t * symbol_elem_of( cbl_label_t *label ) { size_t n = offsetof(struct symbol_elem_t, elem.label); return + // cppcheck-suppress cstyleCast reinterpret_cast<struct symbol_elem_t *>((char*)label - n); } @@ -1719,6 +2053,7 @@ static inline const symbol_elem_t * symbol_elem_of( const cbl_label_t *label ) { size_t n = offsetof(symbol_elem_t, elem.label); return + // cppcheck-suppress cstyleCast reinterpret_cast<const symbol_elem_t *>((const char*)label - n); } @@ -1726,6 +2061,7 @@ static inline symbol_elem_t * symbol_elem_of( cbl_special_name_t *special ) { size_t n = offsetof(symbol_elem_t, elem.special); return + // cppcheck-suppress cstyleCast reinterpret_cast<symbol_elem_t *>((char*)special - n); } @@ -1733,19 +2069,30 @@ static inline symbol_elem_t * symbol_elem_of( cbl_alphabet_t *alphabet ) { size_t n = offsetof(symbol_elem_t, elem.alphabet); return + // cppcheck-suppress cstyleCast reinterpret_cast<symbol_elem_t *>((char*)alphabet - n); } +static inline const symbol_elem_t * +symbol_elem_of( const cbl_alphabet_t *alphabet ) { + size_t n = offsetof(symbol_elem_t, elem.alphabet); + return + // cppcheck-suppress cstyleCast + reinterpret_cast<const symbol_elem_t *>((const char*)alphabet - n); +} + static inline symbol_elem_t * symbol_elem_of( cbl_file_t *file ) { size_t n = offsetof(struct symbol_elem_t, elem.file); return + // cppcheck-suppress cstyleCast reinterpret_cast<struct symbol_elem_t *>((char*)file - n); } static inline const symbol_elem_t * symbol_elem_of( const cbl_file_t *file ) { size_t n = offsetof(symbol_elem_t, elem.file); return + // cppcheck-suppress cstyleCast reinterpret_cast<const symbol_elem_t *>((const char*)file - n); } @@ -1753,18 +2100,20 @@ static inline symbol_elem_t * symbol_elem_of( cbl_field_t *field ) { size_t n = offsetof(struct symbol_elem_t, elem.field); return + // cppcheck-suppress cstyleCast reinterpret_cast<struct symbol_elem_t *>((char*)field - n); } static inline const symbol_elem_t * symbol_elem_of( const cbl_field_t *field ) { size_t n = offsetof(symbol_elem_t, elem.field); return + // cppcheck-suppress cstyleCast reinterpret_cast<const symbol_elem_t *>((const char*)field - n); } symbol_elem_t * symbols_begin( size_t first = 0 ); symbol_elem_t * symbols_end(void); -cbl_field_t * symbol_redefines( const struct cbl_field_t *field ); +cbl_field_t * symbol_redefines( const cbl_field_t *field ); void build_symbol_map(); bool update_symbol_map( symbol_elem_t *e ); @@ -1780,7 +2129,7 @@ symbol_find( size_t program, std::list<const char *> names ); symbol_elem_t * symbol_find_of( size_t program, std::list<const char *> names, size_t group ); -struct cbl_field_t *symbol_find_odo( cbl_field_t * field ); +struct cbl_field_t *symbol_find_odo( const cbl_field_t * field ); size_t dimensions( const cbl_field_t *field ); const symbol_elem_t * symbol_field_current_record(); @@ -1802,63 +2151,79 @@ const cbl_label_t * symbol_program_local( const char called[] ); bool redefine_field( cbl_field_t *field ); -// Functions to correctly extract the underlying type. -static inline struct cbl_function_t * -cbl_function_of( struct symbol_elem_t *e ) { - assert(e->type == SymFunction); - return &e->elem.function; -} +const cbl_field_t * +symbol_unresolved_file_key( const cbl_file_t * file, + const cbl_name_t key_field_name ); -static inline struct cbl_section_t * -cbl_section_of( struct symbol_elem_t *e ) { - assert(e->type == SymDataSection); +static inline cbl_section_t * +cbl_section_of( symbol_elem_t *e ) { + assert(e && e->type == SymDataSection); return &e->elem.section; } -static inline struct cbl_field_t * -cbl_field_of( struct symbol_elem_t *e ) { - assert(e->type == SymField); +static inline cbl_field_t * +cbl_field_of( symbol_elem_t *e ) { + assert(e && e->type == SymField); return &e->elem.field; } -static inline const struct cbl_field_t * -cbl_field_of( const struct symbol_elem_t *e ) { - assert(e->type == SymField); +static inline const cbl_field_t * +cbl_field_of( const symbol_elem_t *e ) { + assert(e && e->type == SymField); return &e->elem.field; } -static inline struct cbl_label_t * -cbl_label_of( struct symbol_elem_t *e ) { - assert(e->type == SymLabel); +static inline cbl_label_t * +cbl_label_of( symbol_elem_t *e ) { + assert(e && e->type == SymLabel); return &e->elem.label; } -static inline const struct cbl_label_t * -cbl_label_of( const struct symbol_elem_t *e ) { - assert(e->type == SymLabel); +static inline const cbl_label_t * +cbl_label_of( const symbol_elem_t *e ) { + assert(e && e->type == SymLabel); return &e->elem.label; } -static inline struct cbl_special_name_t * -cbl_special_name_of( struct symbol_elem_t *e ) { - assert(e->type == SymSpecial); +static inline cbl_special_name_t * +cbl_special_name_of( symbol_elem_t *e ) { + assert(e && e->type == SymSpecial); return &e->elem.special; } -static inline struct cbl_alphabet_t * -cbl_alphabet_of( struct symbol_elem_t *e ) { - assert(e->type == SymAlphabet); +static inline cbl_locale_t * +cbl_locale_of( symbol_elem_t *e ) { + assert(e && e->type == SymLocale); + return &e->elem.locale; +} + +static inline const cbl_locale_t * +cbl_locale_of( const symbol_elem_t *e ) { + assert(e && e->type == SymLocale); + return &e->elem.locale; +} + +static inline cbl_alphabet_t * +cbl_alphabet_of( symbol_elem_t *e ) { + assert(e && e->type == SymAlphabet); + return &e->elem.alphabet; +} + +static inline const cbl_alphabet_t * +cbl_alphabet_of( const symbol_elem_t *e ) { + assert(e && e->type == SymAlphabet); return &e->elem.alphabet; } -static inline struct cbl_file_t * -cbl_file_of( struct symbol_elem_t *e ) { - assert(e->type == SymFile); + +static inline cbl_file_t * +cbl_file_of( symbol_elem_t *e ) { + assert(e && e->type == SymFile); return &e->elem.file; } -static inline const struct cbl_file_t * -cbl_file_of( const struct symbol_elem_t *e ) { - assert(e->type == SymFile); +static inline const cbl_file_t * +cbl_file_of( const symbol_elem_t *e ) { + assert(e && e->type == SymFile); return &e->elem.file; } @@ -1877,43 +2242,43 @@ is_procedure( const symbol_elem_t& e ) { } static inline bool -is_figconst(const struct cbl_field_t *field ) { - return ((field->attr & FIGCONST_MASK) != 0 ); +is_figconst(const cbl_field_t *field ) { + return (field->attr & FIGCONST_MASK) != 0; } static inline bool -is_figconst_low( const struct cbl_field_t *field ) { - return ((field->attr & FIGCONST_MASK) == low_value_e ); +is_figconst_low( const cbl_field_t *field ) { + return (field->attr & FIGCONST_MASK) == low_value_e; } static inline bool -is_figconst_zero( const struct cbl_field_t *field ) { - return ((field->attr & FIGCONST_MASK) == zero_value_e ); +is_figconst_zero( const cbl_field_t *field ) { + return (field->attr & FIGCONST_MASK) == zero_value_e; } static inline bool -is_figconst_space( const struct cbl_field_t *field ) { - return ((field->attr & FIGCONST_MASK) == space_value_e ); +is_figconst_space( const cbl_field_t *field ) { + return (field->attr & FIGCONST_MASK) == space_value_e; } static inline bool -is_figconst_quote( const struct cbl_field_t *field ) { - return ((field->attr & FIGCONST_MASK) == quote_value_e ); +is_figconst_quote( const cbl_field_t *field ) { + return (field->attr & FIGCONST_MASK) == quote_value_e; } static inline bool -is_figconst_high( const struct cbl_field_t *field ) { - return ((field->attr & FIGCONST_MASK) == high_value_e ); +is_figconst_high( const cbl_field_t *field ) { + return (field->attr & FIGCONST_MASK) == high_value_e; } static inline bool -is_space_value( const struct cbl_field_t *field ) { - return( (strcmp(field->name, "SPACE") == 0) - || (strcmp(field->name, "SPACES") == 0) ); +is_space_value( const cbl_field_t *field ) { + return (strcmp(field->name, "SPACE") == 0) + || (strcmp(field->name, "SPACES") == 0); } static inline bool -is_quoted( const struct cbl_field_t *field ) { +is_quoted( const cbl_field_t *field ) { return field->has_attr(quoted_e); } @@ -1933,7 +2298,7 @@ struct cbl_until_addresses_t { struct cbl_label_addresses_t test; // The test at the bottom of the body struct cbl_label_addresses_t testA; // Starting point of a TEST_AFTER loop struct cbl_label_addresses_t setup; // The actual entry point - size_t number_of_conditionals; + unsigned int number_of_conditionals; struct cbl_label_addresses_t condover[MAXIMUM_UNTILS]; // Jumping over the conditional struct cbl_label_addresses_t condinto[MAXIMUM_UNTILS]; // Jumping into the conditional struct cbl_label_addresses_t condback[MAXIMUM_UNTILS]; // Jumping back from the conditional @@ -1941,7 +2306,9 @@ struct cbl_until_addresses_t { }; size_t symbol_index(); // nth after first program symbol -size_t symbol_index( const struct symbol_elem_t *e ); +size_t symbol_index( const symbol_elem_t *e ); +size_t symbol_unique_index( const struct symbol_elem_t *e ); + struct symbol_elem_t * symbol_at( size_t index ); struct cbl_options_t { @@ -1993,17 +2360,20 @@ symbol_field_forward_add( size_t program, size_t parent, struct cbl_field_t * symbol_field_forward( size_t index ); struct cbl_prog_hier_t { - size_t nlabel; struct program_label_t { size_t ordinal; cbl_label_t label; - program_label_t() : ordinal(0) {} - program_label_t( const symbol_elem_t& e ) { + program_label_t() : ordinal(0), label() {} + // because std::copy_if: + // cppcheck-suppress noExplicitConstructor + program_label_t( const symbol_elem_t& e ) { + assert(is_program(e)); ordinal = symbol_index(&e); label = e.elem.label; } - } *labels; - + }; + std::vector<program_label_t> labels; + cbl_prog_hier_t(); }; @@ -2015,13 +2385,11 @@ struct cbl_prog_hier_t { struct cbl_perform_tgt_t { struct cbl_until_addresses_t addresses; - cbl_perform_tgt_t() : ifrom(0), ito(0) {} - cbl_perform_tgt_t( cbl_label_t * from, cbl_label_t *to = NULL ) - : ifrom( from? symbol_index(symbol_elem_of(from)) : 0 ) + cbl_perform_tgt_t() : addresses(), ifrom(0), ito(0) {} + explicit cbl_perform_tgt_t( cbl_label_t * from, cbl_label_t *to = NULL ) + : addresses(), ifrom( from? symbol_index(symbol_elem_of(from)) : 0 ) , ito( to? symbol_index(symbol_elem_of(to)) : 0 ) - { - addresses = {}; - } + {} cbl_label_t * from( cbl_label_t * label ) { ifrom = symbol_index(symbol_elem_of(label)); @@ -2040,11 +2408,12 @@ struct cbl_perform_tgt_t { void dump() const { assert(ifrom); if( !ito ) { - dbgmsg( "%s:%d: #%3zu %s", __PRETTY_FUNCTION__, __LINE__, - ifrom, from()->str() ); + dbgmsg( "%s:%d: #%3" GCC_PRISZ "u %s", __PRETTY_FUNCTION__, __LINE__, + (fmt_size_t)ifrom, from()->str() ); } else { - dbgmsg( "%s:%d: #%3zu %s THRU #%3zu %s", __PRETTY_FUNCTION__, __LINE__, - ifrom, from()->str(), ito, to()->str() ); + dbgmsg( "%s:%d: #%3" GCC_PRISZ "u %s THRU #%3" GCC_PRISZ "u %s", + __PRETTY_FUNCTION__, __LINE__, + (fmt_size_t)ifrom, from()->str(), (fmt_size_t)ito, to()->str() ); } } @@ -2058,10 +2427,11 @@ struct cbl_perform_vary_t { struct cbl_refer_t by; // numeric struct cbl_field_t *until; // FldConditional - cbl_perform_vary_t( const cbl_refer_t& varying = cbl_refer_t(), - const cbl_refer_t& from = cbl_refer_t(), - const cbl_refer_t& by = cbl_refer_t(), - cbl_field_t *until = NULL ) + cbl_perform_vary_t() : until(nullptr) {} + cbl_perform_vary_t( const cbl_refer_t& varying, + const cbl_refer_t& from, + const cbl_refer_t& by, + cbl_field_t *until ) : varying(varying) , from(from) , by(by) @@ -2083,12 +2453,12 @@ is_literal( const cbl_field_t *field ) { } static inline bool -is_signable( const struct cbl_field_t *field ) { +is_signable( const cbl_field_t *field ) { return field->attr & signable_e; } static inline bool -is_temporary( const struct cbl_field_t *field ) { +is_temporary( const cbl_field_t *field ) { return field->attr & intermediate_e; } @@ -2109,7 +2479,7 @@ is_numeric( const cbl_field_t *field ) { bool cobol_filename( const char *name ); const char * cobol_filename(); -const char * cobol_fileline_set( const char line[] ); +int cobol_fileline_set( const char line[] ); char *cobol_name_mangler(const char *cobol_name); @@ -2196,6 +2566,10 @@ class name_queue_t : private std::queue<cbl_namelocs_t> }; +const std::string& keyword_alias_add( const std::string& keyword, + const std::string& alias ); +int binary_integer_usage_of( const char name[] ); + void tee_up_empty(); void tee_up_name( const YYLTYPE& loc, const char name[] ); cbl_namelist_t teed_up_names(); @@ -2216,10 +2590,11 @@ struct symbol_elem_t * symbol_literalA( size_t program, const char name[] ); struct cbl_special_name_t * symbol_special( special_name_t id ); struct symbol_elem_t * symbol_special( size_t program, const char name[] ); +struct symbol_elem_t * symbol_locale( size_t program, const char name[] ); struct symbol_elem_t * symbol_alphabet( size_t program, const char name[] ); struct symbol_elem_t * symbol_file( size_t program, const char name[] ); -struct cbl_field_t * symbol_file_record( struct cbl_file_t *file ); +struct cbl_field_t * symbol_file_record( const cbl_file_t *file ); cbl_file_t::varying_t symbol_file_record_sizes( struct cbl_file_t *file ); struct cbl_section_t * symbol_section( size_t program, struct cbl_section_t *section ); @@ -2229,7 +2604,7 @@ size_t symbol_label_id( const cbl_label_t *label ); struct cbl_field_t * parent_of( const cbl_field_t *f ); const cbl_field_t * occurs_in( const cbl_field_t *f ); -cbl_field_t *rename_not_ok( cbl_field_t *first, cbl_field_t *last); +cbl_field_t *rename_not_ok( const cbl_field_t *first, const cbl_field_t *last); bool immediately_follows( const cbl_field_t *first ); bool is_variable_length( const cbl_field_t *field ); @@ -2237,12 +2612,12 @@ cbl_file_t * symbol_record_file( const cbl_field_t *f ); struct cbl_field_t * symbol_find_odo( const cbl_field_t * field ); -size_t numeric_group_attrs( const cbl_field_t *field ); +uint64_t numeric_group_attrs( const cbl_field_t *field ); static inline struct cbl_field_t * field_at( size_t index ) { struct symbol_elem_t *e = symbol_at(index); - assert(e->type == SymField); + assert(e && e->type == SymField); return &e->elem.field; } @@ -2254,21 +2629,22 @@ size_t symbols_update( size_t first, bool parsed_ok = true ); void symbol_table_init(void); void symbol_table_check(void); -struct symbol_elem_t * symbol_typedef_add( size_t program, - struct cbl_field_t *field ); -struct symbol_elem_t * symbol_field_add( size_t program, - struct cbl_field_t *field ); -struct cbl_label_t * symbol_label_add( size_t program, - struct cbl_label_t *label ); -struct cbl_label_t * symbol_program_add( size_t program, cbl_label_t *input ); -struct symbol_elem_t * symbol_special_add( size_t program, - struct cbl_special_name_t *special ); -struct symbol_elem_t * symbol_alphabet_add( size_t program, - struct cbl_alphabet_t *alphabet ); -struct symbol_elem_t * symbol_file_add( size_t program, - struct cbl_file_t *file ); -struct symbol_elem_t * symbol_section_add( size_t program, - struct cbl_section_t *section ); +symbol_elem_t * symbol_typedef_add( size_t program, + cbl_field_t *field ); +symbol_elem_t * symbol_field_add( size_t program, + cbl_field_t *field ); +cbl_label_t * symbol_label_add( size_t program, + cbl_label_t *label ); +cbl_label_t * symbol_program_add( size_t program, cbl_label_t *input ); +symbol_elem_t * symbol_special_add( size_t program, + cbl_special_name_t *special ); +symbol_elem_t * symbol_locale_add( size_t program, const cbl_locale_t *locale ); +symbol_elem_t * symbol_alphabet_add( size_t program, + const cbl_alphabet_t *alphabet ); +symbol_elem_t * symbol_file_add( size_t program, + cbl_file_t *file ); +symbol_elem_t * symbol_section_add( size_t program, + cbl_section_t *section ); void symbol_field_location( size_t ifield, const YYLTYPE& loc ); YYLTYPE symbol_field_location( size_t ifield ); @@ -2287,25 +2663,186 @@ static inline size_t upsi_register() { return symbol_index(symbol_field(0,0,"UPSI-0")); } -void wsclear( char ch); -const char *wsclear(); +void wsclear( uint32_t ch); +const uint32_t *wsclear(); enum cbl_call_convention_t { cbl_call_verbatim_e = 'V', cbl_call_cobol_e = 'N', // native }; +int keyword_tok( const char * text, bool include_intrinsics = false ); +int redefined_token( const cbl_name_t name ); + +class current_tokens_t { + class tokenset_t { + // token_names is initialized from a generated header file. + std::vector<const char *>token_names; // position indicates token value + std::map <std::string, int> tokens; // aliases + std::set<std::string> cobol_words; // Anything in COBOL-WORDS may appear only once. + public: + static std::string + lowercase( const cbl_name_t name ) { + cbl_name_t lname; + std::transform(name, name + strlen(name) + 1, lname, ftolower); + return lname; + } + static std::string + uppercase( const cbl_name_t name ) { + cbl_name_t uname; + std::transform(name, name + strlen(name) + 1, uname, ftoupper); + return uname; + } + + public: + tokenset_t(); + int find( const cbl_name_t name, bool include_intrinsics ); + + bool equate( const YYLTYPE& loc, int token, + const cbl_name_t name, const cbl_name_t verb = "EQUATE") { + auto lname( lowercase(name) ); + auto cw = cobol_words.insert(lname); + if( ! cw.second ) { + error_msg(loc, "COBOL-WORDS %s: %s may appear but once", verb, name); + return false; + } + auto p = tokens.find(lowercase(name)); + bool fOK = p == tokens.end(); + if( fOK ) { // name not already in use + tokens[lname] = token; + dbgmsg("%s:%d: %d has alias %s", __func__, __LINE__, token, name); + } else { + error_msg(loc, "%s: %s already defined as a token", verb, name); + } + return fOK; + } + bool undefine( const YYLTYPE& loc, + const cbl_name_t name, const cbl_name_t verb = "UNDEFINE" ) { + auto lname( lowercase(name) ); + auto cw = cobol_words.insert(lname); + if( ! cw.second ) { + error_msg(loc, "COBOL-WORDS %s: %s may appear but once", verb, name); + return false; + } + + // Do not erase generic, multi-type tokens COMPUTATIONAL and BINARY_INTEGER. + if( binary_integer_usage_of(name) ) { + dbgmsg("%s:%d: generic %s remains valid as a token", __func__, __LINE__, name); + return true; + } + + auto p = tokens.find(lname); + bool fOK = p != tokens.end(); + if( fOK ) { // name in use + tokens.erase(p); + } else { + error_msg(loc, "%s: %s not defined as a token", verb, name); + } + dbgmsg("%s:%d: %s removed as a valid token name", __func__, __LINE__, name); + return fOK; + } + + bool substitute( const YYLTYPE& loc, + const cbl_name_t extant, int token, const cbl_name_t name ) { + return + equate( loc, token, name, "SUBSTITUTE" ) + && + undefine( loc, extant, "SUBSTITUTE" ); + } + bool reserve( const YYLTYPE& loc, const cbl_name_t name ) { + auto lname( lowercase(name) ); + auto cw = cobol_words.insert(lname); + if( ! cw.second ) { + error_msg(loc, "COBOL-WORDS RESERVE: %s may appear but once", name); + return false; + } + tokens[lname] = -42; + return true; + } + int redefined_as( const cbl_name_t name ) { + auto lname( lowercase(name) ); + if( cobol_words.find(lname) != cobol_words.end() ) { + auto p = tokens.find(lname); + if( p != tokens.end() ) { + return p->second; + } + } + return 0; + } + const char * name_of( int tok ) const { + tok -= (255 + 3); + gcc_assert(0 <= tok && size_t(tok) < token_names.size()); + return tok < 0? "???" : token_names[tok]; + } + }; + + tokenset_t tokens; + public: + current_tokens_t() {} + int find( const cbl_name_t name, bool include_intrinsics ) { + return tokens.find(name, include_intrinsics); + } + bool equate( const YYLTYPE& loc, const cbl_name_t keyword, const cbl_name_t alias ) { + int token; + if( 0 == (token = binary_integer_usage_of(keyword)) ) { + if( 0 == (token = keyword_tok(keyword)) ) { + error_msg(loc, "EQUATE %s: not a valid token", keyword); + return false; + } + } + auto name = keyword_alias_add(tokens.uppercase(keyword), + tokens.uppercase(alias)); + if( name != keyword ) { + error_msg(loc, "EQUATE: %s is already an alias for %s", alias, name.c_str()); + return false; + } + return tokens.equate(loc, token, alias); + } + bool undefine( const YYLTYPE& loc, cbl_name_t keyword ) { + return tokens.undefine(loc, keyword); + } + bool substitute( const YYLTYPE& loc, const cbl_name_t keyword, const cbl_name_t alias ) { + int token; + if( 0 == (token = binary_integer_usage_of(keyword)) ) { + if( 0 == (token = keyword_tok(keyword)) ) { + error_msg(loc, "SUBSTITUTE %s: not a valid token", keyword); + return false; + } + } + auto name = keyword_alias_add(tokens.uppercase(keyword), + tokens.uppercase(alias)); + if( name != keyword ) { + error_msg(loc, "SUBSTITUTE: %s is already an alias for %s", alias, name.c_str()); + return false; + } + + dbgmsg("%s:%d: %s (%d) will have alias %s", __func__, __LINE__, keyword, token, alias); + return tokens.substitute(loc, keyword, token, alias); + } + bool reserve( const YYLTYPE& loc, const cbl_name_t name ) { + return tokens.reserve(loc, name); + } + int redefined_as( const cbl_name_t name ) { + return tokens.redefined_as(name); + } + const char * name_of( int tok ) const { + return tokens.name_of(tok); + } +}; + cbl_call_convention_t current_call_convention(); +current_tokens_t& cdf_current_tokens(); -cbl_call_convention_t +void current_call_convention( cbl_call_convention_t convention); class procref_base_t { private: const char *section_name, *paragraph_name; public: - procref_base_t( const char *section_name = NULL, - const char *paragraph_name = NULL ) + procref_base_t() : section_name(nullptr) , paragraph_name(nullptr) {} + procref_base_t( const char *section_name, + const char *paragraph_name ) : section_name(section_name) , paragraph_name(paragraph_name) {} @@ -2340,9 +2877,6 @@ public: int line_number() const { return line; } }; -int keyword_tok( const char * text, bool include_intrinsics = false ); -int redefined_token( const cbl_name_t name ); - void procedure_definition_add( size_t program, const cbl_label_t *procedure ); void procedure_reference_add( const char *sect, const char *para, int line, size_t context ); @@ -2358,12 +2892,8 @@ symbol_field_same_as( cbl_field_t *tgt, const cbl_field_t *src ); size_t symbol_file_same_record_area( std::list<cbl_file_t*>& files ); -cbl_field_t * -symbol_valid_udf_args( size_t function, - std::list<cbl_refer_t> args = std::list<cbl_refer_t>() ); - bool symbol_currency_add( const char symbol[], const char sign[] = NULL ); -const char * symbol_currency( char symbol ); +const char * symbol_currency( char symbol = '\0' ); const char * symbol_type_str( enum symbol_type_t type ); const char * cbl_field_type_str( enum cbl_field_type_t type ); @@ -2376,7 +2906,7 @@ refer_type_str( const cbl_refer_t *r ) { enum cbl_field_type_t symbol_field_type( size_t program, const char name[] ); -struct symbol_elem_t * symbol_parent( const struct symbol_elem_t *e ); +struct symbol_elem_t * symbol_parent( const symbol_elem_t *e ); int length_of_picture(const char *picture); int rdigits_of_picture(const char *picture); @@ -2391,4 +2921,6 @@ void gcc_location_set( const LOC& loc ); // create an entire .h module. So, I stuck it here. size_t count_characters(const char *in, size_t length); +void current_enabled_ecs( tree ena ); + #endif diff --git a/gcc/cobol/symfind.cc b/gcc/cobol/symfind.cc index 38a8900..472d37a 100644 --- a/gcc/cobol/symfind.cc +++ b/gcc/cobol/symfind.cc @@ -31,6 +31,9 @@ #include "cobol-system.h" +#include "coretypes.h" +#include "tree.h" + #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "util.h" @@ -45,7 +48,7 @@ extern int yydebug; static bool is_data_field( symbol_elem_t& e ) { if( e.type != SymField ) return false; - auto f = cbl_field_of(&e); + const cbl_field_t *f = cbl_field_of(&e); if( f->name[0] == '\0' ) return false; if( is_filler(f) ) return false; @@ -125,11 +128,10 @@ finalize_symbol_map2() { for( auto& elem : symbol_map2 ) { auto& fields( elem.second ); - std::remove_if( fields.begin(), fields.end(), - []( auto isym ) { - auto f = cbl_field_of(symbol_at(isym)); - return f->type == FldInvalid; - } ); + fields.remove_if( []( auto isym ) { + const cbl_field_t *f = cbl_field_of(symbol_at(isym)); + return f->type == FldInvalid; + } ); if( fields.empty() ) empties.insert(elem.first); } @@ -145,13 +147,14 @@ dump_symbol_map2( const field_key_t& key, const std::list<size_t>& candidates ) for( auto candidate : candidates ) { char *tmp = fields; - fields = xasprintf("%s%s %3zu", tmp? tmp : "", sep, candidate); + fields = xasprintf("%s%s %3" GCC_PRISZ "u", + tmp? tmp : "", sep, (fmt_size_t)candidate); sep[0] = ','; free(tmp); } - dbgmsg( "%s:%d: %3zu %s {%s}", __func__, __LINE__, - key.program, key.name, fields ); + dbgmsg( "%s:%d: %3" GCC_PRISZ "u %s {%s}", __func__, __LINE__, + (fmt_size_t)key.program, key.name, fields ); free(fields); } @@ -177,7 +180,8 @@ dump_symbol_map_value( const char name[], const symbol_map_t::value_type& value for( ; p != value.second.end(); p++ ) { char *tmp = ancestry; - ancestry = xasprintf("%s%s %3zu", tmp? tmp : "", sep, *p); + ancestry = xasprintf("%s%s %3" GCC_PRISZ "u", + tmp? tmp : "", sep, (fmt_size_t)*p); sep[0] = ','; free(tmp); } @@ -198,15 +202,10 @@ field_structure( symbol_elem_t& sym ) { static const symbol_map_t::value_type none( symbol_map_t::key_type( 0, "", 0 ), std::vector<size_t>() ); - if( getenv(__func__) && sym.type == SymField ) { - const auto& field = *cbl_field_of(&sym); - dbgmsg("%s: #%zu %s: '%s' is_data_field: %s", __func__, - symbol_index(&sym), cbl_field_type_str(field.type), field.name, - is_data_field(sym)? "yes" : "no" ); - } if( !is_data_field(sym) ) return none; cbl_field_t *field = cbl_field_of(&sym); + assert(field->type != FldForward); // eliminated by is_data_field symbol_map_t::key_type key( sym.program, field->name, field->parent ); symbol_map_t::value_type elem( key, std::vector<size_t>() ); @@ -231,25 +230,9 @@ field_structure( symbol_elem_t& sym ) { } } - if( getenv(__func__) && yydebug ) { - dbgmsg( "%s:%d: '%s' has %zu ancestors", __func__, __LINE__, - elem.first.c_str(), elem.second.size() ); - dump_symbol_map_value(__func__, elem); - } - return elem; } -void erase_symbol_map_fwds( size_t beg ) { - for( auto p = symbols_begin(beg); p < symbols_end(); p++ ) { - if( p->type != SymField ) continue; - const auto& field(*cbl_field_of(p)); - if( field.type == FldForward ) { - symbol_map.erase( sym_name_t(p->program, field.name, field.parent) ); - } - } -} - void build_symbol_map() { static size_t beg = 0; @@ -266,14 +249,11 @@ build_symbol_map() { symbol_map.erase(sym_name_t("")); if( yydebug ) { - dbgmsg( "%s:%d: %zu of %zu symbols inserted into %zu in symbol_map", - __func__, __LINE__, nsym, end, symbol_map.size() ); - - if( getenv(__func__) ) { - for( const auto& elem : symbol_map ) { - dump_symbol_map_value1(elem); - } - } + dbgmsg( "%s:%d: " HOST_SIZE_T_PRINT_UNSIGNED " of " + HOST_SIZE_T_PRINT_UNSIGNED " symbols inserted into " + HOST_SIZE_T_PRINT_UNSIGNED " in symbol_map", + __func__, __LINE__, (fmt_size_t)nsym, (fmt_size_t)end, + (fmt_size_t)symbol_map.size() ); } } @@ -286,18 +266,15 @@ update_symbol_map( symbol_elem_t *e ) { class is_name { const char *name; public: - is_name( const char *name ) : name(name) {} - bool operator()( symbol_map_t::value_type& elem ) { + explicit is_name( const char *name ) : name(name) {} + bool operator()( const symbol_map_t::value_type& elem ) { const bool tf = elem.first == name; - if( tf && getenv("is_name") ) { - dump_key( "matched", elem.first ); - } return tf; } protected: void dump_key( const char tag[], const symbol_map_t::key_type& key ) const { - dbgmsg( "symbol_map key: %s { %3zu %3zu %s }", - tag, key.program, key.parent, key.name ); + dbgmsg( "symbol_map key: %s { %3" GCC_PRISZ "u %3" GCC_PRISZ "u %s }", + tag, (fmt_size_t)key.program, (fmt_size_t)key.parent, key.name ); } }; @@ -312,7 +289,7 @@ class reduce_ancestry { static symbol_map_t::mapped_type candidates_only( const symbol_map_t::value_type& elem ) { return elem.second; } public: - reduce_ancestry( const symbol_map_t& groups ) + explicit reduce_ancestry( const symbol_map_t& groups ) : candidates( groups.size() ) { std::transform( groups.begin(), groups.end(), candidates.begin(), @@ -330,9 +307,9 @@ public: if( p != item.second.end() ) { // Preserve symbol's index at front of ancestor list. symbol_map_t::mapped_type shorter(1 + ancestors->size()); - auto p = shorter.begin(); - *p = item.second.front(); - shorter.insert( ++p, ancestors->begin(), ancestors->end() ); + auto p_l = shorter.begin(); + *p_l = item.second.front(); + shorter.insert( ++p_l, ancestors->begin(), ancestors->end() ); return make_pair(item.first, shorter); } } @@ -345,7 +322,7 @@ public: class different_program { size_t program; public: - different_program( size_t program ) : program(program) {} + explicit different_program( size_t program ) : program(program) {} bool operator()( const symbol_map_t::value_type& item ) const { return ! item.first.same_program(program); } @@ -355,16 +332,16 @@ class in_scope { size_t program; static size_t prog_of( size_t program ) { - auto L = cbl_label_of(symbol_at(program)); + const cbl_label_t *L = cbl_label_of(symbol_at(program)); return L->parent; } public: - in_scope( size_t program ) : program(program) {} + explicit in_scope( size_t program ) : program(program) {} // A symbol is in scope if it's defined by this program or by an ancestor. bool operator()( const symbol_map_t::value_type& item ) const { - symbol_elem_t *e = symbol_at(item.second.front()); + const symbol_elem_t *e = symbol_at(item.second.front()); for( size_t prog = this->program; prog != 0; prog = prog_of(prog) ) { if( e->program == prog ) return true; } @@ -435,7 +412,7 @@ size_t end_of_group( size_t igroup ); static std::vector<size_t> symbol_match2( size_t program, - std::list<const char *> names, bool local = true ) + const std::list<const char *>& names, bool local = true ) { std::vector<size_t> fields; @@ -444,7 +421,7 @@ symbol_match2( size_t program, auto plist = symbol_map2.find(key); if( plist != symbol_map2.end() ) { for( auto candidate : plist->second ) { - auto e = symbol_at(candidate); + const symbol_elem_t *e = symbol_at(candidate); if( name_has_names( e, names, local ) ) { fields.push_back( symbol_index(e) ); } @@ -478,14 +455,16 @@ symbol_match2( size_t program, sep = ""; for( auto field : fields ) { char *partial = fieldstr; - int asret = asprintf(&fieldstr, "%s%s%zu", partial? partial : "", sep, field); + int asret = asprintf(&fieldstr, "%s%s" HOST_SIZE_T_PRINT_UNSIGNED, + partial? partial : "", sep, (fmt_size_t)field); assert(asret); sep = ", "; assert(fieldstr); free(partial); } - dbgmsg("%s: '%s' matches %zu fields: {%s}", __func__, ancestry, fields.size(), fieldstr); + dbgmsg("%s: '%s' matches " HOST_SIZE_T_PRINT_UNSIGNED " fields: {%s}", + __func__, ancestry, (fmt_size_t)fields.size(), fieldstr); free(fieldstr); } free(ancestry); @@ -500,7 +479,7 @@ symbol_match2( size_t program, * N-1. */ static symbol_map_t -symbol_match( size_t program, std::list<const char *> names ) { +symbol_match( size_t program, const std::list<const char *>& names ) { auto matched = symbol_match2( program, names ); symbol_map_t output; @@ -516,7 +495,7 @@ symbol_match( size_t program, std::list<const char *> names ) { } auto inserted = output.insert(*p); if( ! inserted.second ) { - yyerror("%s is not a unique reference", key.name); + error_msg_direct("%s is not a unique reference", key.name); } } return output; @@ -551,13 +530,13 @@ symbol_find( size_t program, std::list<const char *> names ) { auto unique = items.size() == 1; - if( !unique ) { + if( ! unique ) { if( items.empty() ) { return std::pair<symbol_elem_t *, bool>(NULL, false); } if( yydebug ) { - dbgmsg( "%s:%d: '%s' has %zu possible matches", - __func__, __LINE__, names.back(), items.size() ); + dbgmsg( "%s:%d: '%s' has " HOST_SIZE_T_PRINT_UNSIGNED " possible matches", + __func__, __LINE__, names.back(), (fmt_size_t)items.size() ); std::for_each( items.begin(), items.end(), dump_symbol_map_value1 ); } } @@ -573,7 +552,7 @@ symbol_find( size_t program, std::list<const char *> names ) { class in_group { size_t group; public: - in_group( size_t group ) : group(group) {} + explicit in_group( size_t group ) : group(group) {} bool operator()( symbol_map_t::const_reference elem ) const { return 0 < std::count( elem.second.begin(), @@ -585,12 +564,6 @@ symbol_elem_t * symbol_find_of( size_t program, std::list<const char *> names, size_t group ) { symbol_map_t input = symbol_match(program, names); - if( getenv(__func__) && input.size() != 1 ) { - dbgmsg( "%s:%d: '%s' has %zu candidates for group %zu", - __func__, __LINE__, names.back(), input.size(), group ); - std::for_each( input.begin(), input.end(), dump_symbol_map_value1 ); - } - symbol_map_t items; std::copy_if( input.begin(), input.end(), std::inserter(items, items.begin()), in_group(group) ); @@ -602,8 +575,8 @@ symbol_find_of( size_t program, std::list<const char *> names, size_t group ) { } if( yydebug ) { - dbgmsg( "%s:%d: '%s' has %zu possible matches", - __func__, __LINE__, names.back(), input.size() ); + dbgmsg( "%s:%d: '%s' has " HOST_SIZE_T_PRINT_UNSIGNED " possible matches", + __func__, __LINE__, names.back(), (fmt_size_t)input.size() ); std::for_each( input.begin(), input.end(), dump_symbol_map_value1 ); } diff --git a/gcc/cobol/token_names.h b/gcc/cobol/token_names.h index 26dabc8..8ce6472 100644 --- a/gcc/cobol/token_names.h +++ b/gcc/cobol/token_names.h @@ -1,1373 +1,1412 @@ -// generated by ./token_names.h.gen ../../build/gcc/cobol/parse.h -// Fri Jan 31 05:52:10 EST 2025 +// generated by /home/jklowden/projects/3rd/gcc/parser/gcc/cobol/token_names.h.gen cobol/parse.h +// Wed Nov 26 11:57:23 EST 2025 tokens = { - { "identification", IDENTIFICATION_DIV }, // 258 - { "environment", ENVIRONMENT_DIV }, // 259 - { "procedure", PROCEDURE_DIV }, // 260 - { "data", DATA_DIV }, // 261 - { "file", FILE_SECT }, // 262 - { "input-output", INPUT_OUTPUT_SECT }, // 263 - { "linkage", LINKAGE_SECT }, // 264 - { "local-storage", LOCAL_STORAGE_SECT }, // 265 - { "working-storage", WORKING_STORAGE_SECT }, // 266 - { "object-computer", OBJECT_COMPUTER }, // 267 - { "display-of", DISPLAY_OF }, // 268 - { "end-function", END_FUNCTION }, // 269 - { "end-program", END_PROGRAM }, // 270 - { "end-subprogram", END_SUBPROGRAM }, // 271 - { "justified", JUSTIFIED }, // 272 - { "returning", RETURNING }, // 273 - { "no-condition", NO_CONDITION }, // 274 - { "alnum", ALNUM }, // 275 - { "alphed", ALPHED }, // 276 - { "error", ERROR }, // 277 - { "exception", EXCEPTION }, // 278 - { "size-error", SIZE_ERROR }, // 279 - { "exception-name", EXCEPTION_NAME }, // 280 - { "level", LEVEL }, // 281 - { "level66", LEVEL66 }, // 282 - { "level78", LEVEL78 }, // 283 - { "level88", LEVEL88 }, // 284 - { "class-name", CLASS_NAME }, // 285 - { "name", NAME }, // 286 - { "name88", NAME88 }, // 287 - { "nume", NUME }, // 288 - { "numed", NUMED }, // 289 - { "numed-cr", NUMED_CR }, // 290 - { "numed-db", NUMED_DB }, // 291 - { "ninedot", NINEDOT }, // 292 - { "nines", NINES }, // 293 - { "ninev", NINEV }, // 294 - { "pic-p", PIC_P }, // 295 - { "spaces", SPACES }, // 296 - { "space", SPACES }, // 296 - { "literal", LITERAL }, // 297 - { "end", END }, // 298 - { "eop", EOP }, // 299 - { "filename", FILENAME }, // 300 - { "invalid", INVALID }, // 301 - { "number", NUMBER }, // 302 - { "negative", NEGATIVE }, // 303 - { "numstr", NUMSTR }, // 304 - { "overflow", OVERFLOW }, // 305 - { "computational", COMPUTATIONAL }, // 306 - { "perform", PERFORM }, // 307 - { "backward", BACKWARD }, // 308 - { "positive", POSITIVE }, // 309 - { "pointer", POINTER }, // 310 - { "section", SECTION }, // 311 - { "standard-alphabet", STANDARD_ALPHABET }, // 312 - { "switch", SWITCH }, // 313 - { "upsi", UPSI }, // 314 - { "zero", ZERO }, // 315 - { "zeros", ZERO }, // 315 - { "zeroes", ZERO }, // 315 - { "sysin", SYSIN }, // 316 - { "sysipt", SYSIPT }, // 317 - { "sysout", SYSOUT }, // 318 - { "syslist", SYSLIST }, // 319 - { "syslst", SYSLST }, // 320 - { "syspunch", SYSPUNCH }, // 321 - { "syspch", SYSPCH }, // 322 - { "console", CONSOLE }, // 323 - { "c01", C01 }, // 324 - { "c02", C02 }, // 325 - { "c03", C03 }, // 326 - { "c04", C04 }, // 327 - { "c05", C05 }, // 328 - { "c06", C06 }, // 329 - { "c07", C07 }, // 330 - { "c08", C08 }, // 331 - { "c09", C09 }, // 332 - { "c10", C10 }, // 333 - { "c11", C11 }, // 334 - { "c12", C12 }, // 335 - { "csp", CSP }, // 336 - { "s01", S01 }, // 337 - { "s02", S02 }, // 338 - { "s03", S03 }, // 339 - { "s04", S04 }, // 340 - { "s05", S05 }, // 341 - { "afp-5a", AFP_5A }, // 342 - { "stdin", STDIN }, // 343 - { "stdout", STDOUT }, // 344 - { "stderr", STDERR }, // 345 - { "list", LIST }, // 346 - { "map", MAP }, // 347 - { "nolist", NOLIST }, // 348 - { "nomap", NOMAP }, // 349 - { "nosource", NOSOURCE }, // 350 - { "might-be", MIGHT_BE }, // 351 - { "function-udf", FUNCTION_UDF }, // 352 - { "function-udf-0", FUNCTION_UDF_0 }, // 353 - { "date-fmt", DATE_FMT }, // 354 - { "time-fmt", TIME_FMT }, // 355 - { "datetime-fmt", DATETIME_FMT }, // 356 - { "basis", BASIS }, // 357 - { "cbl", CBL }, // 358 - { "constant", CONSTANT }, // 359 - { "copy", COPY }, // 360 - { "defined", DEFINED }, // 361 - { "enter", ENTER }, // 362 - { "feature", FEATURE }, // 363 - { "insertt", INSERTT }, // 364 - { "lsub", LSUB }, // 365 - { "parameter", PARAMETER_kw }, // 366 - { "override", OVERRIDE }, // 367 - { "ready", READY }, // 368 - { "reset", RESET }, // 369 - { "rsub", RSUB }, // 370 - { "service-reload", SERVICE_RELOAD }, // 371 - { "star-cbl", STAR_CBL }, // 372 - { "subscript", SUBSCRIPT }, // 373 - { "suppress", SUPPRESS }, // 374 - { "title", TITLE }, // 375 - { "trace", TRACE }, // 376 - { "use", USE }, // 377 - { "cobol-words", COBOL_WORDS }, // 378 - { "equate", EQUATE }, // 379 - { "undefine", UNDEFINE }, // 380 - { "cdf-define", CDF_DEFINE }, // 381 - { "cdf-display", CDF_DISPLAY }, // 382 - { "cdf-if", CDF_IF }, // 383 - { "cdf-else", CDF_ELSE }, // 384 - { "cdf-end-if", CDF_END_IF }, // 385 - { "cdf-evaluate", CDF_EVALUATE }, // 386 - { "cdf-when", CDF_WHEN }, // 387 - { "cdf-end-evaluate", CDF_END_EVALUATE }, // 388 - { "call-cobol", CALL_COBOL }, // 389 - { "call-verbatim", CALL_VERBATIM }, // 390 - { "if", IF }, // 391 - { "then", THEN }, // 392 - { "else", ELSE }, // 393 - { "sentence", SENTENCE }, // 394 - { "accept", ACCEPT }, // 395 - { "add", ADD }, // 396 - { "alter", ALTER }, // 397 - { "call", CALL }, // 398 - { "cancel", CANCEL }, // 399 - { "close", CLOSE }, // 400 - { "compute", COMPUTE }, // 401 - { "continue", CONTINUE }, // 402 - { "delete", DELETE }, // 403 - { "display", DISPLAY }, // 404 - { "divide", DIVIDE }, // 405 - { "evaluate", EVALUATE }, // 406 - { "exit", EXIT }, // 407 - { "filler", FILLER_kw }, // 408 - { "goback", GOBACK }, // 409 - { "goto", GOTO }, // 410 - { "initialize", INITIALIZE }, // 411 - { "inspect", INSPECT }, // 412 - { "merge", MERGE }, // 413 - { "move", MOVE }, // 414 - { "multiply", MULTIPLY }, // 415 - { "open", OPEN }, // 416 - { "paragraph", PARAGRAPH }, // 417 - { "read", READ }, // 418 - { "release", RELEASE }, // 419 - { "return", RETURN }, // 420 - { "rewrite", REWRITE }, // 421 - { "search", SEARCH }, // 422 - { "set", SET }, // 423 - { "select", SELECT }, // 424 - { "sort", SORT }, // 425 - { "sort-merge", SORT_MERGE }, // 426 - { "string", STRING_kw }, // 427 - { "stop", STOP }, // 428 - { "subtract", SUBTRACT }, // 429 - { "start", START }, // 430 - { "unstring", UNSTRING }, // 431 - { "write", WRITE }, // 432 - { "when", WHEN }, // 433 - { "abs", ABS }, // 434 - { "access", ACCESS }, // 435 - { "acos", ACOS }, // 436 - { "actual", ACTUAL }, // 437 - { "advancing", ADVANCING }, // 438 - { "after", AFTER }, // 439 - { "all", ALL }, // 440 - { "allocate", ALLOCATE }, // 441 - { "alphabet", ALPHABET }, // 442 - { "alphabetic", ALPHABETIC }, // 443 - { "alphabetic-lower", ALPHABETIC_LOWER }, // 444 - { "alphabetic-upper", ALPHABETIC_UPPER }, // 445 - { "alphanumeric", ALPHANUMERIC }, // 446 - { "alphanumeric-edited", ALPHANUMERIC_EDITED }, // 447 - { "also", ALSO }, // 448 - { "alternate", ALTERNATE }, // 449 - { "annuity", ANNUITY }, // 450 - { "anum", ANUM }, // 451 - { "any", ANY }, // 452 - { "anycase", ANYCASE }, // 453 - { "apply", APPLY }, // 454 - { "are", ARE }, // 455 - { "area", AREA }, // 456 - { "areas", AREAS }, // 457 - { "as", AS }, // 458 - { "ascending", ASCENDING }, // 459 - { "activating", ACTIVATING }, // 460 - { "asin", ASIN }, // 461 - { "assign", ASSIGN }, // 462 - { "at", AT }, // 463 - { "atan", ATAN }, // 464 - { "based", BASED }, // 465 - { "baseconvert", BASECONVERT }, // 466 - { "before", BEFORE }, // 467 - { "binary", BINARY }, // 468 - { "bit", BIT }, // 469 - { "bit-of", BIT_OF }, // 470 - { "bit-to-char", BIT_TO_CHAR }, // 471 - { "blank", BLANK }, // 472 - { "block", BLOCK }, // 473 - { "boolean-of-integer", BOOLEAN_OF_INTEGER }, // 474 - { "bottom", BOTTOM }, // 475 - { "by", BY }, // 476 - { "byte", BYTE }, // 477 - { "byte-length", BYTE_LENGTH }, // 478 - { "cf", CF }, // 479 - { "ch", CH }, // 480 - { "changed", CHANGED }, // 481 - { "char", CHAR }, // 482 - { "char-national", CHAR_NATIONAL }, // 483 - { "character", CHARACTER }, // 484 - { "characters", CHARACTERS }, // 485 - { "checking", CHECKING }, // 486 - { "class", CLASS }, // 487 - { "cobol", COBOL }, // 488 - { "code", CODE }, // 489 - { "code-set", CODESET }, // 490 - { "collating", COLLATING }, // 491 - { "column", COLUMN }, // 492 - { "combined-datetime", COMBINED_DATETIME }, // 493 - { "comma", COMMA }, // 494 - { "command-line", COMMAND_LINE }, // 495 - { "command-line-count", COMMAND_LINE_COUNT }, // 496 - { "commit", COMMIT }, // 497 - { "common", COMMON }, // 498 - { "concat", CONCAT }, // 499 - { "condition", CONDITION }, // 500 - { "configuration", CONFIGURATION_SECT }, // 501 - { "contains", CONTAINS }, // 502 - { "content", CONTENT }, // 503 - { "control", CONTROL }, // 504 - { "controls", CONTROLS }, // 505 - { "convert", CONVERT }, // 506 - { "converting", CONVERTING }, // 507 - { "corresponding", CORRESPONDING }, // 508 - { "cos", COS }, // 509 - { "count", COUNT }, // 510 - { "currency", CURRENCY }, // 511 - { "current", CURRENT }, // 512 - { "current-date", CURRENT_DATE }, // 513 - { "data", DATA }, // 514 - { "date", DATE }, // 515 - { "date-compiled", DATE_COMPILED }, // 516 - { "date-of-integer", DATE_OF_INTEGER }, // 517 - { "date-to-yyyymmdd", DATE_TO_YYYYMMDD }, // 518 - { "date-written", DATE_WRITTEN }, // 519 - { "day", DAY }, // 520 - { "day-of-integer", DAY_OF_INTEGER }, // 521 - { "day-of-week", DAY_OF_WEEK }, // 522 - { "day-to-yyyyddd", DAY_TO_YYYYDDD }, // 523 - { "dbcs", DBCS }, // 524 - { "de", DE }, // 525 - { "debugging", DEBUGGING }, // 526 - { "decimal-point", DECIMAL_POINT }, // 527 - { "declaratives", DECLARATIVES }, // 528 - { "default", DEFAULT }, // 529 - { "delimited", DELIMITED }, // 530 - { "delimiter", DELIMITER }, // 531 - { "depending", DEPENDING }, // 532 - { "descending", DESCENDING }, // 533 - { "detail", DETAIL }, // 534 - { "direct", DIRECT }, // 535 - { "direct-access", DIRECT_ACCESS }, // 536 - { "down", DOWN }, // 537 - { "duplicates", DUPLICATES }, // 538 - { "dynamic", DYNAMIC }, // 539 - { "e", E }, // 540 - { "ebcdic", EBCDIC }, // 541 - { "ec", EC }, // 542 - { "egcs", EGCS }, // 543 - { "entry", ENTRY }, // 544 - { "environment", ENVIRONMENT }, // 545 - { "equal", EQUAL }, // 546 - { "every", EVERY }, // 547 - { "examine", EXAMINE }, // 548 - { "exhibit", EXHIBIT }, // 549 - { "exp", EXP }, // 550 - { "exp10", EXP10 }, // 551 - { "extend", EXTEND }, // 552 - { "external", EXTERNAL }, // 553 - { "exception-file", EXCEPTION_FILE }, // 554 - { "exception-file-n", EXCEPTION_FILE_N }, // 555 - { "exception-location", EXCEPTION_LOCATION }, // 556 - { "exception-location-n", EXCEPTION_LOCATION_N }, // 557 - { "exception-statement", EXCEPTION_STATEMENT }, // 558 - { "exception-status", EXCEPTION_STATUS }, // 559 - { "factorial", FACTORIAL }, // 560 - { "false", FALSE_kw }, // 561 - { "fd", FD }, // 562 - { "file-control", FILE_CONTROL }, // 563 - { "file", FILE_KW }, // 564 - { "file-limit", FILE_LIMIT }, // 565 - { "final", FINAL }, // 566 - { "finally", FINALLY }, // 567 - { "find-string", FIND_STRING }, // 568 - { "first", FIRST }, // 569 - { "fixed", FIXED }, // 570 - { "footing", FOOTING }, // 571 - { "for", FOR }, // 572 - { "formatted-current-date", FORMATTED_CURRENT_DATE }, // 573 - { "formatted-date", FORMATTED_DATE }, // 574 - { "formatted-datetime", FORMATTED_DATETIME }, // 575 - { "formatted-time", FORMATTED_TIME }, // 576 - { "form-overflow", FORM_OVERFLOW }, // 577 - { "free", FREE }, // 578 - { "fraction-part", FRACTION_PART }, // 579 - { "from", FROM }, // 580 - { "function", FUNCTION }, // 581 - { "generate", GENERATE }, // 582 - { "giving", GIVING }, // 583 - { "global", GLOBAL }, // 584 - { "go", GO }, // 585 - { "group", GROUP }, // 586 - { "heading", HEADING }, // 587 - { "hex", HEX }, // 588 - { "hex-of", HEX_OF }, // 589 - { "hex-to-char", HEX_TO_CHAR }, // 590 - { "high-values", HIGH_VALUES }, // 591 - { "highest-algebraic", HIGHEST_ALGEBRAIC }, // 592 - { "hold", HOLD }, // 593 - { "ibm-360", IBM_360 }, // 594 - { "in", IN }, // 595 - { "include", INCLUDE }, // 596 - { "index", INDEX }, // 597 - { "indexed", INDEXED }, // 598 - { "indicate", INDICATE }, // 599 - { "initial", INITIAL_kw }, // 600 - { "initiate", INITIATE }, // 601 - { "input", INPUT }, // 602 - { "installation", INSTALLATION }, // 603 - { "interface", INTERFACE }, // 604 - { "integer", INTEGER }, // 605 - { "integer-of-boolean", INTEGER_OF_BOOLEAN }, // 606 - { "integer-of-date", INTEGER_OF_DATE }, // 607 - { "integer-of-day", INTEGER_OF_DAY }, // 608 - { "integer-of-formatted-date", INTEGER_OF_FORMATTED_DATE }, // 609 - { "integer-part", INTEGER_PART }, // 610 - { "into", INTO }, // 611 - { "intrinsic", INTRINSIC }, // 612 - { "invoke", INVOKE }, // 613 - { "i-o", IO }, // 614 - { "i-o-control", IO_CONTROL }, // 615 - { "is", IS }, // 616 - { "isnt", ISNT }, // 617 - { "kanji", KANJI }, // 618 - { "key", KEY }, // 619 - { "label", LABEL }, // 620 - { "last", LAST }, // 621 - { "leading", LEADING }, // 622 - { "left", LEFT }, // 623 - { "length", LENGTH }, // 624 - { "length-of", LENGTH_OF }, // 625 - { "limit", LIMIT }, // 626 - { "limits", LIMITS }, // 627 - { "line", LINE }, // 628 - { "lines", LINES }, // 629 - { "line-counter", LINE_COUNTER }, // 630 - { "linage", LINAGE }, // 631 - { "linkage", LINKAGE }, // 632 - { "locale", LOCALE }, // 633 - { "locale-compare", LOCALE_COMPARE }, // 634 - { "locale-date", LOCALE_DATE }, // 635 - { "locale-time", LOCALE_TIME }, // 636 - { "locale-time-from-seconds", LOCALE_TIME_FROM_SECONDS }, // 637 - { "local-storage", LOCAL_STORAGE }, // 638 - { "location", LOCATION }, // 639 - { "lock", LOCK }, // 640 - { "lock-on", LOCK_ON }, // 641 - { "log", LOG }, // 642 - { "log10", LOG10 }, // 643 - { "lower-case", LOWER_CASE }, // 644 - { "low-values", LOW_VALUES }, // 645 - { "lowest-algebraic", LOWEST_ALGEBRAIC }, // 646 - { "lparen", LPAREN }, // 647 - { "manual", MANUAL }, // 648 - { "maxx", MAXX }, // 649 - { "mean", MEAN }, // 650 - { "median", MEDIAN }, // 651 - { "midrange", MIDRANGE }, // 652 - { "minn", MINN }, // 653 - { "multiple", MULTIPLE }, // 654 - { "mod", MOD }, // 655 - { "mode", MODE }, // 656 - { "module-name", MODULE_NAME }, // 657 - { "named", NAMED }, // 658 - { "nat", NAT }, // 659 - { "national", NATIONAL }, // 660 - { "national-edited", NATIONAL_EDITED }, // 661 - { "national-of", NATIONAL_OF }, // 662 - { "native", NATIVE }, // 663 - { "nested", NESTED }, // 664 - { "next", NEXT }, // 665 - { "no", NO }, // 666 - { "note", NOTE }, // 667 - { "nulls", NULLS }, // 668 - { "null", NULLS }, // 668 - { "nullptr", NULLPTR }, // 669 - { "numeric", NUMERIC }, // 670 - { "numeric-edited", NUMERIC_EDITED }, // 671 - { "numval", NUMVAL }, // 672 - { "numval-c", NUMVAL_C }, // 673 - { "numval-f", NUMVAL_F }, // 674 - { "occurs", OCCURS }, // 675 - { "of", OF }, // 676 - { "off", OFF }, // 677 - { "omitted", OMITTED }, // 678 - { "on", ON }, // 679 - { "only", ONLY }, // 680 - { "optional", OPTIONAL }, // 681 - { "options", OPTIONS }, // 682 - { "ord", ORD }, // 683 - { "order", ORDER }, // 684 - { "ord-max", ORD_MAX }, // 685 - { "ord-min", ORD_MIN }, // 686 - { "organization", ORGANIZATION }, // 687 - { "other", OTHER }, // 688 - { "otherwise", OTHERWISE }, // 689 - { "output", OUTPUT }, // 690 - { "packed-decimal", PACKED_DECIMAL }, // 691 - { "padding", PADDING }, // 692 - { "page", PAGE }, // 693 - { "page-counter", PAGE_COUNTER }, // 694 - { "pf", PF }, // 695 - { "ph", PH }, // 696 - { "pi", PI }, // 697 - { "pic", PIC }, // 698 - { "picture", PICTURE }, // 699 - { "plus", PLUS }, // 700 - { "present-value", PRESENT_VALUE }, // 701 - { "print-switch", PRINT_SWITCH }, // 702 - { "procedure", PROCEDURE }, // 703 - { "procedures", PROCEDURES }, // 704 - { "proceed", PROCEED }, // 705 - { "process", PROCESS }, // 706 - { "program-id", PROGRAM_ID }, // 707 - { "program", PROGRAM_kw }, // 708 - { "property", PROPERTY }, // 709 - { "prototype", PROTOTYPE }, // 710 - { "pseudotext", PSEUDOTEXT }, // 711 - { "quotes", QUOTES }, // 712 - { "quote", QUOTES }, // 712 - { "random", RANDOM }, // 713 - { "random-seed", RANDOM_SEED }, // 714 - { "range", RANGE }, // 715 - { "raise", RAISE }, // 716 - { "raising", RAISING }, // 717 - { "rd", RD }, // 718 - { "record", RECORD }, // 719 - { "recording", RECORDING }, // 720 - { "records", RECORDS }, // 721 - { "recursive", RECURSIVE }, // 722 - { "redefines", REDEFINES }, // 723 - { "reel", REEL }, // 724 - { "reference", REFERENCE }, // 725 - { "relative", RELATIVE }, // 726 - { "rem", REM }, // 727 - { "remainder", REMAINDER }, // 728 - { "remarks", REMARKS }, // 729 - { "removal", REMOVAL }, // 730 - { "renames", RENAMES }, // 731 - { "replace", REPLACE }, // 732 - { "replacing", REPLACING }, // 733 - { "report", REPORT }, // 734 - { "reporting", REPORTING }, // 735 - { "reports", REPORTS }, // 736 - { "repository", REPOSITORY }, // 737 - { "rerun", RERUN }, // 738 - { "reserve", RESERVE }, // 739 - { "restricted", RESTRICTED }, // 740 - { "resume", RESUME }, // 741 - { "reverse", REVERSE }, // 742 - { "reversed", REVERSED }, // 743 - { "rewind", REWIND }, // 744 - { "rf", RF }, // 745 - { "rh", RH }, // 746 - { "right", RIGHT }, // 747 - { "rounded", ROUNDED }, // 748 - { "run", RUN }, // 749 - { "same", SAME }, // 750 - { "screen", SCREEN }, // 751 - { "sd", SD }, // 752 - { "seconds-from-formatted-time", SECONDS_FROM_FORMATTED_TIME }, // 753 - { "seconds-past-midnight", SECONDS_PAST_MIDNIGHT }, // 754 - { "security", SECURITY }, // 755 - { "separate", SEPARATE }, // 756 - { "sequence", SEQUENCE }, // 757 - { "sequential", SEQUENTIAL }, // 758 - { "sharing", SHARING }, // 759 - { "simple-exit", SIMPLE_EXIT }, // 760 - { "sign", SIGN }, // 761 - { "sin", SIN }, // 762 - { "size", SIZE }, // 763 - { "smallest-algebraic", SMALLEST_ALGEBRAIC }, // 764 - { "source", SOURCE }, // 765 - { "source-computer", SOURCE_COMPUTER }, // 766 - { "special-names", SPECIAL_NAMES }, // 767 - { "sqrt", SQRT }, // 768 - { "stack", STACK }, // 769 - { "standard", STANDARD }, // 770 - { "standard-1", STANDARD_1 }, // 771 - { "standard-deviation", STANDARD_DEVIATION }, // 772 - { "standard-compare", STANDARD_COMPARE }, // 773 - { "status", STATUS }, // 774 - { "strong", STRONG }, // 775 - { "substitute", SUBSTITUTE }, // 776 - { "sum", SUM }, // 777 - { "symbol", SYMBOL }, // 778 - { "symbolic", SYMBOLIC }, // 779 - { "synchronized", SYNCHRONIZED }, // 780 - { "tally", TALLY }, // 781 - { "tallying", TALLYING }, // 782 - { "tan", TAN }, // 783 - { "terminate", TERMINATE }, // 784 - { "test", TEST }, // 785 - { "test-date-yyyymmdd", TEST_DATE_YYYYMMDD }, // 786 - { "test-day-yyyyddd", TEST_DAY_YYYYDDD }, // 787 - { "test-formatted-datetime", TEST_FORMATTED_DATETIME }, // 788 - { "test-numval", TEST_NUMVAL }, // 789 - { "test-numval-c", TEST_NUMVAL_C }, // 790 - { "test-numval-f", TEST_NUMVAL_F }, // 791 - { "than", THAN }, // 792 - { "time", TIME }, // 793 - { "times", TIMES }, // 794 - { "to", TO }, // 795 - { "top", TOP }, // 796 - { "top-level", TOP_LEVEL }, // 797 - { "tracks", TRACKS }, // 798 - { "track-area", TRACK_AREA }, // 799 - { "trailing", TRAILING }, // 800 - { "transform", TRANSFORM }, // 801 - { "trim", TRIM }, // 802 - { "true", TRUE_kw }, // 803 - { "try", TRY }, // 804 - { "turn", TURN }, // 805 - { "type", TYPE }, // 806 - { "typedef", TYPEDEF }, // 807 - { "ulength", ULENGTH }, // 808 - { "unbounded", UNBOUNDED }, // 809 - { "unit", UNIT }, // 810 - { "units", UNITS }, // 811 - { "unit-record", UNIT_RECORD }, // 812 - { "until", UNTIL }, // 813 - { "up", UP }, // 814 - { "upon", UPON }, // 815 - { "upos", UPOS }, // 816 - { "upper-case", UPPER_CASE }, // 817 - { "usage", USAGE }, // 818 - { "using", USING }, // 819 - { "usubstr", USUBSTR }, // 820 - { "usupplementary", USUPPLEMENTARY }, // 821 - { "utility", UTILITY }, // 822 - { "uuid4", UUID4 }, // 823 - { "uvalid", UVALID }, // 824 - { "uwidth", UWIDTH }, // 825 - { "value", VALUE }, // 826 - { "variance", VARIANCE }, // 827 - { "varying", VARYING }, // 828 - { "volatile", VOLATILE }, // 829 - { "when-compiled", WHEN_COMPILED }, // 830 - { "with", WITH }, // 831 - { "working-storage", WORKING_STORAGE }, // 832 - { "xml", XML }, // 833 - { "xmlgenerate", XMLGENERATE }, // 834 - { "xmlparse", XMLPARSE }, // 835 - { "year-to-yyyy", YEAR_TO_YYYY }, // 836 - { "yyyyddd", YYYYDDD }, // 837 - { "yyyymmdd", YYYYMMDD }, // 838 - { "arithmetic", ARITHMETIC }, // 839 - { "attribute", ATTRIBUTE }, // 840 - { "auto", AUTO }, // 841 - { "automatic", AUTOMATIC }, // 842 - { "away-from-zero", AWAY_FROM_ZERO }, // 843 - { "background-color", BACKGROUND_COLOR }, // 844 - { "bell", BELL }, // 845 - { "binary-encoding", BINARY_ENCODING }, // 846 - { "blink", BLINK }, // 847 - { "capacity", CAPACITY }, // 848 - { "center", CENTER }, // 849 - { "classification", CLASSIFICATION }, // 850 - { "cycle", CYCLE }, // 851 - { "decimal-encoding", DECIMAL_ENCODING }, // 852 - { "entry-convention", ENTRY_CONVENTION }, // 853 - { "eol", EOL }, // 854 - { "eos", EOS }, // 855 - { "erase", ERASE }, // 856 - { "expands", EXPANDS }, // 857 - { "float-binary", FLOAT_BINARY }, // 858 - { "float-decimal", FLOAT_DECIMAL }, // 859 - { "foreground-color", FOREGROUND_COLOR }, // 860 - { "forever", FOREVER }, // 861 - { "full", FULL }, // 862 - { "highlight", HIGHLIGHT }, // 863 - { "high-order-left", HIGH_ORDER_LEFT }, // 864 - { "high-order-right", HIGH_ORDER_RIGHT }, // 865 - { "ignoring", IGNORING }, // 866 - { "implements", IMPLEMENTS }, // 867 - { "initialized", INITIALIZED }, // 868 - { "intermediate", INTERMEDIATE }, // 869 - { "lc-all", LC_ALL_kw }, // 870 - { "lc-collate", LC_COLLATE_kw }, // 871 - { "lc-ctype", LC_CTYPE_kw }, // 872 - { "lc-messages", LC_MESSAGES_kw }, // 873 - { "lc-monetary", LC_MONETARY_kw }, // 874 - { "lc-numeric", LC_NUMERIC_kw }, // 875 - { "lc-time", LC_TIME_kw }, // 876 - { "lowlight", LOWLIGHT }, // 877 - { "nearest-away-from-zero", NEAREST_AWAY_FROM_ZERO }, // 878 - { "nearest-even", NEAREST_EVEN }, // 879 - { "nearest-toward-zero", NEAREST_TOWARD_ZERO }, // 880 - { "none", NONE }, // 881 - { "normal", NORMAL }, // 882 - { "numbers", NUMBERS }, // 883 - { "prefixed", PREFIXED }, // 884 - { "previous", PREVIOUS }, // 885 - { "prohibited", PROHIBITED }, // 886 - { "relation", RELATION }, // 887 - { "required", REQUIRED }, // 888 - { "reverse-video", REVERSE_VIDEO }, // 889 - { "rounding", ROUNDING }, // 890 - { "seconds", SECONDS }, // 891 - { "secure", SECURE }, // 892 - { "short", SHORT }, // 893 - { "signed", SIGNED }, // 894 - { "standard-binary", STANDARD_BINARY }, // 895 - { "standard-decimal", STANDARD_DECIMAL }, // 896 - { "statement", STATEMENT }, // 897 - { "step", STEP }, // 898 - { "structure", STRUCTURE }, // 899 - { "toward-greater", TOWARD_GREATER }, // 900 - { "toward-lesser", TOWARD_LESSER }, // 901 - { "truncation", TRUNCATION }, // 902 - { "ucs-4", UCS_4 }, // 903 - { "underline", UNDERLINE }, // 904 - { "unsigned", UNSIGNED }, // 905 - { "utf-16", UTF_16 }, // 906 - { "utf-8", UTF_8 }, // 907 - { "address", ADDRESS }, // 908 - { "end-accept", END_ACCEPT }, // 909 - { "end-add", END_ADD }, // 910 - { "end-call", END_CALL }, // 911 - { "end-compute", END_COMPUTE }, // 912 - { "end-delete", END_DELETE }, // 913 - { "end-display", END_DISPLAY }, // 914 - { "end-divide", END_DIVIDE }, // 915 - { "end-evaluate", END_EVALUATE }, // 916 - { "end-multiply", END_MULTIPLY }, // 917 - { "end-perform", END_PERFORM }, // 918 - { "end-read", END_READ }, // 919 - { "end-return", END_RETURN }, // 920 - { "end-rewrite", END_REWRITE }, // 921 - { "end-search", END_SEARCH }, // 922 - { "end-start", END_START }, // 923 - { "end-string", END_STRING }, // 924 - { "end-subtract", END_SUBTRACT }, // 925 - { "end-unstring", END_UNSTRING }, // 926 - { "end-write", END_WRITE }, // 927 - { "end-if", END_IF }, // 928 - { "thru", THRU }, // 929 - { "through", THRU }, // 929 - { "or", OR }, // 930 - { "and", AND }, // 931 - { "not", NOT }, // 932 - { "ne", NE }, // 933 - { "le", LE }, // 934 - { "ge", GE }, // 935 - { "pow", POW }, // 936 - { "neg", NEG }, // 937 + { "identification", IDENTIFICATION_DIV }, // 258 + { "environment", ENVIRONMENT_DIV }, // 259 + { "procedure", PROCEDURE_DIV }, // 260 + { "data", DATA_DIV }, // 261 + { "file", FILE_SECT }, // 262 + { "input-output", INPUT_OUTPUT_SECT }, // 263 + { "linkage", LINKAGE_SECT }, // 264 + { "local-storage", LOCAL_STORAGE_SECT }, // 265 + { "working-storage", WORKING_STORAGE_SECT }, // 266 + { "object-computer", OBJECT_COMPUTER }, // 267 + { "display-of", DISPLAY_OF }, // 268 + { "end-function", END_FUNCTION }, // 269 + { "end-program", END_PROGRAM }, // 270 + { "end-subprogram", END_SUBPROGRAM }, // 271 + { "justified", JUSTIFIED }, // 272 + { "returning", RETURNING }, // 273 + { "no-condition", NO_CONDITION }, // 274 + { "alnum", ALNUM }, // 275 + { "alphed", ALPHED }, // 276 + { "error", ERROR }, // 277 + { "exception", EXCEPTION }, // 278 + { "size-error", SIZE_ERROR }, // 279 + { "exception-name", EXCEPTION_NAME }, // 280 + { "level", LEVEL }, // 281 + { "level66", LEVEL66 }, // 282 + { "level78", LEVEL78 }, // 283 + { "level88", LEVEL88 }, // 284 + { "class-name", CLASS_NAME }, // 285 + { "name", NAME }, // 286 + { "name88", NAME88 }, // 287 + { "nume", NUME }, // 288 + { "numed", NUMED }, // 289 + { "numed-cr", NUMED_CR }, // 290 + { "numed-db", NUMED_DB }, // 291 + { "ninedot", NINEDOT }, // 292 + { "nines", NINES }, // 293 + { "ninev", NINEV }, // 294 + { "pic-p", PIC_P }, // 295 + { "ones", ONES }, // 296 + { "spaces", SPACES }, // 297 + { "space", SPACES }, // 297 + { "eq", EQ }, // 298 + { "literal", LITERAL }, // 299 + { "end", END }, // 300 + { "eop", EOP }, // 301 + { "filename", FILENAME }, // 302 + { "invalid", INVALID }, // 303 + { "number", NUMBER }, // 304 + { "negative", NEGATIVE }, // 305 + { "numstr", NUMSTR }, // 306 + { "overflow", OVERFLOW_kw }, // 307 + { "binary-integer", BINARY_INTEGER }, // 308 + { "computational", COMPUTATIONAL }, // 309 + { "perform", PERFORM }, // 310 + { "backward", BACKWARD }, // 311 + { "positive", POSITIVE }, // 312 + { "pointer", POINTER }, // 313 + { "section", SECTION }, // 314 + { "standard-alphabet", STANDARD_ALPHABET }, // 315 + { "switch", SWITCH }, // 316 + { "upsi", UPSI }, // 317 + { "zero", ZERO }, // 318 + { "zeros", ZERO }, // 318 + { "zeroes", ZERO }, // 318 + { "sysin", SYSIN }, // 319 + { "sysipt", SYSIPT }, // 320 + { "sysout", SYSOUT }, // 321 + { "syslist", SYSLIST }, // 322 + { "syslst", SYSLST }, // 323 + { "syspunch", SYSPUNCH }, // 324 + { "syspch", SYSPCH }, // 325 + { "console", CONSOLE }, // 326 + { "c01", C01 }, // 327 + { "c02", C02 }, // 328 + { "c03", C03 }, // 329 + { "c04", C04 }, // 330 + { "c05", C05 }, // 331 + { "c06", C06 }, // 332 + { "c07", C07 }, // 333 + { "c08", C08 }, // 334 + { "c09", C09 }, // 335 + { "c10", C10 }, // 336 + { "c11", C11 }, // 337 + { "c12", C12 }, // 338 + { "csp", CSP }, // 339 + { "s01", S01 }, // 340 + { "s02", S02 }, // 341 + { "s03", S03 }, // 342 + { "s04", S04 }, // 343 + { "s05", S05 }, // 344 + { "afp-5a", AFP_5A }, // 345 + { "stdin", STDIN }, // 346 + { "stdout", STDOUT }, // 347 + { "stderr", STDERR }, // 348 + { "list", LIST }, // 349 + { "map", MAP }, // 350 + { "nolist", NOLIST }, // 351 + { "nomap", NOMAP }, // 352 + { "nosource", NOSOURCE }, // 353 + { "might-be", MIGHT_BE }, // 354 + { "function-udf", FUNCTION_UDF }, // 355 + { "function-udf-0", FUNCTION_UDF_0 }, // 356 + { "default", DEFAULT }, // 357 + { "date-fmt", DATE_FMT }, // 358 + { "time-fmt", TIME_FMT }, // 359 + { "datetime-fmt", DATETIME_FMT }, // 360 + { "basis", BASIS }, // 361 + { "cbl", CBL }, // 362 + { "constant", CONSTANT }, // 363 + { "copy", COPY }, // 364 + { "defined", DEFINED }, // 365 + { "enter", ENTER }, // 366 + { "feature", FEATURE }, // 367 + { "insertt", INSERTT }, // 368 + { "lsub", LSUB }, // 369 + { "parameter", PARAMETER_kw }, // 370 + { "override", OVERRIDE }, // 371 + { "ready", READY }, // 372 + { "reset", RESET }, // 373 + { "rsub", RSUB }, // 374 + { "service-reload", SERVICE_RELOAD }, // 375 + { "star-cbl", STAR_CBL }, // 376 + { "subscript", SUBSCRIPT }, // 377 + { "suppress", SUPPRESS }, // 378 + { "title", TITLE }, // 379 + { "trace", TRACE }, // 380 + { "use", USE }, // 381 + { "cobol-words", COBOL_WORDS }, // 382 + { "equate", EQUATE }, // 383 + { "undefine", UNDEFINE }, // 384 + { "cdf-define", CDF_DEFINE }, // 385 + { "cdf-display", CDF_DISPLAY }, // 386 + { "cdf-if", CDF_IF }, // 387 + { "cdf-else", CDF_ELSE }, // 388 + { "cdf-end-if", CDF_END_IF }, // 389 + { "cdf-evaluate", CDF_EVALUATE }, // 390 + { "cdf-when", CDF_WHEN }, // 391 + { "cdf-end-evaluate", CDF_END_EVALUATE }, // 392 + { "call-convention", CALL_CONVENTION }, // 393 + { "call-cobol", CALL_COBOL }, // 394 + { "call-verbatim", CALL_VERBATIM }, // 395 + { "cdf-push", CDF_PUSH }, // 396 + { "cdf-pop", CDF_POP }, // 397 + { "source-format", SOURCE_FORMAT }, // 398 + { "if", IF }, // 399 + { "then", THEN }, // 400 + { "else", ELSE }, // 401 + { "sentence", SENTENCE }, // 402 + { "accept", ACCEPT }, // 403 + { "add", ADD }, // 404 + { "alter", ALTER }, // 405 + { "call", CALL }, // 406 + { "cancel", CANCEL }, // 407 + { "close", CLOSE }, // 408 + { "compute", COMPUTE }, // 409 + { "continue", CONTINUE }, // 410 + { "delete", DELETE }, // 411 + { "display", DISPLAY }, // 412 + { "divide", DIVIDE }, // 413 + { "evaluate", EVALUATE }, // 414 + { "exit", EXIT }, // 415 + { "filler", FILLER_kw }, // 416 + { "goback", GOBACK }, // 417 + { "goto", GOTO }, // 418 + { "initialize", INITIALIZE }, // 419 + { "inspect", INSPECT }, // 420 + { "merge", MERGE }, // 421 + { "move", MOVE }, // 422 + { "multiply", MULTIPLY }, // 423 + { "open", OPEN }, // 424 + { "paragraph", PARAGRAPH }, // 425 + { "read", READ }, // 426 + { "release", RELEASE }, // 427 + { "return", RETURN }, // 428 + { "rewrite", REWRITE }, // 429 + { "search", SEARCH }, // 430 + { "set", SET }, // 431 + { "select", SELECT }, // 432 + { "sort", SORT }, // 433 + { "sort-merge", SORT_MERGE }, // 434 + { "string", STRING_kw }, // 435 + { "stop", STOP }, // 436 + { "subtract", SUBTRACT }, // 437 + { "start", START }, // 438 + { "unstring", UNSTRING }, // 439 + { "write", WRITE }, // 440 + { "when", WHEN }, // 441 + { "argument-number", ARGUMENT_NUMBER }, // 442 + { "argument-value", ARGUMENT_VALUE }, // 443 + { "environment-name", ENVIRONMENT_NAME }, // 444 + { "environment-value", ENVIRONMENT_VALUE }, // 445 + { "abs", ABS }, // 446 + { "access", ACCESS }, // 447 + { "acos", ACOS }, // 448 + { "actual", ACTUAL }, // 449 + { "advancing", ADVANCING }, // 450 + { "after", AFTER }, // 451 + { "all", ALL }, // 452 + { "allocate", ALLOCATE }, // 453 + { "alphabet", ALPHABET }, // 454 + { "alphabetic", ALPHABETIC }, // 455 + { "alphabetic-lower", ALPHABETIC_LOWER }, // 456 + { "alphabetic-upper", ALPHABETIC_UPPER }, // 457 + { "alphanumeric", ALPHANUMERIC }, // 458 + { "alphanumeric-edited", ALPHANUMERIC_EDITED }, // 459 + { "also", ALSO }, // 460 + { "alternate", ALTERNATE }, // 461 + { "annuity", ANNUITY }, // 462 + { "anum", ANUM }, // 463 + { "any", ANY }, // 464 + { "anycase", ANYCASE }, // 465 + { "apply", APPLY }, // 466 + { "are", ARE }, // 467 + { "area", AREA }, // 468 + { "areas", AREAS }, // 469 + { "as", AS }, // 470 + { "ascending", ASCENDING }, // 471 + { "activating", ACTIVATING }, // 472 + { "asin", ASIN }, // 473 + { "assign", ASSIGN }, // 474 + { "at", AT }, // 475 + { "atan", ATAN }, // 476 + { "based", BASED }, // 477 + { "baseconvert", BASECONVERT }, // 478 + { "before", BEFORE }, // 479 + { "binary", BINARY }, // 480 + { "bit", BIT }, // 481 + { "bit-of", BIT_OF }, // 482 + { "bit-to-char", BIT_TO_CHAR }, // 483 + { "blank", BLANK }, // 484 + { "block", BLOCK_kw }, // 485 + { "boolean-of-integer", BOOLEAN_OF_INTEGER }, // 486 + { "bottom", BOTTOM }, // 487 + { "by", BY }, // 488 + { "byte", BYTE }, // 489 + { "byte-length", BYTE_LENGTH }, // 490 + { "cf", CF }, // 491 + { "ch", CH }, // 492 + { "changed", CHANGED }, // 493 + { "char", CHAR }, // 494 + { "char-national", CHAR_NATIONAL }, // 495 + { "character", CHARACTER }, // 496 + { "characters", CHARACTERS }, // 497 + { "checking", CHECKING }, // 498 + { "class", CLASS }, // 499 + { "cobol", COBOL }, // 500 + { "code", CODE }, // 501 + { "code-set", CODESET }, // 502 + { "collating", COLLATING }, // 503 + { "column", COLUMN }, // 504 + { "combined-datetime", COMBINED_DATETIME }, // 505 + { "comma", COMMA }, // 506 + { "command-line", COMMAND_LINE }, // 507 + { "command-line-count", COMMAND_LINE_COUNT }, // 508 + { "commit", COMMIT }, // 509 + { "common", COMMON }, // 510 + { "concat", CONCAT }, // 511 + { "condition", CONDITION }, // 512 + { "configuration", CONFIGURATION_SECT }, // 513 + { "contains", CONTAINS }, // 514 + { "content", CONTENT }, // 515 + { "control", CONTROL }, // 516 + { "controls", CONTROLS }, // 517 + { "convert", CONVERT }, // 518 + { "converting", CONVERTING }, // 519 + { "corresponding", CORRESPONDING }, // 520 + { "cos", COS }, // 521 + { "count", COUNT }, // 522 + { "currency", CURRENCY }, // 523 + { "current", CURRENT }, // 524 + { "current-date", CURRENT_DATE }, // 525 + { "data", DATA }, // 526 + { "date", DATE }, // 527 + { "date-compiled", DATE_COMPILED }, // 528 + { "date-of-integer", DATE_OF_INTEGER }, // 529 + { "date-to-yyyymmdd", DATE_TO_YYYYMMDD }, // 530 + { "date-written", DATE_WRITTEN }, // 531 + { "day", DAY }, // 532 + { "day-of-integer", DAY_OF_INTEGER }, // 533 + { "day-of-week", DAY_OF_WEEK }, // 534 + { "day-to-yyyyddd", DAY_TO_YYYYDDD }, // 535 + { "dbcs", DBCS }, // 536 + { "de", DE }, // 537 + { "debugging", DEBUGGING }, // 538 + { "decimal-point", DECIMAL_POINT }, // 539 + { "declaratives", DECLARATIVES }, // 540 + { "delimited", DELIMITED }, // 541 + { "delimiter", DELIMITER }, // 542 + { "depending", DEPENDING }, // 543 + { "descending", DESCENDING }, // 544 + { "detail", DETAIL }, // 545 + { "direct", DIRECT }, // 546 + { "direct-access", DIRECT_ACCESS }, // 547 + { "down", DOWN }, // 548 + { "duplicates", DUPLICATES }, // 549 + { "dynamic", DYNAMIC }, // 550 + { "e", E }, // 551 + { "ebcdic", EBCDIC }, // 552 + { "ec", EC }, // 553 + { "egcs", EGCS }, // 554 + { "encoding", ENCODING }, // 555 + { "entry", ENTRY }, // 556 + { "environment", ENVIRONMENT }, // 557 + { "every", EVERY }, // 558 + { "examine", EXAMINE }, // 559 + { "exhibit", EXHIBIT }, // 560 + { "exp", EXP }, // 561 + { "exp10", EXP10 }, // 562 + { "extend", EXTEND }, // 563 + { "external", EXTERNAL }, // 564 + { "exception-file", EXCEPTION_FILE }, // 565 + { "exception-file-n", EXCEPTION_FILE_N }, // 566 + { "exception-location", EXCEPTION_LOCATION }, // 567 + { "exception-location-n", EXCEPTION_LOCATION_N }, // 568 + { "exception-statement", EXCEPTION_STATEMENT }, // 569 + { "exception-status", EXCEPTION_STATUS }, // 570 + { "factorial", FACTORIAL }, // 571 + { "false", FALSE_kw }, // 572 + { "fd", FD }, // 573 + { "file-control", FILE_CONTROL }, // 574 + { "file", FILE_KW }, // 575 + { "file-limit", FILE_LIMIT }, // 576 + { "final", FINAL }, // 577 + { "finally", FINALLY }, // 578 + { "find-string", FIND_STRING }, // 579 + { "first", FIRST }, // 580 + { "fixed", FIXED }, // 581 + { "footing", FOOTING }, // 582 + { "for", FOR }, // 583 + { "formatted-current-date", FORMATTED_CURRENT_DATE }, // 584 + { "formatted-date", FORMATTED_DATE }, // 585 + { "formatted-datetime", FORMATTED_DATETIME }, // 586 + { "formatted-time", FORMATTED_TIME }, // 587 + { "form-overflow", FORM_OVERFLOW }, // 588 + { "free", FREE }, // 589 + { "fraction-part", FRACTION_PART }, // 590 + { "from", FROM }, // 591 + { "function", FUNCTION }, // 592 + { "generate", GENERATE }, // 593 + { "giving", GIVING }, // 594 + { "global", GLOBAL }, // 595 + { "go", GO }, // 596 + { "group", GROUP }, // 597 + { "heading", HEADING }, // 598 + { "hex", HEX }, // 599 + { "hex-of", HEX_OF }, // 600 + { "hex-to-char", HEX_TO_CHAR }, // 601 + { "high-values", HIGH_VALUES }, // 602 + { "highest-algebraic", HIGHEST_ALGEBRAIC }, // 603 + { "hold", HOLD }, // 604 + { "ibm-360", IBM_360 }, // 605 + { "in", IN }, // 606 + { "include", INCLUDE }, // 607 + { "index", INDEX }, // 608 + { "indexed", INDEXED }, // 609 + { "indicate", INDICATE }, // 610 + { "initial", INITIAL_kw }, // 611 + { "initiate", INITIATE }, // 612 + { "input", INPUT }, // 613 + { "installation", INSTALLATION }, // 614 + { "interface", INTERFACE }, // 615 + { "integer", INTEGER }, // 616 + { "integer-of-boolean", INTEGER_OF_BOOLEAN }, // 617 + { "integer-of-date", INTEGER_OF_DATE }, // 618 + { "integer-of-day", INTEGER_OF_DAY }, // 619 + { "integer-of-formatted-date", INTEGER_OF_FORMATTED_DATE }, // 620 + { "integer-part", INTEGER_PART }, // 621 + { "into", INTO }, // 622 + { "intrinsic", INTRINSIC }, // 623 + { "invoke", INVOKE }, // 624 + { "i-o", IO }, // 625 + { "i-o-control", IO_CONTROL }, // 626 + { "is", IS }, // 627 + { "isnt", ISNT }, // 628 + { "kanji", KANJI }, // 629 + { "key", KEY }, // 630 + { "label", LABEL }, // 631 + { "last", LAST }, // 632 + { "leading", LEADING }, // 633 + { "left", LEFT }, // 634 + { "length", LENGTH }, // 635 + { "length-of", LENGTH_OF }, // 636 + { "limit", LIMIT }, // 637 + { "limits", LIMITS }, // 638 + { "line", LINE }, // 639 + { "lines", LINES }, // 640 + { "line-counter", LINE_COUNTER }, // 641 + { "linage", LINAGE }, // 642 + { "linkage", LINKAGE }, // 643 + { "locale", LOCALE }, // 644 + { "locale-compare", LOCALE_COMPARE }, // 645 + { "locale-date", LOCALE_DATE }, // 646 + { "locale-time", LOCALE_TIME }, // 647 + { "locale-time-from-seconds", LOCALE_TIME_FROM_SECONDS }, // 648 + { "local-storage", LOCAL_STORAGE }, // 649 + { "location", LOCATION }, // 650 + { "lock", LOCK }, // 651 + { "lock-on", LOCK_ON }, // 652 + { "log", LOG }, // 653 + { "log10", LOG10 }, // 654 + { "lower-case", LOWER_CASE }, // 655 + { "low-values", LOW_VALUES }, // 656 + { "lowest-algebraic", LOWEST_ALGEBRAIC }, // 657 + { "lparen", LPAREN }, // 658 + { "manual", MANUAL }, // 659 + { "maxx", MAXX }, // 660 + { "mean", MEAN }, // 661 + { "median", MEDIAN }, // 662 + { "midrange", MIDRANGE }, // 663 + { "minn", MINN }, // 664 + { "multiple", MULTIPLE }, // 665 + { "mod", MOD }, // 666 + { "mode", MODE }, // 667 + { "module-name", MODULE_NAME }, // 668 + { "named", NAMED }, // 669 + { "nat", NAT }, // 670 + { "national", NATIONAL }, // 671 + { "national-edited", NATIONAL_EDITED }, // 672 + { "national-of", NATIONAL_OF }, // 673 + { "native", NATIVE }, // 674 + { "nested", NESTED }, // 675 + { "next", NEXT }, // 676 + { "no", NO }, // 677 + { "note", NOTE }, // 678 + { "nulls", NULLS }, // 679 + { "null", NULLS }, // 679 + { "nullptr", NULLPTR }, // 680 + { "numeric", NUMERIC }, // 681 + { "numeric-edited", NUMERIC_EDITED }, // 682 + { "numval", NUMVAL }, // 683 + { "numval-c", NUMVAL_C }, // 684 + { "numval-f", NUMVAL_F }, // 685 + { "occurs", OCCURS }, // 686 + { "of", OF }, // 687 + { "off", OFF }, // 688 + { "omitted", OMITTED }, // 689 + { "on", ON }, // 690 + { "only", ONLY }, // 691 + { "optional", OPTIONAL }, // 692 + { "options", OPTIONS }, // 693 + { "ord", ORD }, // 694 + { "order", ORDER }, // 695 + { "ord-max", ORD_MAX }, // 696 + { "ord-min", ORD_MIN }, // 697 + { "organization", ORGANIZATION }, // 698 + { "other", OTHER }, // 699 + { "otherwise", OTHERWISE }, // 700 + { "output", OUTPUT }, // 701 + { "packed-decimal", PACKED_DECIMAL }, // 702 + { "padding", PADDING }, // 703 + { "page", PAGE }, // 704 + { "page-counter", PAGE_COUNTER }, // 705 + { "pf", PF }, // 706 + { "ph", PH }, // 707 + { "pi", PI }, // 708 + { "pic", PIC }, // 709 + { "picture", PICTURE }, // 710 + { "plus", PLUS }, // 711 + { "present-value", PRESENT_VALUE }, // 712 + { "print-switch", PRINT_SWITCH }, // 713 + { "procedure", PROCEDURE }, // 714 + { "procedures", PROCEDURES }, // 715 + { "proceed", PROCEED }, // 716 + { "process", PROCESS }, // 717 + { "processing", PROCESSING }, // 718 + { "program-id", PROGRAM_ID }, // 719 + { "program", PROGRAM_kw }, // 720 + { "property", PROPERTY }, // 721 + { "prototype", PROTOTYPE }, // 722 + { "pseudotext", PSEUDOTEXT }, // 723 + { "quotes", QUOTES }, // 724 + { "quote", QUOTES }, // 724 + { "random", RANDOM }, // 725 + { "random-seed", RANDOM_SEED }, // 726 + { "range", RANGE }, // 727 + { "raise", RAISE }, // 728 + { "raising", RAISING }, // 729 + { "rd", RD }, // 730 + { "record", RECORD }, // 731 + { "recording", RECORDING }, // 732 + { "records", RECORDS }, // 733 + { "recursive", RECURSIVE }, // 734 + { "redefines", REDEFINES }, // 735 + { "reel", REEL }, // 736 + { "reference", REFERENCE }, // 737 + { "relative", RELATIVE }, // 738 + { "rem", REM }, // 739 + { "remainder", REMAINDER }, // 740 + { "remarks", REMARKS }, // 741 + { "removal", REMOVAL }, // 742 + { "renames", RENAMES }, // 743 + { "replace", REPLACE }, // 744 + { "replacing", REPLACING }, // 745 + { "report", REPORT }, // 746 + { "reporting", REPORTING }, // 747 + { "reports", REPORTS }, // 748 + { "repository", REPOSITORY }, // 749 + { "rerun", RERUN }, // 750 + { "reserve", RESERVE }, // 751 + { "restricted", RESTRICTED }, // 752 + { "resume", RESUME }, // 753 + { "retry", RETRY }, // 754 + { "reverse", REVERSE }, // 755 + { "reversed", REVERSED }, // 756 + { "rewind", REWIND }, // 757 + { "rf", RF }, // 758 + { "rh", RH }, // 759 + { "right", RIGHT }, // 760 + { "rounded", ROUNDED }, // 761 + { "run", RUN }, // 762 + { "same", SAME }, // 763 + { "screen", SCREEN }, // 764 + { "sd", SD }, // 765 + { "seconds-from-formatted-time", SECONDS_FROM_FORMATTED_TIME }, // 766 + { "seconds-past-midnight", SECONDS_PAST_MIDNIGHT }, // 767 + { "security", SECURITY }, // 768 + { "separate", SEPARATE }, // 769 + { "sequence", SEQUENCE }, // 770 + { "sequential", SEQUENTIAL }, // 771 + { "sharing", SHARING }, // 772 + { "simple-exit", SIMPLE_EXIT }, // 773 + { "sign", SIGN }, // 774 + { "sin", SIN }, // 775 + { "size", SIZE }, // 776 + { "smallest-algebraic", SMALLEST_ALGEBRAIC }, // 777 + { "source", SOURCE }, // 778 + { "source-computer", SOURCE_COMPUTER }, // 779 + { "special-names", SPECIAL_NAMES }, // 780 + { "sqrt", SQRT }, // 781 + { "stack", STACK }, // 782 + { "standard", STANDARD }, // 783 + { "standard-1", STANDARD_1 }, // 784 + { "standard-deviation", STANDARD_DEVIATION }, // 785 + { "standard-compare", STANDARD_COMPARE }, // 786 + { "status", STATUS }, // 787 + { "strong", STRONG }, // 788 + { "substitute", SUBSTITUTE }, // 789 + { "sum", SUM }, // 790 + { "symbol", SYMBOL }, // 791 + { "symbolic", SYMBOLIC }, // 792 + { "synchronized", SYNCHRONIZED }, // 793 + { "tallying", TALLYING }, // 794 + { "tan", TAN }, // 795 + { "terminate", TERMINATE }, // 796 + { "test", TEST }, // 797 + { "test-date-yyyymmdd", TEST_DATE_YYYYMMDD }, // 798 + { "test-day-yyyyddd", TEST_DAY_YYYYDDD }, // 799 + { "test-formatted-datetime", TEST_FORMATTED_DATETIME }, // 800 + { "test-numval", TEST_NUMVAL }, // 801 + { "test-numval-c", TEST_NUMVAL_C }, // 802 + { "test-numval-f", TEST_NUMVAL_F }, // 803 + { "than", THAN }, // 804 + { "time", TIME }, // 805 + { "times", TIMES }, // 806 + { "to", TO }, // 807 + { "top", TOP }, // 808 + { "top-level", TOP_LEVEL }, // 809 + { "tracks", TRACKS }, // 810 + { "track-area", TRACK_AREA }, // 811 + { "trailing", TRAILING }, // 812 + { "transform", TRANSFORM }, // 813 + { "trim", TRIM }, // 814 + { "true", TRUE_kw }, // 815 + { "try", TRY }, // 816 + { "turn", TURN }, // 817 + { "type", TYPE }, // 818 + { "typedef", TYPEDEF }, // 819 + { "ulength", ULENGTH }, // 820 + { "unbounded", UNBOUNDED }, // 821 + { "unit", UNIT }, // 822 + { "units", UNITS }, // 823 + { "unit-record", UNIT_RECORD }, // 824 + { "until", UNTIL }, // 825 + { "up", UP }, // 826 + { "upon", UPON }, // 827 + { "upos", UPOS }, // 828 + { "upper-case", UPPER_CASE }, // 829 + { "usage", USAGE }, // 830 + { "using", USING }, // 831 + { "usubstr", USUBSTR }, // 832 + { "usupplementary", USUPPLEMENTARY }, // 833 + { "utility", UTILITY }, // 834 + { "uuid4", UUID4 }, // 835 + { "uvalid", UVALID }, // 836 + { "uwidth", UWIDTH }, // 837 + { "validating", VALIDATING }, // 838 + { "value", VALUE }, // 839 + { "variance", VARIANCE }, // 840 + { "varying", VARYING }, // 841 + { "volatile", VOLATILE }, // 842 + { "when-compiled", WHEN_COMPILED }, // 843 + { "with", WITH }, // 844 + { "working-storage", WORKING_STORAGE }, // 845 + { "year-to-yyyy", YEAR_TO_YYYY }, // 846 + { "yyyyddd", YYYYDDD }, // 847 + { "yyyymmdd", YYYYMMDD }, // 848 + { "arithmetic", ARITHMETIC }, // 849 + { "attribute", ATTRIBUTE }, // 850 + { "auto", AUTO }, // 851 + { "automatic", AUTOMATIC }, // 852 + { "away-from-zero", AWAY_FROM_ZERO }, // 853 + { "background-color", BACKGROUND_COLOR }, // 854 + { "bell", BELL }, // 855 + { "binary-encoding", BINARY_ENCODING }, // 856 + { "blink", BLINK }, // 857 + { "capacity", CAPACITY }, // 858 + { "center", CENTER }, // 859 + { "classification", CLASSIFICATION }, // 860 + { "cycle", CYCLE }, // 861 + { "decimal-encoding", DECIMAL_ENCODING }, // 862 + { "entry-convention", ENTRY_CONVENTION }, // 863 + { "eol", EOL }, // 864 + { "eos", EOS }, // 865 + { "erase", ERASE }, // 866 + { "expands", EXPANDS }, // 867 + { "float-binary", FLOAT_BINARY }, // 868 + { "float-decimal", FLOAT_DECIMAL }, // 869 + { "foreground-color", FOREGROUND_COLOR }, // 870 + { "forever", FOREVER }, // 871 + { "full", FULL }, // 872 + { "highlight", HIGHLIGHT }, // 873 + { "high-order-left", HIGH_ORDER_LEFT }, // 874 + { "high-order-right", HIGH_ORDER_RIGHT }, // 875 + { "ignoring", IGNORING }, // 876 + { "implements", IMPLEMENTS }, // 877 + { "initialized", INITIALIZED }, // 878 + { "intermediate", INTERMEDIATE }, // 879 + { "lc-all", LC_ALL_kw }, // 880 + { "lc-collate", LC_COLLATE_kw }, // 881 + { "lc-ctype", LC_CTYPE_kw }, // 882 + { "lc-messages", LC_MESSAGES_kw }, // 883 + { "lc-monetary", LC_MONETARY_kw }, // 884 + { "lc-numeric", LC_NUMERIC_kw }, // 885 + { "lc-time", LC_TIME_kw }, // 886 + { "lowlight", LOWLIGHT }, // 887 + { "nearest-away-from-zero", NEAREST_AWAY_FROM_ZERO }, // 888 + { "nearest-even", NEAREST_EVEN }, // 889 + { "nearest-toward-zero", NEAREST_TOWARD_ZERO }, // 890 + { "none", NONE }, // 891 + { "normal", NORMAL }, // 892 + { "numbers", NUMBERS }, // 893 + { "prefixed", PREFIXED }, // 894 + { "previous", PREVIOUS }, // 895 + { "prohibited", PROHIBITED }, // 896 + { "relation", RELATION }, // 897 + { "required", REQUIRED }, // 898 + { "reverse-video", REVERSE_VIDEO }, // 899 + { "rounding", ROUNDING }, // 900 + { "seconds", SECONDS }, // 901 + { "secure", SECURE }, // 902 + { "short", SHORT }, // 903 + { "signed", SIGNED_kw }, // 904 + { "standard-binary", STANDARD_BINARY }, // 905 + { "standard-decimal", STANDARD_DECIMAL }, // 906 + { "statement", STATEMENT }, // 907 + { "step", STEP }, // 908 + { "structure", STRUCTURE }, // 909 + { "toward-greater", TOWARD_GREATER }, // 910 + { "toward-lesser", TOWARD_LESSER }, // 911 + { "truncation", TRUNCATION }, // 912 + { "ucs-4", UCS_4 }, // 913 + { "underline", UNDERLINE }, // 914 + { "unsigned", UNSIGNED_kw }, // 915 + { "utf-16", UTF_16 }, // 916 + { "utf-8", UTF_8 }, // 917 + { "xmlgenerate", XMLGENERATE }, // 918 + { "xmlparse", XMLPARSE }, // 919 + { "address", ADDRESS }, // 920 + { "end-accept", END_ACCEPT }, // 921 + { "end-add", END_ADD }, // 922 + { "end-call", END_CALL }, // 923 + { "end-compute", END_COMPUTE }, // 924 + { "end-delete", END_DELETE }, // 925 + { "end-display", END_DISPLAY }, // 926 + { "end-divide", END_DIVIDE }, // 927 + { "end-evaluate", END_EVALUATE }, // 928 + { "end-multiply", END_MULTIPLY }, // 929 + { "end-perform", END_PERFORM }, // 930 + { "end-read", END_READ }, // 931 + { "end-return", END_RETURN }, // 932 + { "end-rewrite", END_REWRITE }, // 933 + { "end-search", END_SEARCH }, // 934 + { "end-start", END_START }, // 935 + { "end-string", END_STRING }, // 936 + { "end-subtract", END_SUBTRACT }, // 937 + { "end-unstring", END_UNSTRING }, // 938 + { "end-write", END_WRITE }, // 939 + { "end-xml", END_XML }, // 940 + { "end-if", END_IF }, // 941 + { "attributes", ATTRIBUTES }, // 942 + { "element", ELEMENT }, // 943 + { "namespace", NAMESPACE }, // 944 + { "namespace-prefix", NAMESPACE_PREFIX }, // 945 + { "nonnumeric", NONNUMERIC }, // 947 + { "xml-declaration", XML_DECLARATION }, // 948 + { "thru", THRU }, // 950 + { "through", THRU }, // 950 + { "or", OR }, // 951 + { "and", AND }, // 952 + { "not", NOT }, // 953 + { "ne", NE }, // 954 + { "le", LE }, // 955 + { "ge", GE }, // 956 + { "pow", POW }, // 957 + { "neg", NEG }, // 958 }; +// cppcheck-suppress useInitializationList token_names = { - "IDENTIFICATION", // 0 (258) - "ENVIRONMENT", // 1 (259) - "PROCEDURE", // 2 (260) - "DATA", // 3 (261) - "FILE", // 4 (262) - "INPUT-OUTPUT", // 5 (263) - "LINKAGE", // 6 (264) - "LOCAL-STORAGE", // 7 (265) - "WORKING-STORAGE", // 8 (266) - "OBJECT-COMPUTER", // 9 (267) - "DISPLAY-OF", // 10 (268) - "END-FUNCTION", // 11 (269) - "END-PROGRAM", // 12 (270) - "END-SUBPROGRAM", // 13 (271) - "JUSTIFIED", // 14 (272) - "RETURNING", // 15 (273) - "NO-CONDITION", // 16 (274) - "ALNUM", // 17 (275) - "ALPHED", // 18 (276) - "ERROR", // 19 (277) - "EXCEPTION", // 20 (278) - "SIZE-ERROR", // 21 (279) - "EXCEPTION-NAME", // 22 (280) - "LEVEL", // 23 (281) - "LEVEL66", // 24 (282) - "LEVEL78", // 25 (283) - "LEVEL88", // 26 (284) - "CLASS-NAME", // 27 (285) - "NAME", // 28 (286) - "NAME88", // 29 (287) - "NUME", // 30 (288) - "NUMED", // 31 (289) - "NUMED-CR", // 32 (290) - "NUMED-DB", // 33 (291) - "NINEDOT", // 34 (292) - "NINES", // 35 (293) - "NINEV", // 36 (294) - "PIC-P", // 37 (295) - "SPACES", // 38 (296) - "LITERAL", // 39 (297) - "END", // 40 (298) - "EOP", // 41 (299) - "FILENAME", // 42 (300) - "INVALID", // 43 (301) - "NUMBER", // 44 (302) - "NEGATIVE", // 45 (303) - "NUMSTR", // 46 (304) - "OVERFLOW", // 47 (305) - "COMPUTATIONAL", // 48 (306) - "PERFORM", // 49 (307) - "BACKWARD", // 50 (308) - "POSITIVE", // 51 (309) - "POINTER", // 52 (310) - "SECTION", // 53 (311) - "STANDARD-ALPHABET", // 54 (312) - "SWITCH", // 55 (313) - "UPSI", // 56 (314) - "ZERO", // 57 (315) - "SYSIN", // 58 (316) - "SYSIPT", // 59 (317) - "SYSOUT", // 60 (318) - "SYSLIST", // 61 (319) - "SYSLST", // 62 (320) - "SYSPUNCH", // 63 (321) - "SYSPCH", // 64 (322) - "CONSOLE", // 65 (323) - "C01", // 66 (324) - "C02", // 67 (325) - "C03", // 68 (326) - "C04", // 69 (327) - "C05", // 70 (328) - "C06", // 71 (329) - "C07", // 72 (330) - "C08", // 73 (331) - "C09", // 74 (332) - "C10", // 75 (333) - "C11", // 76 (334) - "C12", // 77 (335) - "CSP", // 78 (336) - "S01", // 79 (337) - "S02", // 80 (338) - "S03", // 81 (339) - "S04", // 82 (340) - "S05", // 83 (341) - "AFP-5A", // 84 (342) - "STDIN", // 85 (343) - "STDOUT", // 86 (344) - "STDERR", // 87 (345) - "LIST", // 88 (346) - "MAP", // 89 (347) - "NOLIST", // 90 (348) - "NOMAP", // 91 (349) - "NOSOURCE", // 92 (350) - "MIGHT-BE", // 93 (351) - "FUNCTION-UDF", // 94 (352) - "FUNCTION-UDF-0", // 95 (353) - "DATE-FMT", // 96 (354) - "TIME-FMT", // 97 (355) - "DATETIME-FMT", // 98 (356) - "BASIS", // 99 (357) - "CBL", // 100 (358) - "CONSTANT", // 101 (359) - "COPY", // 102 (360) - "DEFINED", // 103 (361) - "ENTER", // 104 (362) - "FEATURE", // 105 (363) - "INSERTT", // 106 (364) - "LSUB", // 107 (365) - "PARAMETER", // 108 (366) - "OVERRIDE", // 109 (367) - "READY", // 110 (368) - "RESET", // 111 (369) - "RSUB", // 112 (370) - "SERVICE-RELOAD", // 113 (371) - "STAR-CBL", // 114 (372) - "SUBSCRIPT", // 115 (373) - "SUPPRESS", // 116 (374) - "TITLE", // 117 (375) - "TRACE", // 118 (376) - "USE", // 119 (377) - "COBOL-WORDS", // 120 (378) - "EQUATE", // 121 (379) - "UNDEFINE", // 122 (380) - "CDF-DEFINE", // 123 (381) - "CDF-DISPLAY", // 124 (382) - "CDF-IF", // 125 (383) - "CDF-ELSE", // 126 (384) - "CDF-END-IF", // 127 (385) - "CDF-EVALUATE", // 128 (386) - "CDF-WHEN", // 129 (387) - "CDF-END-EVALUATE", // 130 (388) - "CALL-COBOL", // 131 (389) - "CALL-VERBATIM", // 132 (390) - "IF", // 133 (391) - "THEN", // 134 (392) - "ELSE", // 135 (393) - "SENTENCE", // 136 (394) - "ACCEPT", // 137 (395) - "ADD", // 138 (396) - "ALTER", // 139 (397) - "CALL", // 140 (398) - "CANCEL", // 141 (399) - "CLOSE", // 142 (400) - "COMPUTE", // 143 (401) - "CONTINUE", // 144 (402) - "DELETE", // 145 (403) - "DISPLAY", // 146 (404) - "DIVIDE", // 147 (405) - "EVALUATE", // 148 (406) - "EXIT", // 149 (407) - "FILLER", // 150 (408) - "GOBACK", // 151 (409) - "GOTO", // 152 (410) - "INITIALIZE", // 153 (411) - "INSPECT", // 154 (412) - "MERGE", // 155 (413) - "MOVE", // 156 (414) - "MULTIPLY", // 157 (415) - "OPEN", // 158 (416) - "PARAGRAPH", // 159 (417) - "READ", // 160 (418) - "RELEASE", // 161 (419) - "RETURN", // 162 (420) - "REWRITE", // 163 (421) - "SEARCH", // 164 (422) - "SET", // 165 (423) - "SELECT", // 166 (424) - "SORT", // 167 (425) - "SORT-MERGE", // 168 (426) - "STRING", // 169 (427) - "STOP", // 170 (428) - "SUBTRACT", // 171 (429) - "START", // 172 (430) - "UNSTRING", // 173 (431) - "WRITE", // 174 (432) - "WHEN", // 175 (433) - "ABS", // 176 (434) - "ACCESS", // 177 (435) - "ACOS", // 178 (436) - "ACTUAL", // 179 (437) - "ADVANCING", // 180 (438) - "AFTER", // 181 (439) - "ALL", // 182 (440) - "ALLOCATE", // 183 (441) - "ALPHABET", // 184 (442) - "ALPHABETIC", // 185 (443) - "ALPHABETIC-LOWER", // 186 (444) - "ALPHABETIC-UPPER", // 187 (445) - "ALPHANUMERIC", // 188 (446) - "ALPHANUMERIC-EDITED", // 189 (447) - "ALSO", // 190 (448) - "ALTERNATE", // 191 (449) - "ANNUITY", // 192 (450) - "ANUM", // 193 (451) - "ANY", // 194 (452) - "ANYCASE", // 195 (453) - "APPLY", // 196 (454) - "ARE", // 197 (455) - "AREA", // 198 (456) - "AREAS", // 199 (457) - "AS", // 200 (458) - "ASCENDING", // 201 (459) - "ACTIVATING", // 202 (460) - "ASIN", // 203 (461) - "ASSIGN", // 204 (462) - "AT", // 205 (463) - "ATAN", // 206 (464) - "BASED", // 207 (465) - "BASECONVERT", // 208 (466) - "BEFORE", // 209 (467) - "BINARY", // 210 (468) - "BIT", // 211 (469) - "BIT-OF", // 212 (470) - "BIT-TO-CHAR", // 213 (471) - "BLANK", // 214 (472) - "BLOCK", // 215 (473) - "BOOLEAN-OF-INTEGER", // 216 (474) - "BOTTOM", // 217 (475) - "BY", // 218 (476) - "BYTE", // 219 (477) - "BYTE-LENGTH", // 220 (478) - "CF", // 221 (479) - "CH", // 222 (480) - "CHANGED", // 223 (481) - "CHAR", // 224 (482) - "CHAR-NATIONAL", // 225 (483) - "CHARACTER", // 226 (484) - "CHARACTERS", // 227 (485) - "CHECKING", // 228 (486) - "CLASS", // 229 (487) - "COBOL", // 230 (488) - "CODE", // 231 (489) - "CODE-SET", // 232 (490) - "COLLATING", // 233 (491) - "COLUMN", // 234 (492) - "COMBINED-DATETIME", // 235 (493) - "COMMA", // 236 (494) - "COMMAND-LINE", // 237 (495) - "COMMAND-LINE-COUNT", // 238 (496) - "COMMIT", // 239 (497) - "COMMON", // 240 (498) - "CONCAT", // 241 (499) - "CONDITION", // 242 (500) - "CONFIGURATION", // 243 (501) - "CONTAINS", // 244 (502) - "CONTENT", // 245 (503) - "CONTROL", // 246 (504) - "CONTROLS", // 247 (505) - "CONVERT", // 248 (506) - "CONVERTING", // 249 (507) - "CORRESPONDING", // 250 (508) - "COS", // 251 (509) - "COUNT", // 252 (510) - "CURRENCY", // 253 (511) - "CURRENT", // 254 (512) - "CURRENT-DATE", // 255 (513) - "DATA", // 256 (514) - "DATE", // 257 (515) - "DATE-COMPILED", // 258 (516) - "DATE-OF-INTEGER", // 259 (517) - "DATE-TO-YYYYMMDD", // 260 (518) - "DATE-WRITTEN", // 261 (519) - "DAY", // 262 (520) - "DAY-OF-INTEGER", // 263 (521) - "DAY-OF-WEEK", // 264 (522) - "DAY-TO-YYYYDDD", // 265 (523) - "DBCS", // 266 (524) - "DE", // 267 (525) - "DEBUGGING", // 268 (526) - "DECIMAL-POINT", // 269 (527) - "DECLARATIVES", // 270 (528) - "DEFAULT", // 271 (529) - "DELIMITED", // 272 (530) - "DELIMITER", // 273 (531) - "DEPENDING", // 274 (532) - "DESCENDING", // 275 (533) - "DETAIL", // 276 (534) - "DIRECT", // 277 (535) - "DIRECT-ACCESS", // 278 (536) - "DOWN", // 279 (537) - "DUPLICATES", // 280 (538) - "DYNAMIC", // 281 (539) - "E", // 282 (540) - "EBCDIC", // 283 (541) - "EC", // 284 (542) - "EGCS", // 285 (543) - "ENTRY", // 286 (544) - "ENVIRONMENT", // 287 (545) - "EQUAL", // 288 (546) - "EVERY", // 289 (547) - "EXAMINE", // 290 (548) - "EXHIBIT", // 291 (549) - "EXP", // 292 (550) - "EXP10", // 293 (551) - "EXTEND", // 294 (552) - "EXTERNAL", // 295 (553) - "EXCEPTION-FILE", // 296 (554) - "EXCEPTION-FILE-N", // 297 (555) - "EXCEPTION-LOCATION", // 298 (556) - "EXCEPTION-LOCATION-N", // 299 (557) - "EXCEPTION-STATEMENT", // 300 (558) - "EXCEPTION-STATUS", // 301 (559) - "FACTORIAL", // 302 (560) - "FALSE", // 303 (561) - "FD", // 304 (562) - "FILE-CONTROL", // 305 (563) - "FILE", // 306 (564) - "FILE-LIMIT", // 307 (565) - "FINAL", // 308 (566) - "FINALLY", // 309 (567) - "FIND-STRING", // 310 (568) - "FIRST", // 311 (569) - "FIXED", // 312 (570) - "FOOTING", // 313 (571) - "FOR", // 314 (572) - "FORMATTED-CURRENT-DATE", // 315 (573) - "FORMATTED-DATE", // 316 (574) - "FORMATTED-DATETIME", // 317 (575) - "FORMATTED-TIME", // 318 (576) - "FORM-OVERFLOW", // 319 (577) - "FREE", // 320 (578) - "FRACTION-PART", // 321 (579) - "FROM", // 322 (580) - "FUNCTION", // 323 (581) - "GENERATE", // 324 (582) - "GIVING", // 325 (583) - "GLOBAL", // 326 (584) - "GO", // 327 (585) - "GROUP", // 328 (586) - "HEADING", // 329 (587) - "HEX", // 330 (588) - "HEX-OF", // 331 (589) - "HEX-TO-CHAR", // 332 (590) - "HIGH-VALUES", // 333 (591) - "HIGHEST-ALGEBRAIC", // 334 (592) - "HOLD", // 335 (593) - "IBM-360", // 336 (594) - "IN", // 337 (595) - "INCLUDE", // 338 (596) - "INDEX", // 339 (597) - "INDEXED", // 340 (598) - "INDICATE", // 341 (599) - "INITIAL", // 342 (600) - "INITIATE", // 343 (601) - "INPUT", // 344 (602) - "INSTALLATION", // 345 (603) - "INTERFACE", // 346 (604) - "INTEGER", // 347 (605) - "INTEGER-OF-BOOLEAN", // 348 (606) - "INTEGER-OF-DATE", // 349 (607) - "INTEGER-OF-DAY", // 350 (608) - "INTEGER-OF-FORMATTED-DATE", // 351 (609) - "INTEGER-PART", // 352 (610) - "INTO", // 353 (611) - "INTRINSIC", // 354 (612) - "INVOKE", // 355 (613) - "I-O", // 356 (614) - "I-O-CONTROL", // 357 (615) - "IS", // 358 (616) - "ISNT", // 359 (617) - "KANJI", // 360 (618) - "KEY", // 361 (619) - "LABEL", // 362 (620) - "LAST", // 363 (621) - "LEADING", // 364 (622) - "LEFT", // 365 (623) - "LENGTH", // 366 (624) - "LENGTH-OF", // 367 (625) - "LIMIT", // 368 (626) - "LIMITS", // 369 (627) - "LINE", // 370 (628) - "LINES", // 371 (629) - "LINE-COUNTER", // 372 (630) - "LINAGE", // 373 (631) - "LINKAGE", // 374 (632) - "LOCALE", // 375 (633) - "LOCALE-COMPARE", // 376 (634) - "LOCALE-DATE", // 377 (635) - "LOCALE-TIME", // 378 (636) - "LOCALE-TIME-FROM-SECONDS", // 379 (637) - "LOCAL-STORAGE", // 380 (638) - "LOCATION", // 381 (639) - "LOCK", // 382 (640) - "LOCK-ON", // 383 (641) - "LOG", // 384 (642) - "LOG10", // 385 (643) - "LOWER-CASE", // 386 (644) - "LOW-VALUES", // 387 (645) - "LOWEST-ALGEBRAIC", // 388 (646) - "LPAREN", // 389 (647) - "MANUAL", // 390 (648) - "MAXX", // 391 (649) - "MEAN", // 392 (650) - "MEDIAN", // 393 (651) - "MIDRANGE", // 394 (652) - "MINN", // 395 (653) - "MULTIPLE", // 396 (654) - "MOD", // 397 (655) - "MODE", // 398 (656) - "MODULE-NAME", // 399 (657) - "NAMED", // 400 (658) - "NAT", // 401 (659) - "NATIONAL", // 402 (660) - "NATIONAL-EDITED", // 403 (661) - "NATIONAL-OF", // 404 (662) - "NATIVE", // 405 (663) - "NESTED", // 406 (664) - "NEXT", // 407 (665) - "NO", // 408 (666) - "NOTE", // 409 (667) - "NULLS", // 410 (668) - "NULLPTR", // 411 (669) - "NUMERIC", // 412 (670) - "NUMERIC-EDITED", // 413 (671) - "NUMVAL", // 414 (672) - "NUMVAL-C", // 415 (673) - "NUMVAL-F", // 416 (674) - "OCCURS", // 417 (675) - "OF", // 418 (676) - "OFF", // 419 (677) - "OMITTED", // 420 (678) - "ON", // 421 (679) - "ONLY", // 422 (680) - "OPTIONAL", // 423 (681) - "OPTIONS", // 424 (682) - "ORD", // 425 (683) - "ORDER", // 426 (684) - "ORD-MAX", // 427 (685) - "ORD-MIN", // 428 (686) - "ORGANIZATION", // 429 (687) - "OTHER", // 430 (688) - "OTHERWISE", // 431 (689) - "OUTPUT", // 432 (690) - "PACKED-DECIMAL", // 433 (691) - "PADDING", // 434 (692) - "PAGE", // 435 (693) - "PAGE-COUNTER", // 436 (694) - "PF", // 437 (695) - "PH", // 438 (696) - "PI", // 439 (697) - "PIC", // 440 (698) - "PICTURE", // 441 (699) - "PLUS", // 442 (700) - "PRESENT-VALUE", // 443 (701) - "PRINT-SWITCH", // 444 (702) - "PROCEDURE", // 445 (703) - "PROCEDURES", // 446 (704) - "PROCEED", // 447 (705) - "PROCESS", // 448 (706) - "PROGRAM-ID", // 449 (707) - "PROGRAM", // 450 (708) - "PROPERTY", // 451 (709) - "PROTOTYPE", // 452 (710) - "PSEUDOTEXT", // 453 (711) - "QUOTES", // 454 (712) - "RANDOM", // 455 (713) - "RANDOM-SEED", // 456 (714) - "RANGE", // 457 (715) - "RAISE", // 458 (716) - "RAISING", // 459 (717) - "RD", // 460 (718) - "RECORD", // 461 (719) - "RECORDING", // 462 (720) - "RECORDS", // 463 (721) - "RECURSIVE", // 464 (722) - "REDEFINES", // 465 (723) - "REEL", // 466 (724) - "REFERENCE", // 467 (725) - "RELATIVE", // 468 (726) - "REM", // 469 (727) - "REMAINDER", // 470 (728) - "REMARKS", // 471 (729) - "REMOVAL", // 472 (730) - "RENAMES", // 473 (731) - "REPLACE", // 474 (732) - "REPLACING", // 475 (733) - "REPORT", // 476 (734) - "REPORTING", // 477 (735) - "REPORTS", // 478 (736) - "REPOSITORY", // 479 (737) - "RERUN", // 480 (738) - "RESERVE", // 481 (739) - "RESTRICTED", // 482 (740) - "RESUME", // 483 (741) - "REVERSE", // 484 (742) - "REVERSED", // 485 (743) - "REWIND", // 486 (744) - "RF", // 487 (745) - "RH", // 488 (746) - "RIGHT", // 489 (747) - "ROUNDED", // 490 (748) - "RUN", // 491 (749) - "SAME", // 492 (750) - "SCREEN", // 493 (751) - "SD", // 494 (752) - "SECONDS-FROM-FORMATTED-TIME", // 495 (753) - "SECONDS-PAST-MIDNIGHT", // 496 (754) - "SECURITY", // 497 (755) - "SEPARATE", // 498 (756) - "SEQUENCE", // 499 (757) - "SEQUENTIAL", // 500 (758) - "SHARING", // 501 (759) - "SIMPLE-EXIT", // 502 (760) - "SIGN", // 503 (761) - "SIN", // 504 (762) - "SIZE", // 505 (763) - "SMALLEST-ALGEBRAIC", // 506 (764) - "SOURCE", // 507 (765) - "SOURCE-COMPUTER", // 508 (766) - "SPECIAL-NAMES", // 509 (767) - "SQRT", // 510 (768) - "STACK", // 511 (769) - "STANDARD", // 512 (770) - "STANDARD-1", // 513 (771) - "STANDARD-DEVIATION", // 514 (772) - "STANDARD-COMPARE", // 515 (773) - "STATUS", // 516 (774) - "STRONG", // 517 (775) - "SUBSTITUTE", // 518 (776) - "SUM", // 519 (777) - "SYMBOL", // 520 (778) - "SYMBOLIC", // 521 (779) - "SYNCHRONIZED", // 522 (780) - "TALLY", // 523 (781) - "TALLYING", // 524 (782) - "TAN", // 525 (783) - "TERMINATE", // 526 (784) - "TEST", // 527 (785) - "TEST-DATE-YYYYMMDD", // 528 (786) - "TEST-DAY-YYYYDDD", // 529 (787) - "TEST-FORMATTED-DATETIME", // 530 (788) - "TEST-NUMVAL", // 531 (789) - "TEST-NUMVAL-C", // 532 (790) - "TEST-NUMVAL-F", // 533 (791) - "THAN", // 534 (792) - "TIME", // 535 (793) - "TIMES", // 536 (794) - "TO", // 537 (795) - "TOP", // 538 (796) - "TOP-LEVEL", // 539 (797) - "TRACKS", // 540 (798) - "TRACK-AREA", // 541 (799) - "TRAILING", // 542 (800) - "TRANSFORM", // 543 (801) - "TRIM", // 544 (802) - "TRUE", // 545 (803) - "TRY", // 546 (804) - "TURN", // 547 (805) - "TYPE", // 548 (806) - "TYPEDEF", // 549 (807) - "ULENGTH", // 550 (808) - "UNBOUNDED", // 551 (809) - "UNIT", // 552 (810) - "UNITS", // 553 (811) - "UNIT-RECORD", // 554 (812) - "UNTIL", // 555 (813) - "UP", // 556 (814) - "UPON", // 557 (815) - "UPOS", // 558 (816) - "UPPER-CASE", // 559 (817) - "USAGE", // 560 (818) - "USING", // 561 (819) - "USUBSTR", // 562 (820) - "USUPPLEMENTARY", // 563 (821) - "UTILITY", // 564 (822) - "UUID4", // 565 (823) - "UVALID", // 566 (824) - "UWIDTH", // 567 (825) - "VALUE", // 568 (826) - "VARIANCE", // 569 (827) - "VARYING", // 570 (828) - "VOLATILE", // 571 (829) - "WHEN-COMPILED", // 572 (830) - "WITH", // 573 (831) - "WORKING-STORAGE", // 574 (832) - "XML", // 575 (833) - "XMLGENERATE", // 576 (834) - "XMLPARSE", // 577 (835) - "YEAR-TO-YYYY", // 578 (836) - "YYYYDDD", // 579 (837) - "YYYYMMDD", // 580 (838) - "ARITHMETIC", // 581 (839) - "ATTRIBUTE", // 582 (840) - "AUTO", // 583 (841) - "AUTOMATIC", // 584 (842) - "AWAY-FROM-ZERO", // 585 (843) - "BACKGROUND-COLOR", // 586 (844) - "BELL", // 587 (845) - "BINARY-ENCODING", // 588 (846) - "BLINK", // 589 (847) - "CAPACITY", // 590 (848) - "CENTER", // 591 (849) - "CLASSIFICATION", // 592 (850) - "CYCLE", // 593 (851) - "DECIMAL-ENCODING", // 594 (852) - "ENTRY-CONVENTION", // 595 (853) - "EOL", // 596 (854) - "EOS", // 597 (855) - "ERASE", // 598 (856) - "EXPANDS", // 599 (857) - "FLOAT-BINARY", // 600 (858) - "FLOAT-DECIMAL", // 601 (859) - "FOREGROUND-COLOR", // 602 (860) - "FOREVER", // 603 (861) - "FULL", // 604 (862) - "HIGHLIGHT", // 605 (863) - "HIGH-ORDER-LEFT", // 606 (864) - "HIGH-ORDER-RIGHT", // 607 (865) - "IGNORING", // 608 (866) - "IMPLEMENTS", // 609 (867) - "INITIALIZED", // 610 (868) - "INTERMEDIATE", // 611 (869) - "LC-ALL", // 612 (870) - "LC-COLLATE", // 613 (871) - "LC-CTYPE", // 614 (872) - "LC-MESSAGES", // 615 (873) - "LC-MONETARY", // 616 (874) - "LC-NUMERIC", // 617 (875) - "LC-TIME", // 618 (876) - "LOWLIGHT", // 619 (877) - "NEAREST-AWAY-FROM-ZERO", // 620 (878) - "NEAREST-EVEN", // 621 (879) - "NEAREST-TOWARD-ZERO", // 622 (880) - "NONE", // 623 (881) - "NORMAL", // 624 (882) - "NUMBERS", // 625 (883) - "PREFIXED", // 626 (884) - "PREVIOUS", // 627 (885) - "PROHIBITED", // 628 (886) - "RELATION", // 629 (887) - "REQUIRED", // 630 (888) - "REVERSE-VIDEO", // 631 (889) - "ROUNDING", // 632 (890) - "SECONDS", // 633 (891) - "SECURE", // 634 (892) - "SHORT", // 635 (893) - "SIGNED", // 636 (894) - "STANDARD-BINARY", // 637 (895) - "STANDARD-DECIMAL", // 638 (896) - "STATEMENT", // 639 (897) - "STEP", // 640 (898) - "STRUCTURE", // 641 (899) - "TOWARD-GREATER", // 642 (900) - "TOWARD-LESSER", // 643 (901) - "TRUNCATION", // 644 (902) - "UCS-4", // 645 (903) - "UNDERLINE", // 646 (904) - "UNSIGNED", // 647 (905) - "UTF-16", // 648 (906) - "UTF-8", // 649 (907) - "ADDRESS", // 650 (908) - "END-ACCEPT", // 651 (909) - "END-ADD", // 652 (910) - "END-CALL", // 653 (911) - "END-COMPUTE", // 654 (912) - "END-DELETE", // 655 (913) - "END-DISPLAY", // 656 (914) - "END-DIVIDE", // 657 (915) - "END-EVALUATE", // 658 (916) - "END-MULTIPLY", // 659 (917) - "END-PERFORM", // 660 (918) - "END-READ", // 661 (919) - "END-RETURN", // 662 (920) - "END-REWRITE", // 663 (921) - "END-SEARCH", // 664 (922) - "END-START", // 665 (923) - "END-STRING", // 666 (924) - "END-SUBTRACT", // 667 (925) - "END-UNSTRING", // 668 (926) - "END-WRITE", // 669 (927) - "END-IF", // 670 (928) - "THRU", // 671 (929) - "OR", // 672 (930) - "AND", // 673 (931) - "NOT", // 674 (932) - "NE", // 675 (933) - "LE", // 676 (934) - "GE", // 677 (935) - "POW", // 678 (936) - "NEG", // 679 (937) + "IDENTIFICATION", // 0 (258) + "ENVIRONMENT", // 1 (259) + "PROCEDURE", // 2 (260) + "DATA", // 3 (261) + "FILE", // 4 (262) + "INPUT-OUTPUT", // 5 (263) + "LINKAGE", // 6 (264) + "LOCAL-STORAGE", // 7 (265) + "WORKING-STORAGE", // 8 (266) + "OBJECT-COMPUTER", // 9 (267) + "DISPLAY-OF", // 10 (268) + "END-FUNCTION", // 11 (269) + "END-PROGRAM", // 12 (270) + "END-SUBPROGRAM", // 13 (271) + "JUSTIFIED", // 14 (272) + "RETURNING", // 15 (273) + "NO-CONDITION", // 16 (274) + "ALNUM", // 17 (275) + "ALPHED", // 18 (276) + "ERROR", // 19 (277) + "EXCEPTION", // 20 (278) + "SIZE-ERROR", // 21 (279) + "EXCEPTION-NAME", // 22 (280) + "LEVEL", // 23 (281) + "LEVEL66", // 24 (282) + "LEVEL78", // 25 (283) + "LEVEL88", // 26 (284) + "CLASS-NAME", // 27 (285) + "NAME", // 28 (286) + "NAME88", // 29 (287) + "NUME", // 30 (288) + "NUMED", // 31 (289) + "NUMED-CR", // 32 (290) + "NUMED-DB", // 33 (291) + "NINEDOT", // 34 (292) + "NINES", // 35 (293) + "NINEV", // 36 (294) + "PIC-P", // 37 (295) + "ONES", // 38 (296) + "SPACES", // 39 (297) + "EQ", // 40 (298) + "LITERAL", // 41 (299) + "END", // 42 (300) + "EOP", // 43 (301) + "FILENAME", // 44 (302) + "INVALID", // 45 (303) + "NUMBER", // 46 (304) + "NEGATIVE", // 47 (305) + "NUMSTR", // 48 (306) + "OVERFLOW", // 49 (307) + "BINARY-INTEGER", // 50 (308) + "COMPUTATIONAL", // 51 (309) + "PERFORM", // 52 (310) + "BACKWARD", // 53 (311) + "POSITIVE", // 54 (312) + "POINTER", // 55 (313) + "SECTION", // 56 (314) + "STANDARD-ALPHABET", // 57 (315) + "SWITCH", // 58 (316) + "UPSI", // 59 (317) + "ZERO", // 60 (318) + "SYSIN", // 61 (319) + "SYSIPT", // 62 (320) + "SYSOUT", // 63 (321) + "SYSLIST", // 64 (322) + "SYSLST", // 65 (323) + "SYSPUNCH", // 66 (324) + "SYSPCH", // 67 (325) + "CONSOLE", // 68 (326) + "C01", // 69 (327) + "C02", // 70 (328) + "C03", // 71 (329) + "C04", // 72 (330) + "C05", // 73 (331) + "C06", // 74 (332) + "C07", // 75 (333) + "C08", // 76 (334) + "C09", // 77 (335) + "C10", // 78 (336) + "C11", // 79 (337) + "C12", // 80 (338) + "CSP", // 81 (339) + "S01", // 82 (340) + "S02", // 83 (341) + "S03", // 84 (342) + "S04", // 85 (343) + "S05", // 86 (344) + "AFP-5A", // 87 (345) + "STDIN", // 88 (346) + "STDOUT", // 89 (347) + "STDERR", // 90 (348) + "LIST", // 91 (349) + "MAP", // 92 (350) + "NOLIST", // 93 (351) + "NOMAP", // 94 (352) + "NOSOURCE", // 95 (353) + "MIGHT-BE", // 96 (354) + "FUNCTION-UDF", // 97 (355) + "FUNCTION-UDF-0", // 98 (356) + "DEFAULT", // 99 (357) + "DATE-FMT", // 100 (358) + "TIME-FMT", // 101 (359) + "DATETIME-FMT", // 102 (360) + "BASIS", // 103 (361) + "CBL", // 104 (362) + "CONSTANT", // 105 (363) + "COPY", // 106 (364) + "DEFINED", // 107 (365) + "ENTER", // 108 (366) + "FEATURE", // 109 (367) + "INSERTT", // 110 (368) + "LSUB", // 111 (369) + "PARAMETER", // 112 (370) + "OVERRIDE", // 113 (371) + "READY", // 114 (372) + "RESET", // 115 (373) + "RSUB", // 116 (374) + "SERVICE-RELOAD", // 117 (375) + "STAR-CBL", // 118 (376) + "SUBSCRIPT", // 119 (377) + "SUPPRESS", // 120 (378) + "TITLE", // 121 (379) + "TRACE", // 122 (380) + "USE", // 123 (381) + "COBOL-WORDS", // 124 (382) + "EQUATE", // 125 (383) + "UNDEFINE", // 126 (384) + "CDF-DEFINE", // 127 (385) + "CDF-DISPLAY", // 128 (386) + "CDF-IF", // 129 (387) + "CDF-ELSE", // 130 (388) + "CDF-END-IF", // 131 (389) + "CDF-EVALUATE", // 132 (390) + "CDF-WHEN", // 133 (391) + "CDF-END-EVALUATE", // 134 (392) + "CALL-CONVENTION", // 135 (393) + "CALL-COBOL", // 136 (394) + "CALL-VERBATIM", // 137 (395) + "CDF-PUSH", // 138 (396) + "CDF-POP", // 139 (397) + "SOURCE-FORMAT", // 140 (398) + "IF", // 141 (399) + "THEN", // 142 (400) + "ELSE", // 143 (401) + "SENTENCE", // 144 (402) + "ACCEPT", // 145 (403) + "ADD", // 146 (404) + "ALTER", // 147 (405) + "CALL", // 148 (406) + "CANCEL", // 149 (407) + "CLOSE", // 150 (408) + "COMPUTE", // 151 (409) + "CONTINUE", // 152 (410) + "DELETE", // 153 (411) + "DISPLAY", // 154 (412) + "DIVIDE", // 155 (413) + "EVALUATE", // 156 (414) + "EXIT", // 157 (415) + "FILLER", // 158 (416) + "GOBACK", // 159 (417) + "GOTO", // 160 (418) + "INITIALIZE", // 161 (419) + "INSPECT", // 162 (420) + "MERGE", // 163 (421) + "MOVE", // 164 (422) + "MULTIPLY", // 165 (423) + "OPEN", // 166 (424) + "PARAGRAPH", // 167 (425) + "READ", // 168 (426) + "RELEASE", // 169 (427) + "RETURN", // 170 (428) + "REWRITE", // 171 (429) + "SEARCH", // 172 (430) + "SET", // 173 (431) + "SELECT", // 174 (432) + "SORT", // 175 (433) + "SORT-MERGE", // 176 (434) + "STRING", // 177 (435) + "STOP", // 178 (436) + "SUBTRACT", // 179 (437) + "START", // 180 (438) + "UNSTRING", // 181 (439) + "WRITE", // 182 (440) + "WHEN", // 183 (441) + "ARGUMENT-NUMBER", // 184 (442) + "ARGUMENT-VALUE", // 185 (443) + "ENVIRONMENT-NAME", // 186 (444) + "ENVIRONMENT-VALUE", // 187 (445) + "ABS", // 188 (446) + "ACCESS", // 189 (447) + "ACOS", // 190 (448) + "ACTUAL", // 191 (449) + "ADVANCING", // 192 (450) + "AFTER", // 193 (451) + "ALL", // 194 (452) + "ALLOCATE", // 195 (453) + "ALPHABET", // 196 (454) + "ALPHABETIC", // 197 (455) + "ALPHABETIC-LOWER", // 198 (456) + "ALPHABETIC-UPPER", // 199 (457) + "ALPHANUMERIC", // 200 (458) + "ALPHANUMERIC-EDITED", // 201 (459) + "ALSO", // 202 (460) + "ALTERNATE", // 203 (461) + "ANNUITY", // 204 (462) + "ANUM", // 205 (463) + "ANY", // 206 (464) + "ANYCASE", // 207 (465) + "APPLY", // 208 (466) + "ARE", // 209 (467) + "AREA", // 210 (468) + "AREAS", // 211 (469) + "AS", // 212 (470) + "ASCENDING", // 213 (471) + "ACTIVATING", // 214 (472) + "ASIN", // 215 (473) + "ASSIGN", // 216 (474) + "AT", // 217 (475) + "ATAN", // 218 (476) + "BASED", // 219 (477) + "BASECONVERT", // 220 (478) + "BEFORE", // 221 (479) + "BINARY", // 222 (480) + "BIT", // 223 (481) + "BIT-OF", // 224 (482) + "BIT-TO-CHAR", // 225 (483) + "BLANK", // 226 (484) + "BLOCK", // 227 (485) + "BOOLEAN-OF-INTEGER", // 228 (486) + "BOTTOM", // 229 (487) + "BY", // 230 (488) + "BYTE", // 231 (489) + "BYTE-LENGTH", // 232 (490) + "CF", // 233 (491) + "CH", // 234 (492) + "CHANGED", // 235 (493) + "CHAR", // 236 (494) + "CHAR-NATIONAL", // 237 (495) + "CHARACTER", // 238 (496) + "CHARACTERS", // 239 (497) + "CHECKING", // 240 (498) + "CLASS", // 241 (499) + "COBOL", // 242 (500) + "CODE", // 243 (501) + "CODE-SET", // 244 (502) + "COLLATING", // 245 (503) + "COLUMN", // 246 (504) + "COMBINED-DATETIME", // 247 (505) + "COMMA", // 248 (506) + "COMMAND-LINE", // 249 (507) + "COMMAND-LINE-COUNT", // 250 (508) + "COMMIT", // 251 (509) + "COMMON", // 252 (510) + "CONCAT", // 253 (511) + "CONDITION", // 254 (512) + "CONFIGURATION", // 255 (513) + "CONTAINS", // 256 (514) + "CONTENT", // 257 (515) + "CONTROL", // 258 (516) + "CONTROLS", // 259 (517) + "CONVERT", // 260 (518) + "CONVERTING", // 261 (519) + "CORRESPONDING", // 262 (520) + "COS", // 263 (521) + "COUNT", // 264 (522) + "CURRENCY", // 265 (523) + "CURRENT", // 266 (524) + "CURRENT-DATE", // 267 (525) + "DATA", // 268 (526) + "DATE", // 269 (527) + "DATE-COMPILED", // 270 (528) + "DATE-OF-INTEGER", // 271 (529) + "DATE-TO-YYYYMMDD", // 272 (530) + "DATE-WRITTEN", // 273 (531) + "DAY", // 274 (532) + "DAY-OF-INTEGER", // 275 (533) + "DAY-OF-WEEK", // 276 (534) + "DAY-TO-YYYYDDD", // 277 (535) + "DBCS", // 278 (536) + "DE", // 279 (537) + "DEBUGGING", // 280 (538) + "DECIMAL-POINT", // 281 (539) + "DECLARATIVES", // 282 (540) + "DELIMITED", // 283 (541) + "DELIMITER", // 284 (542) + "DEPENDING", // 285 (543) + "DESCENDING", // 286 (544) + "DETAIL", // 287 (545) + "DIRECT", // 288 (546) + "DIRECT-ACCESS", // 289 (547) + "DOWN", // 290 (548) + "DUPLICATES", // 291 (549) + "DYNAMIC", // 292 (550) + "E", // 293 (551) + "EBCDIC", // 294 (552) + "EC", // 295 (553) + "EGCS", // 296 (554) + "ENCODING", // 297 (555) + "ENTRY", // 298 (556) + "ENVIRONMENT", // 299 (557) + "EVERY", // 300 (558) + "EXAMINE", // 301 (559) + "EXHIBIT", // 302 (560) + "EXP", // 303 (561) + "EXP10", // 304 (562) + "EXTEND", // 305 (563) + "EXTERNAL", // 306 (564) + "EXCEPTION-FILE", // 307 (565) + "EXCEPTION-FILE-N", // 308 (566) + "EXCEPTION-LOCATION", // 309 (567) + "EXCEPTION-LOCATION-N", // 310 (568) + "EXCEPTION-STATEMENT", // 311 (569) + "EXCEPTION-STATUS", // 312 (570) + "FACTORIAL", // 313 (571) + "FALSE", // 314 (572) + "FD", // 315 (573) + "FILE-CONTROL", // 316 (574) + "FILE", // 317 (575) + "FILE-LIMIT", // 318 (576) + "FINAL", // 319 (577) + "FINALLY", // 320 (578) + "FIND-STRING", // 321 (579) + "FIRST", // 322 (580) + "FIXED", // 323 (581) + "FOOTING", // 324 (582) + "FOR", // 325 (583) + "FORMATTED-CURRENT-DATE", // 326 (584) + "FORMATTED-DATE", // 327 (585) + "FORMATTED-DATETIME", // 328 (586) + "FORMATTED-TIME", // 329 (587) + "FORM-OVERFLOW", // 330 (588) + "FREE", // 331 (589) + "FRACTION-PART", // 332 (590) + "FROM", // 333 (591) + "FUNCTION", // 334 (592) + "GENERATE", // 335 (593) + "GIVING", // 336 (594) + "GLOBAL", // 337 (595) + "GO", // 338 (596) + "GROUP", // 339 (597) + "HEADING", // 340 (598) + "HEX", // 341 (599) + "HEX-OF", // 342 (600) + "HEX-TO-CHAR", // 343 (601) + "HIGH-VALUES", // 344 (602) + "HIGHEST-ALGEBRAIC", // 345 (603) + "HOLD", // 346 (604) + "IBM-360", // 347 (605) + "IN", // 348 (606) + "INCLUDE", // 349 (607) + "INDEX", // 350 (608) + "INDEXED", // 351 (609) + "INDICATE", // 352 (610) + "INITIAL", // 353 (611) + "INITIATE", // 354 (612) + "INPUT", // 355 (613) + "INSTALLATION", // 356 (614) + "INTERFACE", // 357 (615) + "INTEGER", // 358 (616) + "INTEGER-OF-BOOLEAN", // 359 (617) + "INTEGER-OF-DATE", // 360 (618) + "INTEGER-OF-DAY", // 361 (619) + "INTEGER-OF-FORMATTED-DATE", // 362 (620) + "INTEGER-PART", // 363 (621) + "INTO", // 364 (622) + "INTRINSIC", // 365 (623) + "INVOKE", // 366 (624) + "I-O", // 367 (625) + "I-O-CONTROL", // 368 (626) + "IS", // 369 (627) + "ISNT", // 370 (628) + "KANJI", // 371 (629) + "KEY", // 372 (630) + "LABEL", // 373 (631) + "LAST", // 374 (632) + "LEADING", // 375 (633) + "LEFT", // 376 (634) + "LENGTH", // 377 (635) + "LENGTH-OF", // 378 (636) + "LIMIT", // 379 (637) + "LIMITS", // 380 (638) + "LINE", // 381 (639) + "LINES", // 382 (640) + "LINE-COUNTER", // 383 (641) + "LINAGE", // 384 (642) + "LINKAGE", // 385 (643) + "LOCALE", // 386 (644) + "LOCALE-COMPARE", // 387 (645) + "LOCALE-DATE", // 388 (646) + "LOCALE-TIME", // 389 (647) + "LOCALE-TIME-FROM-SECONDS", // 390 (648) + "LOCAL-STORAGE", // 391 (649) + "LOCATION", // 392 (650) + "LOCK", // 393 (651) + "LOCK-ON", // 394 (652) + "LOG", // 395 (653) + "LOG10", // 396 (654) + "LOWER-CASE", // 397 (655) + "LOW-VALUES", // 398 (656) + "LOWEST-ALGEBRAIC", // 399 (657) + "LPAREN", // 400 (658) + "MANUAL", // 401 (659) + "MAXX", // 402 (660) + "MEAN", // 403 (661) + "MEDIAN", // 404 (662) + "MIDRANGE", // 405 (663) + "MINN", // 406 (664) + "MULTIPLE", // 407 (665) + "MOD", // 408 (666) + "MODE", // 409 (667) + "MODULE-NAME", // 410 (668) + "NAMED", // 411 (669) + "NAT", // 412 (670) + "NATIONAL", // 413 (671) + "NATIONAL-EDITED", // 414 (672) + "NATIONAL-OF", // 415 (673) + "NATIVE", // 416 (674) + "NESTED", // 417 (675) + "NEXT", // 418 (676) + "NO", // 419 (677) + "NOTE", // 420 (678) + "NULLS", // 421 (679) + "NULLPTR", // 422 (680) + "NUMERIC", // 423 (681) + "NUMERIC-EDITED", // 424 (682) + "NUMVAL", // 425 (683) + "NUMVAL-C", // 426 (684) + "NUMVAL-F", // 427 (685) + "OCCURS", // 428 (686) + "OF", // 429 (687) + "OFF", // 430 (688) + "OMITTED", // 431 (689) + "ON", // 432 (690) + "ONLY", // 433 (691) + "OPTIONAL", // 434 (692) + "OPTIONS", // 435 (693) + "ORD", // 436 (694) + "ORDER", // 437 (695) + "ORD-MAX", // 438 (696) + "ORD-MIN", // 439 (697) + "ORGANIZATION", // 440 (698) + "OTHER", // 441 (699) + "OTHERWISE", // 442 (700) + "OUTPUT", // 443 (701) + "PACKED-DECIMAL", // 444 (702) + "PADDING", // 445 (703) + "PAGE", // 446 (704) + "PAGE-COUNTER", // 447 (705) + "PF", // 448 (706) + "PH", // 449 (707) + "PI", // 450 (708) + "PIC", // 451 (709) + "PICTURE", // 452 (710) + "PLUS", // 453 (711) + "PRESENT-VALUE", // 454 (712) + "PRINT-SWITCH", // 455 (713) + "PROCEDURE", // 456 (714) + "PROCEDURES", // 457 (715) + "PROCEED", // 458 (716) + "PROCESS", // 459 (717) + "PROCESSING", // 460 (718) + "PROGRAM-ID", // 461 (719) + "PROGRAM", // 462 (720) + "PROPERTY", // 463 (721) + "PROTOTYPE", // 464 (722) + "PSEUDOTEXT", // 465 (723) + "QUOTES", // 466 (724) + "RANDOM", // 467 (725) + "RANDOM-SEED", // 468 (726) + "RANGE", // 469 (727) + "RAISE", // 470 (728) + "RAISING", // 471 (729) + "RD", // 472 (730) + "RECORD", // 473 (731) + "RECORDING", // 474 (732) + "RECORDS", // 475 (733) + "RECURSIVE", // 476 (734) + "REDEFINES", // 477 (735) + "REEL", // 478 (736) + "REFERENCE", // 479 (737) + "RELATIVE", // 480 (738) + "REM", // 481 (739) + "REMAINDER", // 482 (740) + "REMARKS", // 483 (741) + "REMOVAL", // 484 (742) + "RENAMES", // 485 (743) + "REPLACE", // 486 (744) + "REPLACING", // 487 (745) + "REPORT", // 488 (746) + "REPORTING", // 489 (747) + "REPORTS", // 490 (748) + "REPOSITORY", // 491 (749) + "RERUN", // 492 (750) + "RESERVE", // 493 (751) + "RESTRICTED", // 494 (752) + "RESUME", // 495 (753) + "RETRY", // 496 (754) + "REVERSE", // 497 (755) + "REVERSED", // 498 (756) + "REWIND", // 499 (757) + "RF", // 500 (758) + "RH", // 501 (759) + "RIGHT", // 502 (760) + "ROUNDED", // 503 (761) + "RUN", // 504 (762) + "SAME", // 505 (763) + "SCREEN", // 506 (764) + "SD", // 507 (765) + "SECONDS-FROM-FORMATTED-TIME", // 508 (766) + "SECONDS-PAST-MIDNIGHT", // 509 (767) + "SECURITY", // 510 (768) + "SEPARATE", // 511 (769) + "SEQUENCE", // 512 (770) + "SEQUENTIAL", // 513 (771) + "SHARING", // 514 (772) + "SIMPLE-EXIT", // 515 (773) + "SIGN", // 516 (774) + "SIN", // 517 (775) + "SIZE", // 518 (776) + "SMALLEST-ALGEBRAIC", // 519 (777) + "SOURCE", // 520 (778) + "SOURCE-COMPUTER", // 521 (779) + "SPECIAL-NAMES", // 522 (780) + "SQRT", // 523 (781) + "STACK", // 524 (782) + "STANDARD", // 525 (783) + "STANDARD-1", // 526 (784) + "STANDARD-DEVIATION", // 527 (785) + "STANDARD-COMPARE", // 528 (786) + "STATUS", // 529 (787) + "STRONG", // 530 (788) + "SUBSTITUTE", // 531 (789) + "SUM", // 532 (790) + "SYMBOL", // 533 (791) + "SYMBOLIC", // 534 (792) + "SYNCHRONIZED", // 535 (793) + "TALLYING", // 536 (794) + "TAN", // 537 (795) + "TERMINATE", // 538 (796) + "TEST", // 539 (797) + "TEST-DATE-YYYYMMDD", // 540 (798) + "TEST-DAY-YYYYDDD", // 541 (799) + "TEST-FORMATTED-DATETIME", // 542 (800) + "TEST-NUMVAL", // 543 (801) + "TEST-NUMVAL-C", // 544 (802) + "TEST-NUMVAL-F", // 545 (803) + "THAN", // 546 (804) + "TIME", // 547 (805) + "TIMES", // 548 (806) + "TO", // 549 (807) + "TOP", // 550 (808) + "TOP-LEVEL", // 551 (809) + "TRACKS", // 552 (810) + "TRACK-AREA", // 553 (811) + "TRAILING", // 554 (812) + "TRANSFORM", // 555 (813) + "TRIM", // 556 (814) + "TRUE", // 557 (815) + "TRY", // 558 (816) + "TURN", // 559 (817) + "TYPE", // 560 (818) + "TYPEDEF", // 561 (819) + "ULENGTH", // 562 (820) + "UNBOUNDED", // 563 (821) + "UNIT", // 564 (822) + "UNITS", // 565 (823) + "UNIT-RECORD", // 566 (824) + "UNTIL", // 567 (825) + "UP", // 568 (826) + "UPON", // 569 (827) + "UPOS", // 570 (828) + "UPPER-CASE", // 571 (829) + "USAGE", // 572 (830) + "USING", // 573 (831) + "USUBSTR", // 574 (832) + "USUPPLEMENTARY", // 575 (833) + "UTILITY", // 576 (834) + "UUID4", // 577 (835) + "UVALID", // 578 (836) + "UWIDTH", // 579 (837) + "VALIDATING", // 580 (838) + "VALUE", // 581 (839) + "VARIANCE", // 582 (840) + "VARYING", // 583 (841) + "VOLATILE", // 584 (842) + "WHEN-COMPILED", // 585 (843) + "WITH", // 586 (844) + "WORKING-STORAGE", // 587 (845) + "YEAR-TO-YYYY", // 588 (846) + "YYYYDDD", // 589 (847) + "YYYYMMDD", // 590 (848) + "ARITHMETIC", // 591 (849) + "ATTRIBUTE", // 592 (850) + "AUTO", // 593 (851) + "AUTOMATIC", // 594 (852) + "AWAY-FROM-ZERO", // 595 (853) + "BACKGROUND-COLOR", // 596 (854) + "BELL", // 597 (855) + "BINARY-ENCODING", // 598 (856) + "BLINK", // 599 (857) + "CAPACITY", // 600 (858) + "CENTER", // 601 (859) + "CLASSIFICATION", // 602 (860) + "CYCLE", // 603 (861) + "DECIMAL-ENCODING", // 604 (862) + "ENTRY-CONVENTION", // 605 (863) + "EOL", // 606 (864) + "EOS", // 607 (865) + "ERASE", // 608 (866) + "EXPANDS", // 609 (867) + "FLOAT-BINARY", // 610 (868) + "FLOAT-DECIMAL", // 611 (869) + "FOREGROUND-COLOR", // 612 (870) + "FOREVER", // 613 (871) + "FULL", // 614 (872) + "HIGHLIGHT", // 615 (873) + "HIGH-ORDER-LEFT", // 616 (874) + "HIGH-ORDER-RIGHT", // 617 (875) + "IGNORING", // 618 (876) + "IMPLEMENTS", // 619 (877) + "INITIALIZED", // 620 (878) + "INTERMEDIATE", // 621 (879) + "LC-ALL", // 622 (880) + "LC-COLLATE", // 623 (881) + "LC-CTYPE", // 624 (882) + "LC-MESSAGES", // 625 (883) + "LC-MONETARY", // 626 (884) + "LC-NUMERIC", // 627 (885) + "LC-TIME", // 628 (886) + "LOWLIGHT", // 629 (887) + "NEAREST-AWAY-FROM-ZERO", // 630 (888) + "NEAREST-EVEN", // 631 (889) + "NEAREST-TOWARD-ZERO", // 632 (890) + "NONE", // 633 (891) + "NORMAL", // 634 (892) + "NUMBERS", // 635 (893) + "PREFIXED", // 636 (894) + "PREVIOUS", // 637 (895) + "PROHIBITED", // 638 (896) + "RELATION", // 639 (897) + "REQUIRED", // 640 (898) + "REVERSE-VIDEO", // 641 (899) + "ROUNDING", // 642 (900) + "SECONDS", // 643 (901) + "SECURE", // 644 (902) + "SHORT", // 645 (903) + "SIGNED", // 646 (904) + "STANDARD-BINARY", // 647 (905) + "STANDARD-DECIMAL", // 648 (906) + "STATEMENT", // 649 (907) + "STEP", // 650 (908) + "STRUCTURE", // 651 (909) + "TOWARD-GREATER", // 652 (910) + "TOWARD-LESSER", // 653 (911) + "TRUNCATION", // 654 (912) + "UCS-4", // 655 (913) + "UNDERLINE", // 656 (914) + "UNSIGNED", // 657 (915) + "UTF-16", // 658 (916) + "UTF-8", // 659 (917) + "XMLGENERATE", // 660 (918) + "XMLPARSE", // 661 (919) + "ADDRESS", // 662 (920) + "END-ACCEPT", // 663 (921) + "END-ADD", // 664 (922) + "END-CALL", // 665 (923) + "END-COMPUTE", // 666 (924) + "END-DELETE", // 667 (925) + "END-DISPLAY", // 668 (926) + "END-DIVIDE", // 669 (927) + "END-EVALUATE", // 670 (928) + "END-MULTIPLY", // 671 (929) + "END-PERFORM", // 672 (930) + "END-READ", // 673 (931) + "END-RETURN", // 674 (932) + "END-REWRITE", // 675 (933) + "END-SEARCH", // 676 (934) + "END-START", // 677 (935) + "END-STRING", // 678 (936) + "END-SUBTRACT", // 679 (937) + "END-UNSTRING", // 680 (938) + "END-WRITE", // 681 (939) + "END-XML", // 682 (940) + "END-IF", // 683 (941) + "ATTRIBUTES", // 684 (942) + "ELEMENT", // 685 (943) + "NAMESPACE", // 686 (944) + "NAMESPACE-PREFIX", // 687 (945) + "NONNUMERIC", // 689 (947) + "XML-DECLARATION", // 690 (948) + "THRU", // 692 (950) + "OR", // 693 (951) + "AND", // 694 (952) + "NOT", // 695 (953) + "NE", // 696 (954) + "LE", // 697 (955) + "GE", // 698 (956) + "POW", // 699 (957) + "NEG", // 700 (958) }; diff --git a/gcc/cobol/udf/stored-char-length.cbl b/gcc/cobol/udf/stored-char-length.cbl index 9ab3b14..66889d0 100644 --- a/gcc/cobol/udf/stored-char-length.cbl +++ b/gcc/cobol/udf/stored-char-length.cbl @@ -1,3 +1,6 @@ + >> PUSH source format + >>SOURCE format is fixed + * This function is in public domain. * Contributed by James K. Lowden of Cobolworx in August 2024 @@ -13,3 +16,4 @@ to Output-Value. End Function STORED-CHAR-LENGTH. + >> pop source format
\ No newline at end of file diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc index 101a0a0..32add9f 100644 --- a/gcc/cobol/util.cc +++ b/gcc/cobol/util.cc @@ -34,34 +34,34 @@ * header files. */ -#include "cobol-system.h" +#include <cobol-system.h> +#include <coretypes.h> +#include <tree.h> +#undef yy_flex_debug + #include <langinfo.h> -#include "coretypes.h" -#include "version.h" -#include "demangle.h" -#include "intl.h" -#include "backtrace.h" -#include "diagnostic.h" -#include "diagnostic-color.h" -#include "diagnostic-url.h" -#include "diagnostic-metadata.h" -#include "diagnostic-path.h" -#include "edit-context.h" -#include "selftest.h" -#include "selftest-diagnostic.h" -#include "opts.h" +#include <coretypes.h> +#include <version.h> +#include <demangle.h> +#include <intl.h> +#include <backtrace.h> +#include <diagnostic.h> +#include <opts.h> #include "util.h" + #include "cbldiag.h" +#include "cdfval.h" #include "lexio.h" -#define HOWEVER_GCC_DEFINES_TREE #include "../../libgcobol/ec.h" #include "../../libgcobol/common-defs.h" #include "symbols.h" #include "inspect.h" #include "../../libgcobol/io.h" #include "genapi.h" +#include "genutil.h" +#include "../../libgcobol/charmaps.h" #pragma GCC diagnostic ignored "-Wunused-result" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" @@ -86,25 +86,233 @@ static inline char * get_current_dir_name () { /* Use libiberty's allocator here. */ - char *buf = (char *) xmalloc (PATH_MAX); + char *buf = static_cast<char *>(xmalloc (PATH_MAX)); return getcwd (buf, PATH_MAX); } #endif +/* + * For printing messages, usually the size of the thing is some kind of string + * length, and doesn't really need a size_t. For message formatting, use a + * simple unsigned long, and warn if that's no good. "gb4" here stands for + * "4 Gigabytes". + */ +unsigned long +gb4( size_t input ) { + if( input != static_cast<unsigned long>(input) ) { + dbgmsg("size too large to print: %lx:%lx", + (unsigned long)(input >> (4 * sizeof(unsigned long))), + static_cast<unsigned long>(input)); + } + return input; +} + +/* + * Most CDF Directives -- those that have state -- can be pushed and popped. + * This class maintains stacks of them, with each stack having a "default + * value" that may be updated, without push/pop, via a CDF directive or + * command-line option. A push to a stack pushes the default value onto it; a + * pop copies the top of the stack to the default value. + * + * Supported: + * CALL-CONVENTION + * COBOL-WORDS + * DEFINE + * DISPLAY + * IF + * POP + * PUSH + * SOURCE FORMAT + * TURN + * not supported + * EVALUATE + * FLAG-02 + * FLAG-14 + * LEAP-SECOND + * LISTING + * PAGE + * PROPAGATE + * REF-MOD-ZERO-LENGTH + * + * >>PUSH ALL calls the class's push() method. + * >>POP ALL calls the class's pop() method. + */ +class cdf_directives_t +{ + template <typename T> + class cdf_stack_t : private std::stack<T> { // cppcheck-suppress noConstructor + T default_value; + const T& top() const { return std::stack<T>::top(); } + bool empty() const { return std::stack<T>::empty(); } + public: + void value( const T& value ) { + T& output( empty()? default_value : std::stack<T>::top() ); // cppcheck-suppress constVariableReference + output = value; + dbgmsg("cdf_directives_t::%s: %s", __func__, str(output).c_str()); + } + T& value() { + return empty()? default_value : std::stack<T>::top(); + } + void push() { + std::stack<T>::push(value()); + dbgmsg("cdf_directives_t::%s: %s", __func__, str(top()).c_str()); + } + void pop() { + if( empty() ) { + error_msg(YYLTYPE(), "CDF stack empty"); // cppcheck-suppress syntaxError + return; + } + default_value = top(); + std::stack<T>::pop(); + dbgmsg("cdf_directives_t::%s: %s", __func__, str(default_value).c_str()); + } + protected: + static std::string str(cbl_call_convention_t arg) { + char output[2] = { static_cast<char>(arg) }; + return std::string("call-convention ") + output; + } + static std::string str(current_tokens_t) { + return "<cobol-words>"; + } + static std::string str(cdf_values_t) { + return "<dictionary>"; + } + static std::string str(source_format_t arg) { + return arg.description(); + } + static std::string str(cbl_enabled_exceptions_t) { + return "<enabled_exceptions>"; + } + }; + + public: + cdf_stack_t<cbl_call_convention_t> call_convention; + cdf_stack_t<current_tokens_t> cobol_words; + cdf_stack_t<cdf_values_t> dictionary; // DEFINE + cdf_stack_t<source_format_t> source_format; + cdf_stack_t<cbl_enabled_exceptions_t> enabled_exceptions; + + cdf_directives_t() { + call_convention.value() = cbl_call_cobol_e; + } + + void push() { + call_convention.push(); + cobol_words.push(); + dictionary.push(); + source_format.push(); + enabled_exceptions.push(); + } + void pop() { + call_convention.pop(); + cobol_words.pop(); + dictionary.pop(); + source_format.pop(); + enabled_exceptions.pop(); + } +}; +static cdf_directives_t cdf_directives; + +void +current_call_convention( cbl_call_convention_t convention) { + cdf_directives.call_convention.value(convention); +} +cbl_call_convention_t +current_call_convention() { + return cdf_directives.call_convention.value(); +} + +current_tokens_t& +cdf_current_tokens() { + return cdf_directives.cobol_words.value(); +} + +cdf_values_t& +cdf_dictionary() { + return cdf_directives.dictionary.value(); +} + +void +cobol_set_indicator_column( int column ) { + cdf_directives.source_format.value().indicator_column_set(column); +} +source_format_t& cdf_source_format() { + return cdf_directives.source_format.value(); +} + +cbl_enabled_exceptions_t& +cdf_enabled_exceptions() { + return cdf_directives.enabled_exceptions.value(); +} + +void cdf_push() { cdf_directives.push(); } +void cdf_push_call_convention() { cdf_directives.call_convention.push(); } +void cdf_push_current_tokens() { cdf_directives.cobol_words.push(); } +void cdf_push_dictionary() { cdf_directives.dictionary.push(); } +void cdf_push_enabled_exceptions() { cdf_directives.enabled_exceptions.push(); } +void cdf_push_source_format() { cdf_directives.source_format.push(); } + +void cdf_pop() { cdf_directives.pop(); } +void cdf_pop_call_convention() { cdf_directives.call_convention.pop(); } +void cdf_pop_current_tokens() { cdf_directives.cobol_words.pop(); } +void cdf_pop_dictionary() { cdf_directives.dictionary.pop(); } +void cdf_pop_enabled_exceptions() { cdf_directives.enabled_exceptions.pop(); } +void cdf_pop_source_format() { cdf_directives.source_format.pop(); } + +/* + * Construct a cbl_field_t from a CDF literal, to be installed in the symbol table. + */ +cbl_field_t +cdf_literalize( const std::string& name, const cdfval_t& value ) { + cbl_field_t field; + + if( value.is_numeric() ) { + auto initial = xasprintf("%ld", (long)value.as_number()); + auto len = strlen(initial); + cbl_field_data_t data(len, len); + data.initial = initial; + data.valify(); + field = cbl_field_t{ FldLiteralN, constant_e, data, 0, name.c_str()}; + } else { + auto len = strlen(value.string); + cbl_field_data_t data(len, len); + data.initial = xstrdup(value.string); + field = cbl_field_t{ FldLiteralA, constant_e, data, 0, name.c_str() }; + field.set_attr(quoted_e); + } + field.codeset.set(); + + return field; +} + +const std::list<cbl_field_t> +cdf_literalize() { + std::list<cbl_field_t> fields; + auto dict = cdf_dictionary(); + + for( auto elem : dict ) { + std::string name(elem.first); + const cdfval_t& value(elem.second); + + fields.push_back(cdf_literalize(name, value)); + } + return fields; +} + const char * symbol_type_str( enum symbol_type_t type ) { switch(type) { case SymFilename: return "SymFilename"; - case SymFunction: - return "SymFunction"; case SymField: return "SymField"; case SymLabel: return "SymLabel"; case SymSpecial: return "SymSpecial"; + case SymLocale: + return "SymLocale"; case SymAlphabet: return "SymAlphabet"; case SymFile: @@ -112,7 +320,7 @@ symbol_type_str( enum symbol_type_t type ) case SymDataSection: return "SymDataSection"; } - dbgmsg("%s:%d: invalid symbol_type_t %d", __func__, __LINE__, type); + cbl_internal_error("%s:%d: invalid %<symbol_type_t%> %d", __func__, __LINE__, type); return "???"; } @@ -158,10 +366,8 @@ cbl_field_type_str( enum cbl_field_type_t type ) return "FldSwitch"; case FldPointer: return "FldPointer"; - case FldBlob: - return "FldBlob"; } - dbgmsg("%s:%d: invalid symbol_type_t %d", __func__, __LINE__, type); + cbl_internal_error("%s:%d: invalid %<symbol_type_t%> %d", __func__, __LINE__, type); return "???"; } @@ -321,8 +527,9 @@ is_numeric_edited( const char picture[] ) { break; default: numed_message = xasprintf("invalid PICTURE character " - "'%c' at offset %zu in '%s'", - *p, p - picture, picture); + "'%c' at offset " HOST_SIZE_T_PRINT_UNSIGNED + " in '%s'", + *p, (fmt_size_t)(p - picture), picture); break; } @@ -346,49 +553,50 @@ normalize_picture( char picture[] ) regmatch_t pmatch[4]; if( (erc = regcomp(preg, regex, cflags)) != 0 ) { - regerror(erc, preg, regexmsg, sizeof(regexmsg)); - dbgmsg( "%s:%d: could not compile regex: %s", __func__, __LINE__, regexmsg ); - return picture; + regerror(erc, preg, regexmsg, sizeof(regexmsg)); + dbgmsg( "%s:%d: could not compile regex: %s", __func__, __LINE__, regexmsg ); + return picture; } while( (erc = regexec(preg, picture, COUNT_OF(pmatch), pmatch, 0)) == 0 ) { - assert(pmatch[1].rm_so != -1 && pmatch[1].rm_so < pmatch[1].rm_eo); - size_t len = pmatch[1].rm_eo - pmatch[1].rm_so; - assert(len == 1); - const char *start = picture + pmatch[1].rm_so; - - assert(pmatch[2].rm_so != -2 && pmatch[2].rm_so < pmatch[2].rm_eo); - len = pmatch[2].rm_eo - pmatch[2].rm_so; - assert(len > 0); - - /* - * Overwrite e.g. A(4) with AAAA. - */ - assert(pmatch[2].rm_so == pmatch[1].rm_eo + 1); // character paren number - p = picture + pmatch[2].rm_so; - len = 0; - if( 1 != sscanf(p, "%zu", &len) ) { - dbgmsg("%s:%d: no number found in '%s'", __func__, __LINE__, p); - goto irregular; - } - if( len == 0 ) { - dbgmsg("%s:%d: ZERO length found in '%s'", __func__, __LINE__, p); - goto irregular; - } + assert(pmatch[1].rm_so != -1 && pmatch[1].rm_so < pmatch[1].rm_eo); + size_t len = pmatch[1].rm_eo - pmatch[1].rm_so; + assert(len == 1); + const char *start = picture + pmatch[1].rm_so; + + assert(pmatch[2].rm_so != -2 && pmatch[2].rm_so < pmatch[2].rm_eo); + len = pmatch[2].rm_eo - pmatch[2].rm_so; + assert(len > 0); + + /* + * Overwrite e.g. A(4) with AAAA. + */ + assert(pmatch[2].rm_so == pmatch[1].rm_eo + 1); // character paren number + p = picture + pmatch[2].rm_so; + len = 0; + fmt_size_t lenf = 0; + if( 1 != sscanf(p, "%" GCC_PRISZ "u", &lenf) ) { + dbgmsg("%s:%d: no number found in '%s'", __func__, __LINE__, p); + goto irregular; + } + len = lenf; + if( len == 0 ) { + dbgmsg("%s:%d: ZERO length found in '%s'", __func__, __LINE__, p); + goto irregular; + } - std::vector <char> pic(len + 1, '\0'); - memset(pic.data(), *start, len); - const char *finish = picture + pmatch[2].rm_eo, - *eopicture = picture + strlen(picture); + std::vector <char> pic(len + 1, '\0'); + memset(pic.data(), *start, len); + const char *finish = picture + pmatch[2].rm_eo, + *eopicture = picture + strlen(picture); - p = xasprintf( "%*s%s%*s", - (int)(start - picture), picture, - pic.data(), - (int)(eopicture - finish), finish ); + p = xasprintf( "%*s%s%*s", + (int)(start - picture), picture, + pic.data(), + (int)(eopicture - finish), finish ); - free(picture); - picture = p; - continue; + free(picture); + picture = p; } assert(erc == REG_NOMATCH); @@ -446,7 +654,6 @@ is_elementary( enum cbl_field_type_t type ) case FldForward: case FldIndex: case FldSwitch: - case FldBlob: return false; case FldPointer: case FldAlphanumeric: @@ -459,7 +666,7 @@ is_elementary( enum cbl_field_type_t type ) case FldFloat: return true; // takes up space } - dbgmsg("%s:%d: invalid symbol_type_t %d", __func__, __LINE__, type); + cbl_internal_error("%s:%d: invalid %<symbol_type_t%> %d", __func__, __LINE__, type); return false; } @@ -638,6 +845,7 @@ symbol_field_type_update( cbl_field_t *field, // type matches itself if( field->type == candidate ) { if( is_usage ) field->usage = candidate; + field->codeset.set(); return true; } if( is_usage && field->usage == candidate ) return true; @@ -664,7 +872,6 @@ symbol_field_type_update( cbl_field_t *field, */ if( is_usage ) { switch(field->type) { - case FldBlob: case FldDisplay: gcc_unreachable(); // type is never just "display" break; @@ -715,11 +922,24 @@ symbol_field_type_update( cbl_field_t *field, case FldInvalid: field->type = candidate; field->attr |= numeric_group_attrs(field); + // update encoding + switch( field->type ) { + case FldNumericDisplay: + case FldAlphaEdited: + case FldNumericEdited: + { + bool retval = field->codeset.set(); + return retval; + } + default: + break; + } return true; case FldDisplay: if( is_displayable(candidate) ) { field->type = candidate; field->attr |= numeric_group_attrs(field); + if( ! field->codeset.valid() ) return field->codeset.set(); return true; } break; @@ -730,6 +950,7 @@ symbol_field_type_update( cbl_field_t *field, field->clear_attr(all_x_e); field->type = field->usage; field->attr |= numeric_group_attrs(field); + if( ! field->codeset.valid() ) return field->codeset.set(); return true; case FldNumericDisplay: case FldNumericEdited: @@ -741,7 +962,6 @@ symbol_field_type_update( cbl_field_t *field, case FldForward: case FldSwitch: case FldPointer: - case FldBlob: // invalid usage value gcc_unreachable(); break; @@ -771,7 +991,7 @@ symbol_field_type_update( cbl_field_t *field, bool redefine_field( cbl_field_t *field ) { - cbl_field_t *primary = symbol_redefines(field); + const cbl_field_t *primary = symbol_redefines(field); bool fOK = true; if( !primary ) return false; @@ -819,7 +1039,7 @@ cbl_field_t::report_invalid_initial_value(const YYLTYPE& loc) const { // 8 or more, we need do no further testing because we assume // everything fits. if( data.capacity < 8 ) { - auto p = strchr(data.initial, symbol_decimal_point()); + const char *p = strchr(data.initial, symbol_decimal_point()); if( p && atoll(p+1) != 0 ) { error_msg(loc, "integer type %s VALUE '%s' " "requires integer VALUE", @@ -882,8 +1102,8 @@ cbl_field_t::report_invalid_initial_value(const YYLTYPE& loc) const { return TOUPPER(ch) == 'E'; } ); if( !has_exponent && data.precision() < pend - p ) { - error_msg(loc, "%s cannot represent VALUE '%s' exactly (max .%zu)", - name, data.initial, pend - p); + error_msg(loc, "%s cannot represent VALUE %qs exactly (max %c%ld)", + name, data.initial, '.', (long)(pend - p)); } } } @@ -915,15 +1135,15 @@ cbl_field_t::report_invalid_initial_value(const YYLTYPE& loc) const { // consider all-alphabetic if( has_attr(all_alpha_e) ) { bool alpha_value = fig != zero_value_e; - + if( fig == normal_value_e ) { - alpha_value = std::all_of( data.initial, - data.initial + - strlen(data.initial), - []( char ch ) { - return ISSPACE(ch) || - ISPUNCT(ch) || - ISALPHA(ch); } ); + alpha_value = std::none_of( data.initial, + data.initial + + data.capacity, + []( char ch ) { + return + ISPUNCT(ch) || + ISDIGIT(ch); } ); } if( ! alpha_value ) { error_msg(loc, "alpha-only %s VALUE '%s' contains non-alphabetic data", @@ -941,8 +1161,7 @@ const cbl_field_t * literal_subscript_oob( const cbl_refer_t& r, size_t& isub /* output */) { // Verify literal subscripts if dimensions are correct. size_t ndim(dimensions(r.field)); - if( ndim == 0 || ndim != r.nsubscript ) return NULL; - cbl_refer_t *esub = r.subscripts + r.nsubscript; + if( ndim == 0 || ndim != r.nsubscript() ) return NULL; std::vector<cbl_field_t *> dims( ndim, NULL ); auto pdim = dims.end(); @@ -960,32 +1179,31 @@ literal_subscript_oob( const cbl_refer_t& r, size_t& isub /* output */) { * for the corresponding dimension. Return the first subscript not * meeting those criteria, if any. */ - auto p = std::find_if( r.subscripts, esub, - [&pdim]( const cbl_refer_t& r ) { + auto psub = std::find_if( r.subscripts.begin(), r.subscripts.end(), + [pdim]( const cbl_refer_t& r ) mutable { const auto& occurs((*pdim)->occurs); pdim++; return ! occurs.subscript_ok(r.field); } ); - isub = p - r.subscripts; - return p == esub? NULL : dims[isub]; + isub = psub - r.subscripts.begin(); + return psub == r.subscripts.end()? NULL : dims[isub]; } size_t cbl_refer_t::subscripts_set( const std::list<cbl_refer_t>& subs ) { - nsubscript = subs.size(); - subscripts = new cbl_refer_t[nsubscript]; - std::copy( subs.begin(), subs.end(), subscripts ); - + subscripts.clear(); + std::copy( subs.begin(), subs.end(), std::back_inserter(subscripts) ); return dimensions(field); } const char * cbl_refer_t::str() const { - static char subscripts[64]; - sprintf(subscripts, "(%u of %zu dimensions)", nsubscript, dimensions(field)); + static char subscripts_l[64]; + sprintf(subscripts_l, "(%u of " HOST_SIZE_T_PRINT_UNSIGNED " dimensions)", + nsubscript(), (fmt_size_t)dimensions(field)); char *output = xasprintf("%s %s %s", field? field_str(field) : "(none)", - 0 < dimensions(field)? subscripts : "", + 0 < dimensions(field)? subscripts_l : "", is_refmod_reference()? "(refmod)" : "" ); return output; } @@ -998,18 +1216,18 @@ cbl_refer_t::name() const { const char * cbl_refer_t::deref_str() const { - std::vector<char> dimstr(nsubscript * 16, '\0'); + std::vector<char> dimstr(nsubscript() * 16, '\0'); dimstr.at(0) = '('; auto p = dimstr.begin() + 1; if( !field ) return name(); - for( auto sub = subscripts; sub < subscripts + nsubscript; sub++ ) { - auto initial = sub->field->data.initial ? sub->field->data.initial : "?"; + for( const auto& sub : subscripts ) { + auto initial = sub.field->data.initial ? sub.field->data.initial : "?"; size_t len = dimstr.end() - p; p += snprintf( &*p, len, "%s ", initial ); } - if( 0 < nsubscript ) { + if( ! subscripts.empty() ) { *--p = ')'; } char *output = xasprintf("%s%s", field->name, dimstr.data()); @@ -1028,10 +1246,10 @@ struct move_corresponding_field { tgt.field = cbl_field_of(symbol_at(elem.second)); if( yydebug ) { - dbgmsg("move_corresponding:%d: SRC: %3zu %s", __LINE__, - elem.first, src.str()); - dbgmsg("move_corresponding:%d: to %3zu %s", __LINE__, - elem.second, tgt.str()); + dbgmsg("move_corresponding:%d: SRC: %3" GCC_PRISZ "u %s", __LINE__, + (fmt_size_t)elem.first, src.str()); + dbgmsg("move_corresponding:%d: to %3" GCC_PRISZ "u %s", __LINE__, + (fmt_size_t)elem.second, tgt.str()); } parser_move(tgt, src); @@ -1086,10 +1304,8 @@ valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ) static_assert(sizeof(matrix[0]) == COUNT_OF(matrix[0]), "matrix should be square"); - for( const cbl_field_t *args[] = {tgt, src}, **p=args; - p < args + COUNT_OF(args); p++ ) { - auto& f(**p); - switch(f.type) { + for( auto field : { src, tgt } ) { + switch(field->type) { case FldClass: case FldConditional: case FldIndex: @@ -1099,11 +1315,10 @@ valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ) return false; // parser should not allow the following types here case FldForward: - case FldBlob: default: - if( sizeof(matrix[0]) < f.type ) { + if( sizeof(matrix[0]) < field->type ) { cbl_internal_error("logic error: MOVE %s %s invalid type:", - cbl_field_type_str(f.type), f.name); + cbl_field_type_str(field->type), field->name); } break; } @@ -1129,15 +1344,25 @@ valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ) case 0: if( src->type == FldLiteralA && is_numericish(tgt) && !is_literal(tgt) ) { // Allow if input string is an integer. - const char *p = src->data.initial, *pend = p + src->data.capacity; - if( p[0] == '+' || p[0] == '-' ) p++; + size_t outcount; + char *in_ascii = static_cast<char *>(xmalloc(4 * src->data.capacity)); + const char *in_asciip = __gg__iconverter( src->codeset.encoding, + DEFAULT_SOURCE_ENCODING, + src->data.initial, + src->data.capacity, + &outcount ); + memcpy(in_ascii, in_asciip, outcount); + const char *p = in_ascii, *pend = p + src->data.capacity; + if( (p[0] == ascii_plus) || (p[0] == ascii_minus) ) p++; retval = std::all_of( p, pend, isdigit ); if( yydebug && ! retval ) { auto bad = std::find_if( p, pend, []( char ch ) { return ! ISDIGIT(ch); } ); - dbgmsg("%s:%d: offending character '%c' at position %zu", - __func__, __LINE__, *bad, bad - p); + dbgmsg("%s:%d: offending character '%c' at position " + HOST_SIZE_T_PRINT_UNSIGNED, + __func__, __LINE__, *bad, (fmt_size_t)(bad - p)); } + free(in_ascii); } break; case 1: @@ -1169,12 +1394,6 @@ valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ) } } - if( yydebug && getenv(__func__) ) { - dbgmsg("%s:%d: ok to move %s to %s (0x%02x)", __func__, __LINE__, - cbl_field_type_str(src->type), cbl_field_type_str(tgt->type), - retval); - } - return retval; } @@ -1182,8 +1401,6 @@ bool valid_picture( enum cbl_field_type_t type, const char picture[] ) { switch(type) { - case FldBlob: - gcc_unreachable(); // can't get here via the parser case FldInvalid: case FldGroup: case FldLiteralA: @@ -1228,7 +1445,6 @@ uint32_t type_capacity( enum cbl_field_type_t type, uint32_t digits ) { switch(type) { - case FldBlob: gcc_unreachable(); case FldInvalid: case FldGroup: case FldAlphanumeric: @@ -1366,7 +1582,7 @@ public: { assert(isym); } - procdef_t( const procref_base_t& ref ) + explicit procdef_t( const procref_base_t& ref ) : procref_base_t(ref) , isym(0) {} @@ -1375,13 +1591,6 @@ public: return procref_base_t(*this) < procref_base_t(that); } - bool operator<( const procref_base_t& that ) const { - if( that.has_section() ) { - return procref_base_t(*this) < that; - } - return strcasecmp(paragraph(), that.paragraph()) < 0; - } - cbl_label_t * label_of() const { return isym == 0? NULL : cbl_label_of(symbol_at(isym)); } @@ -1412,7 +1621,7 @@ static procedures_t::iterator current_procedure = programs.end()->second.end(); class procedure_match { const procref_base_t& ref; public: - procedure_match( const procref_base_t& ref ) : ref(ref) {} + explicit procedure_match( const procref_base_t& ref ) : ref(ref) {} // Match a 2-name reference to section & paragraph, else to one or the other. bool operator()( procedures_t::const_reference elem ) { const procdef_t& key = elem.first; @@ -1440,16 +1649,7 @@ locally_unique( size_t program, const procdef_t& key, const procref_t& ref ) { const char *section_name = ref.has_section()? ref.section() : key.section(); procref_base_t full_ref(section_name, ref.paragraph()); - if( getenv(__func__) ) { - dbgmsg("%s: %zu for ref %s of '%s' (line %d) " - "in %s of '%s' (as %s of '%s')", __func__, - procedures.count(full_ref), - ref.paragraph(), ref.section(), ref.line_number(), - key.paragraph(), key.section(), - full_ref.paragraph(), full_ref.section() ); - } - - return 1 == procedures.count(full_ref); + return 1 == procedures.count(procdef_t(full_ref)); } // Add each section and paragraph to the map as it occurs in the Cobol text. @@ -1470,9 +1670,6 @@ procedure_definition_add( size_t program, const cbl_label_t *procedure ) { } procdef_t key( section_name, paragraph_name, isym ); - if( getenv(__func__) ) { - dbgmsg("%s: #%3zu %s of %s", __func__, isym, paragraph_name, section_name); - } current_procedure = programs[program].insert( make_pair(key, procedures_t::mapped_type()) ); } @@ -1482,9 +1679,6 @@ void procedure_reference_add( const char *section, const char *paragraph, int line, size_t context ) { - if( getenv(__func__) ) { - dbgmsg("%s: line %3d %s of %s", __func__, line, paragraph, section); - } current_procedure->second.push_back( procref_t(section, paragraph, line, context) ); } @@ -1515,10 +1709,11 @@ ambiguous_reference( size_t program ) { ambiguous = find_if_not( proc.second.begin(), proc.second.end(), is_unique(program, proc.first) ); if( proc.second.end() != ambiguous ) { - if( yydebug || getenv("symbol_label_add")) { - dbgmsg("%s: %s of '%s' has %zu potential matches", __func__, - ambiguous->paragraph(), ambiguous->section(), - procedures.count(*ambiguous)); + if( yydebug ) { + dbgmsg("%s: %s of '%s' has " HOST_SIZE_T_PRINT_UNSIGNED + "potential matches", __func__, + ambiguous->paragraph(), ambiguous->section(), + (fmt_size_t)procedures.count(procdef_t(*ambiguous))); } return new procref_t(*ambiguous); } @@ -1545,7 +1740,7 @@ intradeclarative_reference() { class next_group { size_t isym; public: - next_group( symbol_elem_t *group ) : isym(symbol_index(group)) {} + explicit next_group( const symbol_elem_t *group ) : isym(symbol_index(group)) {} // return true if elem is not a member of the group bool operator()( const symbol_elem_t& elem ) { @@ -1561,9 +1756,9 @@ parent_names( const symbol_elem_t *elem, if( is_filler(cbl_field_of(elem)) ) return; - // dbgmsg("%s: asked about %s of %s (%zu away)", __func__, + // dbgmsg("%s: asked about %s of %s (" HOST_SIZE_T_PRINT_UNSIGNED " away)", __func__, // cbl_field_of(elem)->name, - // cbl_field_of(group)->name, elem - group); + // cbl_field_of(group)->name, (fmt_size_t)(elem - group)); for( const symbol_elem_t *e=elem; e && group < e; e = symbol_parent(e) ) { names.push_front( cbl_field_of(e)->name ); @@ -1582,15 +1777,17 @@ public: symbol_elem_t *rgroup, type_t type ) : lgroup(lgroup), rgroup(rgroup), type(type) { - dbgmsg( "%s:%d: for #%zu %s and #%zu %s on line %d", __func__, __LINE__, - symbol_index(lgroup), cbl_field_of(lgroup)->name, - symbol_index(rgroup), cbl_field_of(rgroup)->name, yylineno ); + dbgmsg( "%s:%d: for #" HOST_SIZE_T_PRINT_UNSIGNED + " %s and #" HOST_SIZE_T_PRINT_UNSIGNED " %s on line %d", + __func__, __LINE__, + (fmt_size_t)symbol_index(lgroup), cbl_field_of(lgroup)->name, + (fmt_size_t)symbol_index(rgroup), cbl_field_of(rgroup)->name, yylineno ); } static bool any_redefines( const cbl_field_t& field, const symbol_elem_t *group ) { for( const cbl_field_t *f = &field; f && f->parent > 0; f = parent_of(f) ) { - symbol_elem_t *e = symbol_at(f->parent); + const symbol_elem_t *e = symbol_at(f->parent); if( e == group || e->type != SymField ) break; if( symbol_redefines(f) ) return true; } @@ -1661,8 +1858,9 @@ corresponding_fields( cbl_field_t *lhs, cbl_field_t *rhs, lhsg.a = symbols_begin(field_index(lhs)); lhsg.z = std::find_if( lhsg.a, symbols_end(), next_group(lhsg.a) ); - dbgmsg("%s:%d: examining %zu symbols after %s", __func__, __LINE__, - lhsg.z - lhsg.a, lhs->name); + dbgmsg("%s:%d: examining " HOST_SIZE_T_PRINT_UNSIGNED " symbols after %s", + __func__, __LINE__, + (fmt_size_t)(lhsg.z - lhsg.a), lhs->name); find_corresponding finder( symbol_at(field_index(lhs)), symbol_at(field_index(rhs)), type ); @@ -1670,8 +1868,9 @@ corresponding_fields( cbl_field_t *lhs, cbl_field_t *rhs, output.erase(0); - dbgmsg( "%s:%d: %s and %s have %zu corresponding fields", - __func__, __LINE__, lhs->name, rhs->name, output.size() ); + dbgmsg( "%s:%d: %s and %s have " HOST_SIZE_T_PRINT_UNSIGNED + " corresponding fields", + __func__, __LINE__, lhs->name, rhs->name, (fmt_size_t)output.size() ); return output; } @@ -1711,12 +1910,13 @@ date_time_fmt( const char input[] ) { { 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; + int cflags = REG_EXTENDED | REG_ICASE, eflags=0; regmatch_t m[5]; char result = 0; if( ! compiled ) { for( auto& fmt : fmts ) { + int erc; if( (erc = regcomp(&fmt.reg, fmt.pattern, cflags)) != 0 ) { char msg[80]; regerror(erc, &fmt.reg, msg, sizeof(msg)); @@ -1749,11 +1949,10 @@ struct input_file_t { ino_t inode; int lineno; const char *name; - const line_map *lines; input_file_t( const char *name, ino_t inode, - int lineno=1, const line_map *lines = NULL ) - : inode(inode), lineno(lineno), name(name), lines(lines) + int lineno=1 ) + : inode(inode), lineno(lineno), name(name) { if( inode == 0 ) inode_set(); } @@ -1772,14 +1971,29 @@ struct input_file_t { class unique_stack : public std::stack<input_file_t> { + friend void cobol_set_pp_option(int opt); + bool option_m; + std::set<std::string> all_names; + + const char * + no_wd( const char *wd, const char *name ) { + int i; + for( i=0; wd[i] == name[i]; i++ ) i++; + if( wd[i] == '\0' && name[i] == '/' ) i++; + return yydebug? name : name + i; + } + public: + unique_stack() : option_m(false) {} + bool push( const value_type& value ) { auto ok = std::none_of( c.cbegin(), c.cend(), - [value]( auto& that ) { + [value]( const auto& that ) { return value == that; } ); if( ok ) { std::stack<input_file_t>::push(value); + all_names.insert(value.name); return true; } size_t n = c.size(); @@ -1790,21 +2004,39 @@ class unique_stack : public std::stack<input_file_t> "----- ---- --------" "----------------------------------------"); for( const auto& v : c ) { - dbgmsg( " %4zu %4d %s", c.size() - --n, v.lineno, no_wd(wd, v.name) ); + dbgmsg( " %4" GCC_PRISZ "u %4d %s", + (fmt_size_t)(c.size() - --n), v.lineno, no_wd(wd, v.name) ); } } else { - dbgmsg("unable to get current working directory: %m"); + dbgmsg("unable to get current working directory: %s", xstrerror(errno)); } free(wd); } return false; } - const char * - no_wd( const char *wd, const char *name ) { - int i; - for( i=0; wd[i] == name[i]; i++ ) i++; - if( wd[i] == '\0' && name[i] == '/' ) i++; - return yydebug? name : name + i; + + // Look down into the stack. peek(0) == top() + const input_file_t& peek( size_t n ) const { + gcc_assert( n < size() ); + return c.at(size() - ++n); + } + + void option( int opt ) { // capture other preprocessor options eventually + assert(opt == 'M'); + option_m = true; + } + int option() const { + return option_m? 'M' : 0; + } + + void print() const { + std::string input( top().name ); + printf( "%s: ", input.c_str() ); + for( const auto& name : all_names ) { + if( name != input ) + printf( "\\\n\t%s ", name.c_str() ); + } + printf("\n"); } }; @@ -1813,6 +2045,12 @@ static unique_stack input_filenames; static std::map<std::string, ino_t> old_filenames; static const unsigned int sysp = 0; // not a C header file, cf. line-map.h +void cobol_set_pp_option(int opt) { + // capture other preprocessor options eventually + assert(opt == 'M'); + input_filenames.option_m = true; +} + /* * Maintain a stack of input filenames. Ensure the files are unique (by * inode), to prevent copybook cycles. Before pushing a new name, Record the @@ -1823,46 +2061,60 @@ static const unsigned int sysp = 0; // not a C header file, cf. line-map.h * to enforce uniqueness, and the scanner to maintain line numbers. */ bool cobol_filename( const char *name, ino_t inode ) { - line_map *lines = NULL; + //const line_map *lines = NULL; if( inode == 0 ) { auto p = old_filenames.find(name); if( p == old_filenames.end() ) { for( auto& elem : old_filenames ) { - dbgmsg("%6zu %-30s", elem.second, elem.first.c_str()); + dbgmsg("%6" GCC_PRISZ "u %-30s", + (fmt_size_t)elem.second, elem.first.c_str()); } cbl_errx( "logic error: missing inode for %s", name); } - inode = p->second; - assert(inode != 0); + else { + inode = p->second; + assert(inode != 0); + } } linemap_add(line_table, LC_ENTER, sysp, name, 1); input_filename_vestige = name; - bool pushed = input_filenames.push( input_file_t(name, inode, 1, lines) ); - input_filenames.top().lineno = yylineno = 1; - if( getenv(__func__) ) { - dbgmsg(" saving %s with lineno as %d", - input_filenames.top().name, input_filenames.top().lineno); - } + bool pushed = input_filenames.push( input_file_t(name, inode, 1) ); return pushed; } const char * -cobol_lineno_save() { +cobol_lineno( int lineno ) { if( input_filenames.empty() ) return NULL; auto& input( input_filenames.top() ); - input.lineno = yylineno; - if( getenv(__func__) ) { - dbgmsg(" setting %s with lineno as %d", input.name, input.lineno); - } + input.lineno = lineno; return input.name; } +/* + * This function is called from the scanner, usually when a copybook is on top + * of the input stack, before the parser retrieves the token and resets the + * current filename. For that reason, we normaly want to line number of the + * file that is about to become the current one, which is the one behind top(). + * + * If somehow we arrive here when there is nothing underneath, we return the + * current line nubmer, or zero if there's no input. The only consequence is + * that the reported line number might be wrong. + */ +int +cobol_lineno() { + if( input_filenames.empty() ) return 0; + size_t n = input_filenames.size() < 2? 0 : 1; + const auto& input( input_filenames.peek(n) ); + return input.lineno; +} + const char * cobol_filename() { - return input_filenames.empty()? input_filename_vestige : input_filenames.top().name; + return input_filenames.empty()? + input_filename_vestige : input_filenames.top().name; } -const char * +void cobol_filename_restore() { assert(!input_filenames.empty()); const input_file_t& top( input_filenames.top() ); @@ -1870,26 +2122,70 @@ cobol_filename_restore() { input_filename_vestige = top.name; input_filenames.pop(); - if( input_filenames.empty() ) return NULL; + if( input_filenames.empty() ) return; - auto& input = input_filenames.top(); + const auto& input = input_filenames.top(); - input.lines = linemap_add(line_table, LC_LEAVE, sysp, NULL, 0); + linemap_add(line_table, LC_LEAVE, sysp, NULL, 0); +} - yylineno = input.lineno; - if( getenv("cobol_filename") ) { - dbgmsg("restoring %s with lineno to %d", input.name, input.lineno); +size_t +symbol_unique_index( const struct symbol_elem_t *e ) { + assert(e); + size_t usym = symbol_index(e); +#if READY_FOR_INODE + if( ! input_filenames.empty() ) { + size_t inode = input_filenames.top().inode; + usym = usym ^ inode; } - return input.name; +#endif + return usym; } -static location_t token_location; +static int first_line_minus_1 = 0; +static location_t token_location_minus_1 = 0; +static location_t token_location = 0; + +location_t current_token_location() { return token_location; } +location_t current_token_location(const location_t& loc) { + return token_location = loc; +} +location_t current_location_minus_one() { return token_location_minus_1; } +void current_location_minus_one_clear() + { + first_line_minus_1 = 0; + } +/* + * Update global token_location with a location_t expressing a source range + * with start and caret at the first line/column of LOC, and finishing at the + * last line/column of LOC. + */ template <typename LOC> static void gcc_location_set_impl( const LOC& loc ) { - token_location = linemap_line_start( line_table, loc.last_line, 80 ); - token_location = linemap_position_for_column( line_table, loc.first_column); + // Set the position to the first line & column in the location. + static location_t loc_m_1 = 0; + const location_t + start_line = linemap_line_start( line_table, loc.first_line, 80 ), + token_start = linemap_position_for_column( line_table, loc.first_column), + finish_line = linemap_line_start( line_table, loc.last_line, 80 ), + token_finish = linemap_position_for_column( line_table, loc.last_column); + token_location = make_location (token_start, token_start, token_finish); + + if( loc.first_line > first_line_minus_1 ) { + // In order for GDB-COBOL to be able to step through COBOL code properly, + // it is sometimes necessary for the code at the beginning of a COBOL + // line to be using the location_t of the previous line. This is true, for + // example, when laying down the infrastructure code between the last + // statement of a paragraph and the code created at the beginning of the + // following paragragh. This code assumes that token_location values of + // interest are monotonic, and stores that prior value. + first_line_minus_1 = loc.first_line; + token_location_minus_1 = loc_m_1; + loc_m_1 = token_location; + } + location_dump(__func__, __LINE__, "parser", loc); } @@ -1912,11 +2208,9 @@ verify_format( const char gmsgid[] ) { static regex_t re; static int cflags = REG_EXTENDED; static int status = regcomp( &re, pattern, cflags ); - static char errbuf[80]; - - if( status != 0 ) { + static char errbuf[80]; int n = regerror(status, &re, errbuf, sizeof(errbuf)); gcc_assert(size_t(n) < sizeof(errbuf)); fprintf(stderr, "%s:%d: %s", __func__, __LINE__, errbuf); @@ -1932,9 +2226,16 @@ verify_format( const char gmsgid[] ) { } #endif -static const diagnostic_option_id option_zero; +static const diagnostics::option_id option_zero; size_t parse_error_inc(); +void gcc_location_dump() { + linemap_dump_location( line_table, token_location, stderr ); +} + + +void ydferror( const char gmsgid[], ... ) ATTRIBUTE_GCOBOL_DIAG(1, 2); + void ydferror( const char gmsgid[], ... ) { verify_format(gmsgid); @@ -1943,8 +2244,9 @@ ydferror( const char gmsgid[], ... ) { va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, token_location); - bool ret = global_dc->diagnostic_impl (&richloc, nullptr, option_zero, - gmsgid, &ap, DK_ERROR); + /*bool ret =*/ global_dc->diagnostic_impl (&richloc, nullptr, option_zero, + gmsgid, &ap, + diagnostics::kind::error); va_end (ap); } @@ -1957,7 +2259,7 @@ extern YYLTYPE yylloc; * the global token_location, which is passed to the diagnostic framework. The * original value is restored when the instantiated variable goes out of scope. */ -class temp_loc_t : protected YYLTYPE { +class temp_loc_t { location_t orig; public: temp_loc_t() : orig(token_location) { @@ -1965,14 +2267,11 @@ class temp_loc_t : protected YYLTYPE { gcc_location_set(yylloc); // use lookahead location } - temp_loc_t( const YYLTYPE& loc) : orig(token_location) { + explicit temp_loc_t( const YYLTYPE& loc) : orig(token_location) { gcc_location_set(loc); } - temp_loc_t( const YDFLTYPE& loc) : orig(token_location) { - YYLTYPE lloc = { - loc.first_line, loc.first_column, - loc.last_line, loc.last_column }; - gcc_location_set(lloc); + explicit temp_loc_t( const YDFLTYPE& loc) : orig(token_location) { + gcc_location_set(loc); } ~temp_loc_t() { if( orig != token_location ) { @@ -2002,7 +2301,8 @@ class temp_loc_t : protected YYLTYPE { va_start (ap, gmsgid); \ rich_location richloc (line_table, token_location); \ bool ret = global_dc->diagnostic_impl (&richloc, nullptr, option_zero, \ - gmsgid, &ap, DK_ERROR); \ + gmsgid, &ap, \ + diagnostics::kind::error); \ va_end (ap); \ global_dc->end_group(); @@ -2011,18 +2311,38 @@ void error_msg( const YYLTYPE& loc, const char gmsgid[], ... ) { ERROR_MSG_BODY } +void error_msg( const YDFLTYPE& loc, const char gmsgid[], ... ) + ATTRIBUTE_GCOBOL_DIAG(2, 3); + void error_msg( const YDFLTYPE& loc, const char gmsgid[], ... ) { ERROR_MSG_BODY } -void -cdf_location_set(YYLTYPE loc) { - extern YDFLTYPE ydflloc; +bool +warn_msg( const YYLTYPE& loc, const char gmsgid[], ... ) { + temp_loc_t looker(loc); + verify_format(gmsgid); + auto_diagnostic_group d; + va_list ap; + va_start (ap, gmsgid); + rich_location richloc (line_table, token_location); + auto ret = emit_diagnostic_valist( diagnostics::kind::warning, + token_location, + option_zero, gmsgid, &ap ); + va_end (ap); + return ret; +} - ydflloc.first_line = loc.first_line; - ydflloc.first_column = loc.first_column; - ydflloc.last_line = loc.last_line; - ydflloc.last_column = loc.last_column; +void error_msg_direct( const char gmsgid[], ... ) { + verify_format(gmsgid); + parse_error_inc(); + auto_diagnostic_group d; + va_list ap; + va_start (ap, gmsgid); + /*auto ret = */emit_diagnostic_valist( diagnostics::kind::error, + token_location, + option_zero, gmsgid, &ap ); + va_end (ap); } void @@ -2034,24 +2354,15 @@ yyerror( const char gmsgid[], ... ) { va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, token_location); - bool ret = global_dc->diagnostic_impl (&richloc, nullptr, option_zero, - gmsgid, &ap, DK_ERROR); + /*bool ret =*/ global_dc->diagnostic_impl ( &richloc, + nullptr, + option_zero, + gmsgid, + &ap, diagnostics::kind::error); va_end (ap); global_dc->end_group(); } -bool -yywarn( const char gmsgid[], ... ) { - verify_format(gmsgid); - auto_diagnostic_group d; - va_list ap; - va_start (ap, gmsgid); - auto ret = emit_diagnostic_valist( DK_WARNING, token_location, - option_zero, gmsgid, &ap ); - va_end (ap); - return ret; -} - /* * Sometimes during parsing an error is noticed late. This message refers back * to an arbitrary file and line number. @@ -2078,7 +2389,7 @@ yyerrorvl( int line, const char *filename, const char fmt[], ... ) { static inline size_t matched_length( const regmatch_t& rm ) { return rm.rm_eo - rm.rm_so; } -const char * +int cobol_fileline_set( const char line[] ) { static const char pattern[] = "#line +([[:alnum:]]+) +[\"']([^\"']+). *\n"; static const int cflags = REG_EXTENDED | REG_ICASE; @@ -2091,7 +2402,7 @@ cobol_fileline_set( const char line[] ) { if( (erc = regcomp(&re, pattern, cflags)) != 0 ) { regerror(erc, &re, regexmsg, sizeof(regexmsg)); dbgmsg( "%s:%d: could not compile regex: %s", __func__, __LINE__, regexmsg ); - return line; + return 0; } preg = &re; } @@ -2099,10 +2410,10 @@ cobol_fileline_set( const char line[] ) { if( erc != REG_NOMATCH ) { regerror(erc, preg, regexmsg, sizeof(regexmsg)); dbgmsg( "%s:%d: could not compile regex: %s", __func__, __LINE__, regexmsg ); - return line; + return 0; } - error_msg(yylloc, "invalid #line directive: %s", line ); - return line; + error_msg(yylloc, "invalid %<#line%> directive: %s", line ); + return 0; } const char @@ -2110,41 +2421,42 @@ cobol_fileline_set( const char line[] ) { *filename = xstrndup(line + pmatch[2].rm_so, matched_length(pmatch[2])); int fileline; - if( 1 != sscanf(line_str, "%d", &fileline) ) - yywarn("could not parse line number %s from #line directive", line_str); - + if( 1 != sscanf(line_str, "%d", &fileline) ) { + cbl_message(LexLineE, + "could not parse line number %s from %<#line%> directive", + line_str); + } input_file_t input_file( filename, ino_t(0), fileline ); // constructor sets inode - if( getenv(__func__) ) return filename; // ignore #line directive - if( input_filenames.empty() ) { - input_file.lines = linemap_add(line_table, LC_ENTER, sysp, filename, 1); input_filenames.push(input_file); } input_file_t& file = input_filenames.top(); file = input_file; - yylineno = file.lineno; - return file.name; + return file.lineno; } -class timespec_t { - struct timespec now; +//#define TIMING_PARSE +#ifdef TIMING_PARSE +class cbl_timespec { + uint64_t now; // Nanoseconds public: - timespec_t() { - clock_gettime(CLOCK_MONOTONIC, &now); + cbl_timespec() { + now = get_time_nanoseconds(); } double ns() const { - return now.tv_sec * 1000000000 + now.tv_nsec; + return now; } - friend double operator-( const timespec_t& now, const timespec_t& then ); + friend double operator-( const cbl_timespec& now, const cbl_timespec& then ); }; double -operator-( const timespec_t& then, const timespec_t& now ) { +operator-( const cbl_timespec& now, const cbl_timespec& then ) { return (now.ns() - then.ns()) / 1000000000; } +#endif static int parse_file( const char filename[] ) @@ -2155,15 +2467,25 @@ parse_file( const char filename[] ) parser_enter_file(filename); - timespec_t start; + if( input_filenames.option() == 'M' ) { + input_filenames.print(); + return 0; + } + +#ifdef TIMING_PARSE + cbl_timespec start; +#endif int erc = yyparse(); - timespec_t finish; +#ifdef TIMING_PARSE + cbl_timespec finish; double dt = finish - start; + printf("Overall parse & generate time is %.6f seconds\n", dt); +#endif + parser_leave_file(); - //printf("Overall parse & generate time is %.6f seconds\n", dt); fclose (yyin); @@ -2187,30 +2509,20 @@ cobol_set_debugging( bool flex, bool yacc, bool parser ) yy_flex_debug = flex? 1 : 0; ydfdebug = yydebug = yacc? 1 : 0; f_trace_debug = parser? 1 : 0; - - char *ind = getenv("INDICATOR_COLUMN"); - if( ind ) { - int col; - if( 1 != sscanf(ind, "%d", &col) ) { - yywarn("ignored non-integer value for INDICATOR_COLUMN=%s", ind); - } - cobol_set_indicator_column(col); - } } -os_locale_t os_locale = { "UTF-8", xstrdup("C.UTF-8") }; - +os_locale_t os_locale = { "UTF-8", "C.UTF-8" }; void cobol_parse_files (int nfile, const char **files) { - char * opaque = setlocale(LC_CTYPE, ""); + const char * opaque = setlocale(LC_CTYPE, ""); if( ! opaque ) { - yywarn("setlocale: unable to initialize LOCALE"); + cbl_message(ParLocaleW, "setlocale: unable to initialize LOCALE"); } else { char *codeset = nl_langinfo(CODESET); if( ! codeset ) { - yywarn("nl_langinfo failed after setlocale succeeded"); + cbl_message(ParLangInfoW, "%<nl_langinfo%> failed after %<setlocale()%> succeeded"); } else { os_locale.codeset = codeset; } @@ -2222,19 +2534,6 @@ cobol_parse_files (int nfile, const char **files) } } -/* Outputs the formatted string onto the file descriptor */ - -void -cbl_message(int fd, const char *format_string, ...) - { - va_list ap; - va_start(ap, format_string); - char *ostring = xvasprintf(format_string, ap); - va_end(ap); - write(fd, ostring, strlen(ostring)); - free(ostring); - } - /* Uses the GCC internal_error () to output the formatted string. Processing ends with a stack trace */ @@ -2244,18 +2543,37 @@ cbl_internal_error(const char *gmsgid, ...) { auto_diagnostic_group d; va_list ap; va_start(ap, gmsgid); - emit_diagnostic_valist( DK_ICE, token_location, option_zero, gmsgid, &ap ); + emit_diagnostic_valist( diagnostics::kind::ice, + token_location, option_zero, gmsgid, &ap ); va_end(ap); + abort(); // This unnecessary statement is needed so that [[noreturn]] + // // doesn't cause a warning. } +diagnostics::kind cbl_diagnostic_kind( cbl_diag_id_t id ); +const char * cbl_diagnostic_option( cbl_diag_id_t id ); + void -cbl_unimplementedw(const char *gmsgid, ...) { +cbl_unimplementedw(cbl_diag_id_t id, const char *gmsgid, ...) { verify_format(gmsgid); auto_diagnostic_group d; + const char *option; + char *msg = nullptr; + + diagnostics::kind kind = cbl_diagnostic_kind(id); + if( kind == diagnostics::kind::ignored ) return; + + if( (option = cbl_diagnostic_option(id)) != nullptr ) { + msg = xasprintf("%s [%s]", gmsgid, option); + gmsgid = msg; + } + va_list ap; + va_start(ap, gmsgid); - emit_diagnostic_valist( DK_SORRY, token_location, option_zero, gmsgid, &ap ); + emit_diagnostic_valist( kind, token_location, option_zero, gmsgid, &ap ); va_end(ap); + free(msg); } void @@ -2264,7 +2582,8 @@ cbl_unimplemented(const char *gmsgid, ...) { auto_diagnostic_group d; va_list ap; va_start(ap, gmsgid); - emit_diagnostic_valist( DK_SORRY, token_location, option_zero, gmsgid, &ap ); + emit_diagnostic_valist( diagnostics::kind::sorry, + token_location, option_zero, gmsgid, &ap ); va_end(ap); } @@ -2275,12 +2594,13 @@ cbl_unimplemented_at( const YYLTYPE& loc, const char *gmsgid, ... ) { auto_diagnostic_group d; va_list ap; va_start(ap, gmsgid); - emit_diagnostic_valist( DK_SORRY, token_location, option_zero, gmsgid, &ap ); + emit_diagnostic_valist( diagnostics::kind::sorry, + token_location, option_zero, gmsgid, &ap ); va_end(ap); } -/* - * analogs to err(3) and errx(3). +/* + * analogs to err(3) and errx(3). */ #pragma GCC diagnostic push @@ -2292,7 +2612,8 @@ cbl_err(const char *fmt, ...) { verify_format(gmsgid); va_list ap; va_start(ap, fmt); - emit_diagnostic_valist( DK_FATAL, token_location, option_zero, gmsgid, &ap ); + emit_diagnostic_valist( diagnostics::kind::fatal, + token_location, option_zero, gmsgid, &ap ); va_end(ap); } #pragma GCC diagnostic pop @@ -2303,10 +2624,18 @@ cbl_errx(const char *gmsgid, ...) { auto_diagnostic_group d; va_list ap; va_start(ap, gmsgid); - emit_diagnostic_valist( DK_FATAL, token_location, option_zero, gmsgid, &ap ); + emit_diagnostic_valist( diagnostics::kind::fatal, + token_location, option_zero, gmsgid, &ap ); va_end(ap); } +/* + * For a function that uses host *printf, %zu or %td or %wu are not ok, sadly. + * not all supported host arches support those. So, for *printf family one + * needs to use macros like HOST_WIDE_INT_PRINT_DEC (for HOST_WIDE_INT + * argument), or HOST_SIZE_T_PRINT_UNSIGNED (for size_t, with casts to + * (fmt_size_t)). + */ void dbgmsg(const char *msg, ...) { if( yy_flex_debug || yydebug ) { @@ -2319,12 +2648,6 @@ dbgmsg(const char *msg, ...) { } } -void -dialect_error( const YYLTYPE& loc, const char term[], const char dialect[] ) { - error_msg(loc, "%s is not ISO syntax, requires -dialect %s", - term, dialect); -} - bool fisdigit(int c) { return ISDIGIT(c); @@ -2332,12 +2655,621 @@ bool fisdigit(int c) bool fisspace(int c) { return ISSPACE(c); - }; + } int ftolower(int c) { return TOLOWER(c); } +int ftoupper(int c) + { + return TOUPPER(c); + } bool fisprint(int c) { return ISPRINT(c); - }; + } + +// 8.9 Reserved words +static const std::set<std::string> reserved_words = { + // GCC COBOL keywords + "COMMAND-LINE", + "COMMAND-LINE-COUNT", + + // GCC device names + "C01", + "C02", + "C03", + "C04", + "C05", + "C06", + "C07", + "C08", + "C09", + "C10", + "C11", + "C12", + "CONSOLE", + "S01", + "S02", + "S03", + "S04", + "S05", + "STDERR", + "STDIN", + "STDOUT", + "SYSIN", + "SYSIPT", + "SYSLIST", + "SYSLST", + "SYSOUT", + "SYSPCH", + "SYSPUNCH", + "UPSI", + + // IBM keywords that GCC recognizes + "BASIS", + "CBL", + "ENTER", + "READY", + "TITLE", + "TRACE", + "ALTER", + "COBOL", + "DATE-COMPILED", + "DATE-WRITTEN", + "DBCS", + "DEBUGGING", + "EGCS", + "ENTRY", + "EVERY", + "INSTALLATION", + "I-O-CONTROL", + "KANJI", + "LABEL", + "NULLS", + "PADDING", + "PROCEDURES", + "PROCEED", + "RECORDING", + "RERUN", + "REVERSED", + "SECURITY", + "TALLY", + "VOLATILE", + "XML", + "END-START", + + // ISO 2023 keywords + "ACCEPT", + "ACCESS", + "ACTIVE-CLASS", + "ADD", + "ADDRESS", + "ADVANCING", + "AFTER", + "ALIGNED", + "ALL", + "ALLOCATE", + "ALPHABET", + "ALPHABETIC", + "ALPHABETIC-LOWER", + "ALPHABETIC-UPPER", + "ALPHANUMERIC", + "ALPHANUMERIC-EDITED", + "ALSO", + "ALTERNATE", + "AND", + "ANY", + "ANYCASE", + "ARE", + "AREA", + "AREAS", + "AS", + "ASCENDING", + "ASSIGN", + "AT", + "B-AND", + "B-NOT", + "B-OR", + "B-SHIFT-L", + "B-SHIFT-LC", + "B-SHIFT-R", + "B-SHIFT-RC", + "B-XOR", + "BASED", + "BEFORE", + "BINARY", + "BINARY-CHAR", + "BINARY-DOUBLE", + "BINARY-LONG", + "BINARY-SHORT", + "BIT", + "BLANK", + "BLOCK", + "BOOLEAN", + "BOTTOM", + "BY", + "CALL", + "CANCEL", + "CF", + "CH", + "CHARACTER", + "CHARACTERS", + "CLASS", + "CLASS-ID", + "CLOSE", + "CODE", + "CODE-SET", + "COL", + "COLLATING", + "COLS", + "COLUMN", + "COLUMNS", + "COMMA", + "COMMIT", + "COMMON", + "COMP", + "COMPUTATIONAL", + "COMPUTE", + "CONDITION", + "CONFIGURATION", + "CONSTANT", + "CONTAINS", + "CONTENT", + "CONTINUE", + "CONTROL", + "CONTROLS", + "CONVERTING", + "COPY", + "CORR", + "CORRESPONDING", + "COUNT", + "CRT", + "CURRENCY", + "CURSOR", + "DATA", + "DATA-POINTER", + "DATE", + "DAY", + "DAY-OF-WEEK", + "DE", + "DECIMAL-POINT", + "DECLARATIVES", + "DEFAULT", + "DELETE", + "DELIMITED", + "DELIMITER", + "DEPENDING", + "DESCENDING", + "DESTINATION", + "DETAIL", + "DISPLAY", + "DIVIDE", + "DIVISION", + "DOWN", + "DUPLICATES", + "DYNAMIC", + "EC", + "EDITING", + "ELSE", + "EMD-START", + "END", + "END-ACCEPT", + "END-ADD", + "END-CALL", + "END-COMPUTE", + "END-DELETE", + "END-DISPLAY", + "END-DIVIDE", + "END-EVALUATE", + "END-IF", + "END-MULTIPLY", + "END-OF-PAGE", + "END-PERFORM", + "END-READ", + "END-RECEIVE", + "END-RETURN", + "END-REWRITE", + "END-SEARCH", + "END-SEND", + "END-STRING", + "END-SUBTRACT", + "END-UNSTRING", + "END-WRITE", + "ENVIRONMENT", + "EO", + "EOP", + "EQUAL", + "ERROR", + "EVALUATE", + "EXCEPTION", + "EXCEPTION-OBJECT", + "EXCLUSIVE-OR", + "EXIT", + "EXTEND", + "EXTERNAL", + "FACTORY", + "FALSE", + "FARTHEST-FROM-ZERO", + "FD", + "FILE", + "FILE-CONTROL", + "FILLER", + "FINAL", + "FINALLY", + "FIRST", + "FLOAT-BINARY-128", + "FLOAT-BINARY-32", + "FLOAT-BINARY-64", + "FLOAT-DECIMAL-16", + "FLOAT-DECIMAL-34", + "FLOAT-EXTENDED", + "FLOAT-INFINITY", + "FLOAT-LONG", + "FLOAT-NOT-A-NUMBER-", + "FLOAT-SHORT", + "FOOTING", + "FOR", + "FORMAT", + "FREE", + "FROM", + "FUNCTION", + "FUNCTION-ID", + "FUNCTION-POINTER", + "GENERATE", + "GET", + "GIVING", + "GLOBAL", + "GO", + "GOBACK", + "GREATER", + "GROUP", + "GROUP-USAGE", + "HEADING", + "HIGH-VALUE", + "HIGH-VALUES", + "I-O", + "I-OICONTROL", + "IDENTIFICATION", + "IF", + "IN", + "IN-ARITHMETIC-RANGE", + "INDEX", + "INDEXED", + "INDICATE", + "INHERITS", + "INITIAL", + "INITIALIZE", + "INITIATE", + "INPUT", + "INPUT-OUTPUT", + "INSPECT", + "INTERFACE", + "INTERFACE-ID", + "INTO", + "INVALID", + "INVOKE", + "IS", + "JUST", + "JUSTIFIED", + "KEY", + "LAST", + "LEADING", + "LEFT", + "LENGTH", + "LESS", + "LIMIT", + "LIMITS", + "LINAGE", + "LINAGE-COUNTER", + "LINE", + "LINE-COUNTER", + "LINES", + "LINKAGE", + "LOCAL-STORAGE", + "LOCALE", + "LOCATION", + "LOCK", + "LOW-VALUE", + "LOW-VALUES", + "MERGE", + "MESSAGE-TAG", + "METHOD-ID", + "MINUS", + "MODE", + "MOVE", + "MULTIPLY", + "NATIONAL", + "NATIONAL-EDITED", + "NATIVE", + "NEAREST-TO-ZERO", + "NEGATIVE", + "NESTED", + "NEXT", + "NO", + "NOT", + "NULL", + "NUMBER", + "NUMERIC", + "NUMERIC-EDITED", + "OBJECT", + "OBJECT-COMPUTER", + "OBJECT-REFERENCE", + "OCCURS", + "OF", + "OFF", + "OMITTED", + "ON", + "OPEN", + "OPTIONAL", + "OPTIONS", + "OR", + "ORDER", + "ORGANIZATION", + "OTHER", + "OUTPUT", + "OVERFLOW", + "OVERRIDE", + "PACKED-DECIMAL", + "PAGE", + "PAGE-COUNTER", + "PERFORM", + "PF", + "PH", + "PIC", + "PICTURE", + "PLUS", + "POINTER", + "POSITIVE", + "PRESENT", + "PRINTING", + "PROCEDURE", + "PROGRAM", + "PROGRAM-ID", + "PROGRAM-POINTER", + "PROPERTY", + "PROTOTYPE", + "QUIET", + "QUOTE", + "QUOTES", + "RAISE", + "RAISING", + "RANDOM", + "RD", + "READ", + "RECEIVE", + "RECORD", + "RECORDS", + "REDEFINES", + "REEL", + "REFERENCE", + "RELATIVE", + "RELEASE", + "REMAINDER", + "REMOVAL", + "RENAMES", + "REPLACE", + "REPLACING", + "REPORT", + "REPORTING", + "REPORTS", + "REPOSITORY", + "RESERVE", + "RESET", + "RESUME", + "RETRY", + "RETURN", + "RETURNING", + "REWIND", + "REWRITE", + "RF", + "RH", + "RIGHT", + "ROLLBACK", + "ROUNDED", + "RUN", + "SAME", + "SCREEN", + "SD", + "SEARCH", + "SECTION", + "SELECT", + "SELF", + "SEND", + "SENTENCE", + "SEPARATE", + "SEQUENCE", + "SEQUENTIAL", + "SET", + "SHARING", + "SIGN", + "SIGNALING", + "SIZE", + "SORT", + "SORT-MERGE", + "SOURCE", + "SOURCE-COMPUTER", + "SOURCES", + "SPACE", + "SPACES", + "SPECIAL-NAMES", + "STANDARD", + "STANDARD-1", + "STANDARD-2", + "START", + "STATUS", + "STOP", + "STRING", + "SUBTRACT", + "SUM", + "SUPER", + "SUPPRESS", + "SYMBOLIC", + "SYNC", + "SYNCHRONIZED", + "SYSTEM-DEFAULT", + "TABLE", + "TALLYING", + "TERMINATE", + "TEST", + "THAN", + "THEN", + "THROUGH", + "THRU", + "TIME", + "TIMES", + "TO", + "TOP", + "TRAILING", + "TRUE", + "TYPE", + "TYPEDEF", + "UNIT", + "UNIVERSAL", + "UNLOCK", + "UNSTRING", + "UNTIL", + "UP", + "UPON", + "USAGE", + "USE", + "USER-DEFAULT", + "USING", + "VAL-STATUS", + "VALID", + "VALIDATE", + "VALIDATE-STATUS", + "VALUE", + "VALUES", + "VARYING", + "WHEN", + "WITH", + "WORKING-STORAGE", + "WRITE", + "XOR", + "ZERO", + "ZEROES", + "ZEROS", + "+", + "-", + "*", + "/", + "**", + "<", + "<=", + "<>", + "=", + ">", + ">=", + "&", + "*>", + "::", + ">>", +}; + +// 8.10 Context-sensitive words +static const std::set<std::string> context_sensitive_words = { + "ACTIVATING", // MODULE-NAME intrinsic function + "ANUM", // CONVERT intrinsic function + "APPLY", // I-O-CONTROL paragraph + "ARITHMETIC", // OPTIONS paragraph + "ATTRIBUTE", // SET statement + "AUTO", // screen description entry + "AUTOMATIC", // LOCK MODE clause + "AWAY-FROM-ZERO", // ROUNDED phrase + "BACKGROUND-COLOR", // screen description entry + "BACKWARD", // INSPECT statement + "BELL", // screen description entry and SET attribute statement + "BINARY-ENCODING", // USAGE clause and FLOAT-DECIMAL clause + "BLINK", // screen description entry and SET attribute statement + "BYTE", // CONVERT intrinsic function + "BYTES", // RECORD clause + "BYTE-LENGTH", // constant entry + "CAPACITY", // OCCURS clause + "CENTER", // COLUMN clause + "CLASSIFICATION", // OBJECT-COMPUTER paragraph + "CURRENT", // MODULE-NAME intrinsic function + "CYCLE", // EXIT statement + "DECIMAL-ENCODING", // USAGE clause and FLOAT-DECIMAL clause + "EOL", // ERASE clause in a screen description entry + "EOS", // ERASE clause in a screen description entry + "ENTRY-CONVENTION", // OPTIONS paragraph + "ERASE", // screen description entry + "EXPANDS", // class-specifier and interface-specifier of the REPOSITORY paragraph + "FLOAT-BINARY", // OPTIONS paragraph + "FLOAT-DECIMAL", // OPTIONS paragraph + "FOREGROUND-COLOR", // screen description entry + "FOREVER", // RETRY phrase + "FULL", // screen description entry + "HEX", // CONVERT intrinsic function + "HIGH-ORDER-LEFT", // FLOAT-BINARY clause, FLOAT-DECIMAL clause, and USAGE clause + "HIGH-ORDER-RIGHT", // FLOAT-BINARY clause, FLOAT-DECIMAL clause, and USAGE clause + "HIGHLIGHT", // screen description entry and SET attribute statement + "IGNORING", // READ statement + "IMPLEMENTS", // FACTORY paragraph and OBJECT paragraph + "INITIALIZED", // ALLOCATE statement and OCCURS clause + "INTERMEDIATE", // OPTIONS paragraph + "INTRINSIC", // function-specifier of the REPOSITORY paragraph + "LC_ALL", // SET statement + "LC_COLLATE", // SET statement + "LC_CTYPE", // SET statement + "LC_MESSAGES", // SET statement + "LC_MONETARY", // SET statement + "LC_NUMERIC", // SET statement + "LC_TIME", // SET statement + "LOWLIGHT", // screen description entry and SET attribute statement + "MANUAL", // LOCK MODE clause + "MULTIPLE", // LOCK ON phrase + "NAT", // CONVERT intrinsic function + "NEAREST-AWAY-FROM-ZERO", // INTERMEDIATE ROUNDING clause and ROUNDED phrase + "NEAREST-EVEN", // INTERMEDIATE ROUNDING clause and ROUNDED phrase + "NEAREST-TOWARD-ZERO", // INTERMEDIATE ROUNDING clause and ROUNDED phrase + "NONE", // DEFAULT clause + "NORMAL", // STOP statement + "NUMBERS", // COLUMN clause and LINE clause + "ONLY", // Object-view, SHARING clause, SHARING phrase, and USAGE clause + "PARAGRAPH", // EXIT statement + "PREFIXED", // DYNAMIC LENGTH STRUCTURE clause + "PREVIOUS", // READ statement + "PROHIBITED", // INTERMEDIATE ROUNDING clause and ROUNDED phrase + "RECURSIVE", // PROGRAM-ID paragraph + "RELATION", // VALIDATE-STATUS clause + "REQUIRED", // screen description entry + "REVERSE-VIDEO", // screen description entry and SET attribute statement + "ROUNDING", // OPTIONS paragraph + "SECONDS", // RETRY phrase, CONTINUE statement + "SECURE", // screen description entry + "SHORT", // DYNAMIC LENGTH STRUCTURE clause + "SIGNED", // DYNAMIC LENGTH STRUCTURE clause and USAGE clause + "STACK", // MODULE-NAME intrinsic function + "STANDARD-BINARY", // ARITHMETIC clause + "STANDARD-DECIMAL", // ARITHMETIC clause + "STATEMENT", // RESUME statement + "STEP", // OCCURS clause + "STRONG", // TYPEDEF clause + "STRUCTURE", // DYNAMIC LENGTH STRUCTURE clause + "SYMBOL", // CURRENCY clause + "TOP-LEVEL", // MODULE-NAME intrinsic function + "TOWARD-GREATER", // ROUNDED phrase + "TOWARD-LESSER", // ROUNDED phrase + "TRUNCATION", // INTERMEDIATE ROUNDING clause and ROUNDED phrase + "UCS-4", // ALPHABET clause + "UNDERLINE", // screen description entry and SET attribute statement + "UNSIGNED", // USAGE clause + "UTF-8", // ALPHABET clause + "UTF-16", // ALPHABET clause + "YYYYDDD", // ACCEPT statement + "YYYYMMDD", // ACCEPT statement +}; + +// Is the input a COBOL word, per ISO/IEC 1989:2023 (E) ? +// We add a few GCC-specific keywords, and our supported IBM keywords. +bool +iso_cobol_word( const std::string& name, bool include_context ) { + auto ok = 1 == reserved_words.count(name); + if( include_context && !ok ) { + ok = 1 == context_sensitive_words.count(name); + } + return ok; +} + diff --git a/gcc/cobol/util.h b/gcc/cobol/util.h index eb08ed7..d457e3e 100644 --- a/gcc/cobol/util.h +++ b/gcc/cobol/util.h @@ -31,19 +31,96 @@ #ifndef _UTIL_H_ #define _UTIL_H_ -void cbl_message(int fd, const char *format_string, ...); -void cbl_internal_error(const char *format_string, ...); +[[noreturn]] void cbl_internal_error(const char *format_string, ...) + ATTRIBUTE_GCOBOL_DIAG(1, 2); -void cbl_err(const char *format_string, ...); -void cbl_errx(const char *format_string, ...); +void cbl_err(const char *format_string, ...) ATTRIBUTE_GCOBOL_DIAG(1, 2); +void cbl_errx(const char *format_string, ...) ATTRIBUTE_GCOBOL_DIAG(1, 2); bool fisdigit(int c); bool fisspace(int c); int ftolower(int c); +int ftoupper(int c); bool fisprint(int c); -const char * cobol_filename_restore(); -const char * cobol_lineno_save(); +void cobol_set_pp_option(int opt); +void cobol_filename_restore(); +const char * cobol_lineno( int ); +int cobol_lineno(void); + +unsigned long gb4( size_t input ); + +template <typename P> +static inline const void * +as_voidp( P p ) { + return static_cast<const void *>(p); +} + +/* + * The default source format, whether free or fixed, is determined + * heuristically by examining the PROGRAM-ID line, if it exists, in the first + * input file. If that file does not have such a line, the default is free + * format. Else the format is set to fixed if anything appears on that line + * that would prohibit parsing it as free format, + */ +class source_format_t { + bool first_file, explicitly; + int left, right; +public: + source_format_t() + : first_file(true), explicitly(false), left(0), right(0) + {} + void indicator_column_set( int column ) { + explicitly = true; + if( column == 0 ) right = 0; + if( column < 0 ) { + column = -column; + right = 73; + } + left = column; + } + + bool inference_pending() { + bool tf = first_file && !explicitly; + first_file = false; + return tf; + } + + void infer( const char *bol, bool want_reference_format ); + + inline bool is_fixed() const { return left == 7; } + inline bool is_reffmt() const { return is_fixed() && right == 73; } + inline bool is_free() const { return ! is_fixed(); } + + const char * description() const { + if( is_reffmt() ) return "REFERENCE"; + if( is_fixed() ) return "FIXED"; + if( is_free() ) return "FREE"; + gcc_unreachable(); + } + + inline int left_margin() { + return left == 0? left : left - 1; + } + inline int right_margin() { + return right == 0? right : right - 1; + } +}; + + +void cdf_push(); +void cdf_push_call_convention(); +void cdf_push_current_tokens(); +void cdf_push_dictionary(); +void cdf_push_enabled_exceptions(); +void cdf_push_source_format(); + +void cdf_pop(); +void cdf_pop_call_convention(); +void cdf_pop_current_tokens(); +void cdf_pop_dictionary(); +void cdf_pop_source_format(); +void cdf_pop_enabled_exceptions(); #endif |
