diff --git a/gcc/cobol/UAT/bugsuite.src/bugs.at b/gcc/cobol/UAT/bugsuite.src/bugs.at index 5709380d3b20e3894bd75e58e540be67b6b4122f..21b525bffee84f14c04cd9da7b4c793eb26ee283 100644 --- a/gcc/cobol/UAT/bugsuite.src/bugs.at +++ b/gcc/cobol/UAT/bugsuite.src/bugs.at @@ -32,3 +32,58 @@ AT_CHECK([$COMPILE prog.cob], [0], [], []) AT_CHECK([./a.out], [1], [], []) AT_CLEANUP +AT_SETUP([Repeated program-id causes a crash]) +AT_KEYWORDS([bugs]) +AT_DATA([prog.cob], []) +AT_CHECK([$COMPILE prog.cob], [0], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + PROCEDURE DIVISION. + DISPLAY "Hi." + END PROGRAM prog. + + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + PROCEDURE DIVISION. + DISPLAY "Hi." + END PROGRAM prog. +], []) +AT_CHECK([./a.out], [1], [], []) +AT_CLEANUP + +AT_SETUP([Repeated variable name should be an error]) +AT_KEYWORDS([bugs]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 redundant PIC 9. + 01 redundant PIC 9. + PROCEDURE DIVISION. + DISPLAY redundant + DISPLAY "Hi". + END PROGRAM prog. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([./a.out], [1], [], []) +AT_CLEANUP + + +AT_SETUP([Empty value shenanigans]) +AT_KEYWORDS([bugs]) +AT_DATA([prog.cob], [IDENTIFICATION DIVISION. +PROGRAM-ID. routine_128_cobol. +DATA DIVISION. +LINKAGE SECTION. +01 var1 pic 9(30) VALUE . +01 var2 pic 9(30) VALUE . +PROCEDURE DIVISION USING var1 RETURNING var2. + DISPLAY " I am COBOL routine_128_cobol". + DISPLAY var1 + MOVE var1 TO var2 + END PROGRAM routine_c. +]) +AT_CHECK([$COMPILE prog.cob], [1], [], []) +AT_CLEANUP + diff --git a/gcc/cobol/UAT/failsuite.src/run_evaluate.at b/gcc/cobol/UAT/failsuite.src/run_evaluate.at index 90d5af41347c1e4154f92b630ed78094b286c521..f5d35b3bfd5bb742f1ed0c7d168bebd2755a4f98 100644 --- a/gcc/cobol/UAT/failsuite.src/run_evaluate.at +++ b/gcc/cobol/UAT/failsuite.src/run_evaluate.at @@ -8,50 +8,3 @@ AT_COLOR_TESTS AT_TESTED('$GCOBOL') -AT_SETUP([EVALUATE condition (2)]) -AT_KEYWORDS([evaluate condition]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 XVAL PIC X VALUE '_'. - 88 UNDERSCORE VALUE '_'. - PROCEDURE DIVISION. - DISPLAY 'Next line should be "UNDERSCORE evaluates to TRUE"' - EVALUATE TRUE - WHEN NOT UNDERSCORE - DISPLAY - "***IMPROPERLY*** NOT UNDERSCORE evaluates to TRUE" - END-DISPLAY - END-EVALUATE. - EVALUATE TRUE - WHEN UNDERSCORE - DISPLAY "UNDERSCORE evaluates to TRUE" - END-DISPLAY - END-EVALUATE. - - DISPLAY - 'Next line should be "NOT UNDERSCORE evaluates to FALSE"' - EVALUATE FALSE - WHEN NOT UNDERSCORE - DISPLAY "NOT UNDERSCORE evaluates to FALSE" - END-DISPLAY - END-EVALUATE. - EVALUATE FALSE - WHEN UNDERSCORE - DISPLAY - "***IMPROPERLY*** UNDERSCORE evaluates to FALSE" - END-DISPLAY - END-EVALUATE. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([./a.out], [0], [Next line should be "UNDERSCORE evaluates to TRUE" -UNDERSCORE evaluates to TRUE -Next line should be "NOT UNDERSCORE evaluates to FALSE" -NOT UNDERSCORE evaluates to FALSE -], []) -AT_CLEANUP - - diff --git a/gcc/cobol/UAT/failsuite.src/run_functions.at b/gcc/cobol/UAT/failsuite.src/run_functions.at index e9382881337ce350af094044dfb5715f4ad690b5..ef5097aece6d03097fe7348f8b07e13daa53413b 100644 --- a/gcc/cobol/UAT/failsuite.src/run_functions.at +++ b/gcc/cobol/UAT/failsuite.src/run_functions.at @@ -21,3842 +21,6 @@ ### ISO+IEC+1989-2002 15 Intrinsic Functions ### ISO+IEC+1989-2002 9.4 User-Defined Functions -AT_SETUP([FUNCTION ABS]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC S9(4)V9(4) VALUE -1.2345. - PROCEDURE DIVISION. - COMPUTE X = FUNCTION ABS( X ) - DISPLAY X - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[+0001.2345 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION ACOS]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION ACOS ( -0.2345 ) TO Z. - IF Z NOT = 1.807500521108243435101500438523210 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION ANNUITY]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION ANNUITY ( 3, 5 ) TO Z. - IF Z NOT = 3.002932551319648093841642228739002 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION ASIN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION ASIN ( -0.2345 ) TO Y. - IF Y NOT = -0.236704194313346815870178746883458 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION ATAN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION ATAN ( 1 ) TO Y. - IF Y NOT = 0.785398163397448309615660845819875 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION BYTE-LENGTH]) -AT_KEYWORDS([functions length]) -AT_SKIP_IF(true) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(4). - 01 Z PIC N(4). - 01 TEST-FLD PIC S9(04)V9(08). - PROCEDURE DIVISION. - MOVE FUNCTION BYTE-LENGTH ( X ) - TO TEST-FLD. - IF TEST-FLD NOT = 4 - DISPLAY 'BYTE-LENGTH X(4) wrong: ' TEST-FLD - END-DISPLAY - END-IF - MOVE FUNCTION BYTE-LENGTH ( Z ) - TO TEST-FLD - IF TEST-FLD NOT = 8 - DISPLAY 'BYTE-LENGTH N(4) wrong: ' TEST-FLD - END-DISPLAY - END-IF - - MOVE FUNCTION BYTE-LENGTH ( '00128' ) - TO TEST-FLD - IF TEST-FLD NOT = 5 - DISPLAY 'BYTE-LENGTH "00128" wrong: ' TEST-FLD - END-DISPLAY - END-IF - * note: we currently do not support items of category boolean... - *> MOVE FUNCTION BYTE-LENGTH ( b'100' ) - *> TO TEST-FLD - *> IF TEST-FLD NOT = 3 - *> DISPLAY 'BYTE-LENGTH b"100" wrong: ' TEST-FLD - *> END-DISPLAY - *> END-IF - MOVE FUNCTION BYTE-LENGTH ( x'a0' ) - TO TEST-FLD - IF TEST-FLD NOT = 1 - DISPLAY 'BYTE-LENGTH x"a0" wrong: ' TEST-FLD - END-DISPLAY - END-IF - MOVE FUNCTION BYTE-LENGTH ( z'a0' ) - TO TEST-FLD - IF TEST-FLD NOT = 3 - DISPLAY 'BYTE-LENGTH z"a0" wrong: ' TEST-FLD - END-DISPLAY - END-IF - * we currently generate national constants as - * alphanumeric constants... - * MOVE FUNCTION BYTE-LENGTH ( n'a0' ) - * TO TEST-FLD - * IF TEST-FLD NOT = 4 - * DISPLAY 'BYTE-LENGTH n"a0" wrong: ' TEST-FLD - * END-DISPLAY - * END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], -[prog.cob:7: warning: handling of USAGE NATIONAL is unfinished; implementation is likely to be changed -]) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION CHAR]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC S9(4)V9(4) VALUE 108. - 01 TEST-FLD. - 05 TEST-DATA PIC X(01). - 88 VALID-DATA VALUE 'k'. - 05 TEST-UNSET PIC X VALUE '_'. - 88 VALID-UNSET VALUE '_'. - PROCEDURE DIVISION. - STRING FUNCTION CHAR ( X ) - DELIMITED BY SIZE - INTO TEST-FLD - END-STRING. - EVALUATE TRUE - WHEN NOT VALID-UNSET - DISPLAY "FUNCTION result too long" - END-DISPLAY - WHEN VALID-DATA - CONTINUE - WHEN OTHER - DISPLAY TEST-DATA - END-DISPLAY - END-EVALUATE. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION COMBINED-DATETIME]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(04)V9(08). - PROCEDURE DIVISION. - MOVE FUNCTION COMBINED-DATETIME ( 987, 345.6 ) - TO TEST-FLD. - IF TEST-FLD NOT = 987.003456 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION CONCAT / CONCATENATE]) -AT_KEYWORDS([functions]) - -# note: CONCAT was added in COBOL 202x with GnuCOBOL's CONCATENATE -# as blueprint -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(4). - 01 TEST-FLD. - 05 TEST-DATA PIC X(14). - 88 VALID-DATA VALUE 'defxabczz55666'. - 05 TEST-UNSET PIC X VALUE '_'. - 88 VALID-UNSET VALUE '_'. - PROCEDURE DIVISION. - MOVE "defx" TO Y. - STRING FUNCTION CONCAT ( Y "abc" "zz" "55" "666" ) - DELIMITED BY SIZE - INTO TEST-FLD - END-STRING. - EVALUATE TRUE - WHEN NOT VALID-UNSET - DISPLAY "FUNCTION result too long" - END-DISPLAY - WHEN TEST-DATA - <> FUNCTION CONCAT ( Y "abc" "zz" "55" "666" ) - DISPLAY "CONCAT issue, '" TEST-DATA - "' vs. '" - FUNCTION CONCAT ( Y "abc" "zz" "55" "666" ) "'" - END-DISPLAY - WHEN VALID-DATA - CONTINUE - WHEN OTHER - DISPLAY TEST-DATA - END-DISPLAY - END-EVALUATE. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION CONCAT with reference modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(4). - 01 TEST-FLD PIC X(9) VALUE SPACES. - PROCEDURE DIVISION. - MOVE 'defx' TO Y. - MOVE FUNCTION CONCAT - ( Y "abc" "zz" "55" "666" ) (2 : 9) - TO TEST-FLD. - IF TEST-FLD NOT = 'efxabczz5' - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION CONTENT-LENGTH]) -AT_KEYWORDS([functions length]) -AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 P USAGE POINTER. - 01 X PIC X(4) VALUE Z"ABC". - 01 TEST-FLD USAGE BINARY-LONG. - PROCEDURE DIVISION. - MOVE FUNCTION CONTENT-LENGTH ( P ) - TO TEST-FLD. - IF TEST-FLD NOT = 0 - DISPLAY 'CONTENT-LENGTH NULL wrong: ' TEST-FLD - END-DISPLAY - END-IF - SET P TO ADDRESS OF X - MOVE FUNCTION CONTENT-LENGTH ( P ) - TO TEST-FLD - IF TEST-FLD NOT = 3 - DISPLAY 'CONTENT-LENGTH z"abc" wrong: ' TEST-FLD - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION CONTENT-OF]) -AT_KEYWORDS([functions POINTER literal BASED ALLOCATE FREE EXCEPTION-STATUS]) -AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 P USAGE POINTER. - 01 X PIC X(4) VALUE Z"ABC". - 01 B PIC X(10) BASED. - PROCEDURE DIVISION. - SET P TO ADDRESS OF X - IF FUNCTION CONTENT-OF ( P ) NOT EQUAL 'ABC' THEN - DISPLAY 'CONTENT-OF(ptr) wrong' END-DISPLAY - END-IF - IF FUNCTION CONTENT-OF ( P, 2 ) NOT EQUAL 'AB' THEN - DISPLAY 'CONTENT-OF(ptr, len) wrong' END-DISPLAY - END-IF - IF FUNCTION EXCEPTION-STATUS NOT = SPACES THEN - DISPLAY 'unexpected exception (1): ' - FUNCTION EXCEPTION-STATUS - END-DISPLAY - END-IF - SET P TO NULL - MOVE 'PPPP' TO X - STRING FUNCTION CONTENT-OF ( P ) - DELIMITED BY SIZE - INTO X - END-STRING - *> Note: result *should* depend on dialect option zero-length literals - IF X NOT EQUAL 'PPPP' THEN - DISPLAY 'CONTENT-OF empty POINTER wrong: "'" X "'" - END-DISPLAY - END-IF - IF FUNCTION EXCEPTION-STATUS NOT = "EC-DATA-PTR-NULL" THEN - DISPLAY 'missing exception (1)' - END-DISPLAY - END-IF - ALLOCATE B INITIALIZED - SET P TO ADDRESS OF B - IF FUNCTION CONTENT-OF ( P, 1 ) NOT EQUAL SPACES THEN - DISPLAY 'CONTENT-OF allocated BASED item wrong' - END-DISPLAY - END-IF - IF FUNCTION EXCEPTION-STATUS NOT = SPACES THEN - DISPLAY 'unexpected exception (2): ' - FUNCTION EXCEPTION-STATUS - END-DISPLAY - END-IF - FREE B - SET P TO ADDRESS OF B - MOVE 'BBBB' TO X - STRING FUNCTION CONTENT-OF ( P ) - DELIMITED BY SIZE - INTO X - END-STRING - *> Note: result *should* depend on dialect option zero-length literals - IF X NOT EQUAL 'BBBB' THEN - DISPLAY 'CONTENT-OF unallocated BASED item wrong: "' X '"' - END-DISPLAY - END-IF - IF FUNCTION EXCEPTION-STATUS NOT = "EC-DATA-PTR-NULL" THEN - DISPLAY 'missing exception (2)' - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -# Invalid test. -# prog.cob:18: ANY LENGTH valid only for 01 in LIKAGE SECTION of a contained program -# "subprog", so called, is not contained. - -AT_SETUP([FUNCTION as CALL parameter BY CONTENT]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - PROCEDURE DIVISION. - PROG-MAIN. - CALL "subprog" USING BY CONTENT - FUNCTION CONCAT("Abc" "D") - STOP RUN. - END PROGRAM prog. *> bzzt - - *> ***************************** - IDENTIFICATION DIVISION. - PROGRAM-ID. subprog. - - DATA DIVISION. - LINKAGE SECTION. - 01 TESTING PIC X ANY LENGTH. - - PROCEDURE DIVISION USING TESTING. - SUBPROG-MAIN. - DISPLAY TESTING - GOBACK. - END PROGRAM subprog. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [AbcD -], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION COS]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION COS ( -0.2345 ) TO Y. - IF Y NOT = 0.972630641256258184713416962414561 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION CURRENCY-SYMBOL]) -AT_KEYWORDS([functions]) -AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC X(8) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION CURRENCY-SYMBOL TO TEST-FLD. - DISPLAY "OK" NO ADVANCING - END-DISPLAY - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION CURRENT-DATE]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD. - 02 WS-YEAR PIC 9(04). - 88 VALID-YEAR VALUE 1980 THRU 9999. - 02 WS-MONTH PIC 9(02). - 88 VALID-MONTH VALUE 01 THRU 12. - 02 WS-DAY PIC 9(02). - 88 VALID-DAY VALUE 01 THRU 31. - 02 WS-HOUR PIC 9(02). - 88 VALID-HOUR VALUE 00 THRU 23. - 02 WS-MIN PIC 9(02). - 88 VALID-MIN VALUE 00 THRU 59. - 02 WS-SEVALIDD PIC 9(02). - 88 VALID-SEC VALUE 00 THRU 59. - 02 WS-HUNDSEC PIC 9(02). - 88 VALID-HUNDSEC VALUE 00 THRU 99. - 02 WS-GREENW PIC X. - 88 VALID-GREENW VALUE "-", "+", "0". - 88 ZERO-GREENW VALUE "0". - 02 WS-OFFSET PIC 9(02). - 88 VALID-OFFSET VALUE 00 THRU 13. - 88 ZERO-OFFSET VALUE 00. - 02 WS-OFFSET2 PIC 9(02). - 88 VALID-OFFSET2 VALUE 00 THRU 59. - 88 ZERO-OFFSET2 VALUE 00. - 02 WS-UNSET PIC X VALUE '_'. - 88 VALID-UNSET VALUE '_'. - PROCEDURE DIVISION. - STRING FUNCTION CURRENT-DATE - DELIMITED BY SIZE - INTO TEST-FLD - END-STRING. - EVALUATE TRUE - WHEN NOT VALID-UNSET - DISPLAY "FUNCTION result too long" - END-DISPLAY - WHEN VALID-YEAR AND - VALID-MONTH AND - VALID-DAY AND - VALID-HOUR AND - VALID-MIN AND - VALID-SEC AND - VALID-HUNDSEC AND - VALID-GREENW AND - VALID-OFFSET AND - VALID-OFFSET2 AND - VALID-UNSET AND - ((NOT ZERO-GREENW) OR (ZERO-OFFSET AND ZERO-OFFSET2)) - CONTINUE - WHEN OTHER - DISPLAY "CURRENT-DATE with wrong format: " - TEST-FLD (01:21) - END-DISPLAY - END-EVALUATE. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION DATE-OF-INTEGER]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION DATE-OF-INTEGER ( 146000 ) - TO TEST-FLD. - IF TEST-FLD NOT = 20000925 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION DATE-TO-YYYYMMDD]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION DATE-TO-YYYYMMDD ( 981002, -10, 1994 ) - TO TEST-FLD. - IF TEST-FLD NOT = 018981002 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION DAY-OF-INTEGER]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION DAY-OF-INTEGER ( 146000 ) - TO TEST-FLD. - IF TEST-FLD NOT = 2000269 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION DAY-TO-YYYYDDD]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION DAY-TO-YYYYDDD ( 95005, -10, 2013 ) - TO TEST-FLD. - IF TEST-FLD NOT = 001995005 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION E]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC 9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION E TO Y. - IF Y NOT = 2.718281828459045235360287471352662 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION EXCEPTION-FILE]) -AT_KEYWORDS([functions exceptions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - INPUT-OUTPUT SECTION. - FILE-CONTROL. - SELECT TEST-FILE ASSIGN "NOTEXIST" - FILE STATUS IS TEST-STATUS. - DATA DIVISION. - FILE SECTION. - FD TEST-FILE. - 01 TEST-REC PIC X(4). - WORKING-STORAGE SECTION. - 01 TEST-STATUS PIC XX. - PROCEDURE DIVISION. - DISPLAY FUNCTION EXCEPTION-FILE '|' - NO ADVANCING - END-DISPLAY. - OPEN INPUT TEST-FILE. - DISPLAY FUNCTION EXCEPTION-FILE - NO ADVANCING - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[00|35TEST-FILE], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION EXCEPTION-LOCATION]) -AT_KEYWORDS([functions exceptions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - INPUT-OUTPUT SECTION. - FILE-CONTROL. - SELECT TEST-FILE ASSIGN "NOTEXIST" - FILE STATUS IS TEST-STATUS. - DATA DIVISION. - FILE SECTION. - FD TEST-FILE. - 01 TEST-REC PIC X(4). - WORKING-STORAGE SECTION. - 01 TEST-STATUS PIC XX. - PROCEDURE DIVISION. - A00-MAIN SECTION. - A00. - DISPLAY FUNCTION EXCEPTION-LOCATION '|' - NO ADVANCING - END-DISPLAY. - OPEN INPUT TEST-FILE. - B00-MAIN SECTION. - B00. - DISPLAY FUNCTION EXCEPTION-LOCATION - NO ADVANCING - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[ |prog; A00 OF A00-MAIN; 21], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION EXCEPTION-STATEMENT]) -AT_KEYWORDS([functions exceptions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - INPUT-OUTPUT SECTION. - FILE-CONTROL. - SELECT TEST-FILE ASSIGN "NOTEXIST" - FILE STATUS IS TEST-STATUS. - DATA DIVISION. - FILE SECTION. - FD TEST-FILE. - 01 TEST-REC PIC X(4). - WORKING-STORAGE SECTION. - 01 TEST-STATUS PIC XX. - PROCEDURE DIVISION. - DISPLAY FUNCTION EXCEPTION-STATEMENT '|' - NO ADVANCING - END-DISPLAY. - OPEN INPUT TEST-FILE. - DISPLAY FUNCTION EXCEPTION-STATEMENT - NO ADVANCING - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[ |OPEN ], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION EXCEPTION-STATUS]) -AT_KEYWORDS([functions exceptions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - INPUT-OUTPUT SECTION. - FILE-CONTROL. - SELECT TEST-FILE ASSIGN "NOTEXIST" - FILE STATUS IS TEST-STATUS. - DATA DIVISION. - FILE SECTION. - FD TEST-FILE. - 01 TEST-REC PIC X(4). - WORKING-STORAGE SECTION. - 01 TEST-STATUS PIC XX. - PROCEDURE DIVISION. - DISPLAY FUNCTION EXCEPTION-STATUS '|' - NO ADVANCING - END-DISPLAY. - OPEN INPUT TEST-FILE. - DISPLAY FUNCTION EXCEPTION-STATUS - NO ADVANCING - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[ |EC-I-O-PERMANENT-ERROR ], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION EXP]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S99V9(31). - PROCEDURE DIVISION. - MOVE FUNCTION EXP ( 3 ) TO Y. - IF Y NOT = 20.0855369231876677409285296545817 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION EXP10]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION EXP10 ( 4 ) - TO TEST-FLD. - IF TEST-FLD NOT = 000010000 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FACTORIAL]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION FACTORIAL ( 6 ) - TO TEST-FLD. - IF TEST-FLD NOT = 000000720 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-CURRENT-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Datetime-Format CONSTANT "YYYYMMDDThhmmss.ss+hhmm". - 01 str PIC X(25). - PROCEDURE DIVISION. - *> Test normal inputs. - MOVE FUNCTION FORMATTED-CURRENT-DATE ( Datetime-Format ) - TO str - IF FUNCTION TEST-FORMATTED-DATETIME ( Datetime-Format, str) - <> 0 - DISPLAY "Test 1 failed: " str END-DISPLAY - END-IF. - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(10). - PROCEDURE DIVISION. - *> Test normal inputs. - MOVE FUNCTION FORMATTED-DATE ( "YYYYMMDD", 1 ) TO str - IF str <> "16010101" - DISPLAY "Test 1 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATE ( "YYYY-MM-DD", 1 ) TO str - IF str <> "1601-01-01" - DISPLAY "Test 2 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATE ( "YYYYDDD", 1 ) TO str - IF str <> "1601001" - DISPLAY "Test 3 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATE ( "YYYY-DDD", 1 ) TO str - IF str <> "1601-001" - DISPLAY "Test 4 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATE ( "YYYYWwwD", 1 ) TO str - IF str <> "1601W011" - DISPLAY "Test 5 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATE ( "YYYY-Www-D", 1 ) TO str - IF str <> "1601-W01-1" - DISPLAY "Test 6 failed: " str END-DISPLAY - END-IF - - *> Test week number edge cases. - *> For 2012-01-01. - MOVE FUNCTION FORMATTED-DATE ( "YYYYWwwD", 150115 ) TO str - IF str <> "2011W527" - DISPLAY "Test 7 failed: " str END-DISPLAY - END-IF - - *> and for 2013-12-30. - MOVE FUNCTION FORMATTED-DATE ( "YYYYWwwD", 150844 ) TO str - IF str <> "2014W011" - DISPLAY "Test 8 failed: " str END-DISPLAY - END-IF - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-DATE with ref modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(04). - PROCEDURE DIVISION. - MOVE FUNCTION FORMATTED-DATE ("YYYYMMDD", 1) (3:4) - TO STR - IF STR NOT = '0101' - DISPLAY STR - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-DATETIME]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(40). - PROCEDURE DIVISION. - *> Test normal inputs. - MOVE FUNCTION FORMATTED-DATETIME - ("YYYYMMDDThhmmss", 1, 45296) - TO str - IF str <> "16010101T123456" - DISPLAY "Test 1 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATETIME - ("YYYY-MM-DDThh:mm:ss", 1, 45296) - TO str - IF str <> "1601-01-01T12:34:56" - DISPLAY "Test 2 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATETIME - ("YYYYDDDThhmmss+hhmm", 1, 45296, -754) - TO str - IF str <> "1601001T123456-1234" - DISPLAY "Test 3 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-DATETIME - ("YYYYDDDThhmmss+hhmm", 1, 45296) - TO str - IF str <> "1601001T123456+0000" - DISPLAY "Test 4 failed: " str END-DISPLAY - END-IF - - *> Test underflow to next day due to offset - MOVE FUNCTION FORMATTED-DATETIME - ("YYYYDDDThhmmss.sssssssssZ", 150846, 0, - 1) - TO str - IF str <> "2013365T235900.000000000Z" - DISPLAY "Test 5 failed: " str END-DISPLAY - END-IF - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-DATETIME with ref modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(04). - PROCEDURE DIVISION. - MOVE FUNCTION FORMATTED-DATETIME - ("YYYYMMDDThhmmss", 1, 1) (3:4) - TO STR - IF STR NOT = '0101' - DISPLAY STR - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-TIME]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(20). - PROCEDURE DIVISION. - *> Test normal inputs. - MOVE FUNCTION FORMATTED-TIME ( "hhmmss", 45296 ) TO str - IF str <> "123456" - DISPLAY "Test 1 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ss", 45296 ) TO str - IF str <> "12:34:56" - DISPLAY "Test 2 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hhmmssZ", 86399, -1 ) TO str - IF str <> "000059Z" - DISPLAY "Test 3 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ssZ", 45296) - TO str - IF str <> "12:34:56Z" - DISPLAY "Test 4 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hhmmss.ss", 45296.78 ) TO str - IF str <> "123456.78" - DISPLAY "Test 5 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ss.ssZ", 0, 120) - TO str - IF str <> "22:00:00.00Z" - DISPLAY "Test 6 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 45296) - TO str - IF str <> "123456+0000" - DISPLAY "Test 7 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ss+hh:mm", 45296, 0 ) - TO str - IF str <> "12:34:56+00:00" - DISPLAY "Test 8 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 45296, -754) - TO str - IF str <> "123456-1234" - DISPLAY "Test 9 failed: " str END-DISPLAY - END-IF - - *> Test with invalid/missing offset times. - MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 1, 3000 ) - TO str - IF str <> SPACES - OR FUNCTION EXCEPTION-STATUS <> "EC-ARGUMENT-FUNCTION" - OR FUNCTION EXCEPTION-LOCATION <> "prog; ; 60" - DISPLAY "Test 10 failed: " str END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 1, -3000 ) - TO str - IF str <> SPACES - OR FUNCTION EXCEPTION-STATUS <> "EC-ARGUMENT-FUNCTION" - OR FUNCTION EXCEPTION-LOCATION <> "prog; ; 68" - DISPLAY "Test 11 failed: " str END-DISPLAY - END-IF - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-TIME DP.COMMA]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - SPECIAL-NAMES. - DECIMAL-POINT IS COMMA. - - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(11). - - PROCEDURE DIVISION. - MOVE FUNCTION FORMATTED-TIME ("hh:mm:ss,ss", 45296) TO str - IF str <> "12:34:56,00" - DISPLAY "Test 1 failed: " str END-DISPLAY - END-IF - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FORMATTED-TIME with ref modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(04). - PROCEDURE DIVISION. - MOVE FUNCTION FORMATTED-TIME ("hhmmss", 45296) (3:4) - TO STR - IF STR NOT = '3456' - DISPLAY STR - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION FRACTION-PART]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(04)V9(08). - PROCEDURE DIVISION. - MOVE FUNCTION FRACTION-PART ( 3.12345 ) - TO TEST-FLD. - IF TEST-FLD NOT = +0000.12345 - DISPLAY 'FRACTION-PART ( +3.12345 ) wrong: ' TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION FRACTION-PART ( -3.12345 ) - TO TEST-FLD. - IF TEST-FLD NOT = -0000.12345 - DISPLAY 'FRACTION-PART ( -3.12345 ) wrong: ' TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION HIGHEST-ALGEBRAIC]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 F1 PIC S999. - 01 F2 PIC S9(4) BINARY. - 01 F3 PIC 99V9(3). - 01 F4 PIC $**,**9.99BCR. - 01 F5 PIC $**,**9.99. - 01 F6 USAGE BINARY-CHAR SIGNED. - 01 F7 USAGE BINARY-CHAR UNSIGNED. - 01 TEST-FLD PIC S9(08)V9(04). - PROCEDURE DIVISION. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F1) - TO TEST-FLD. - IF TEST-FLD NOT = 999 - DISPLAY "Test 1 fail: " TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F2) - TO TEST-FLD. - IF TEST-FLD NOT = 9999 - DISPLAY "Test 2 fail: " TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F3) - TO TEST-FLD. - IF TEST-FLD NOT = 99.999 - DISPLAY "Test 3 fail: " TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F4) - TO TEST-FLD. - IF TEST-FLD NOT = 99999.99 - DISPLAY "Test 4 fail: " TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F5) - TO TEST-FLD. - IF TEST-FLD NOT = 99999.99 - DISPLAY "Test 5 fail: " TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F6) - TO TEST-FLD. - IF TEST-FLD NOT = 127 - DISPLAY "Test 6 fail: " TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION HIGHEST-ALGEBRAIC (F7) - TO TEST-FLD. - IF TEST-FLD NOT = 255 - DISPLAY "Test 7 fail: " TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION INTEGER]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC S9(4)V9(4) VALUE -1.5. - 01 Y PIC 9(12) VALUE 600851475143. - 01 TEST-FLD PIC S9(14)V9(08). - PROCEDURE DIVISION. - MOVE FUNCTION INTEGER ( X ) - TO TEST-FLD. - IF TEST-FLD NOT = -2 - DISPLAY 'INTEGER ( X ) wrong: ' TEST-FLD - END-DISPLAY - END-IF. - MOVE FUNCTION INTEGER ( Y / 71 ) - TO TEST-FLD. - IF TEST-FLD NOT = 8462696833 - DISPLAY 'INTEGER ( Y / 71 ) wrong: ' TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION INTEGER-OF-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION INTEGER-OF-DATE ( 20000925 ) - TO TEST-FLD. - IF TEST-FLD NOT = 000146000 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION INTEGER-OF-DAY]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC S9(09)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION INTEGER-OF-DAY ( 2000269 ) - TO TEST-FLD. - IF TEST-FLD NOT = 000146000 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION INTEGER-OF-FORMATTED-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 day-int PIC 9(9). - - PROCEDURE DIVISION. - *> The date 2013-12-30 is used as it can also be used to - *> check the conversion of dates in week form. - MOVE FUNCTION INTEGER-OF-FORMATTED-DATE - ("YYYY-MM-DD", "2013-12-30") - TO day-int - IF day-int <> 150844 - DISPLAY "Test 1 failed: " day-int END-DISPLAY - END-IF - - MOVE FUNCTION INTEGER-OF-FORMATTED-DATE - ("YYYY-DDD", "2013-364") - TO day-int - IF day-int <> 150844 - DISPLAY "Test 2 failed: " day-int END-DISPLAY - END-IF - - MOVE FUNCTION INTEGER-OF-FORMATTED-DATE - ("YYYY-Www-D", "2014-W01-1") - TO day-int - IF day-int <> 150844 - DISPLAY "Test 3 failed: " day-int END-DISPLAY - END-IF - - MOVE FUNCTION INTEGER-OF-FORMATTED-DATE - ("YYYY-MM-DDThh:mm:ss", "2013-12-30T12:34:56") - TO day-int - IF day-int <> 150844 - DISPLAY "Test 4 failed: " day-int END-DISPLAY - END-IF - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION INTEGER-PART]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC S9(4)V9(4) VALUE -1.5. - 01 TEST-FLD PIC S9(04)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION INTEGER-PART ( X ) - TO TEST-FLD. - IF TEST-FLD NOT = -1 - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LENGTH]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC S9(4)V9(4) VALUE -1.5. - 01 N PIC N(9). - 01 TEST-FLD PIC S9(04)V9(02). - PROCEDURE DIVISION. - MOVE FUNCTION LENGTH ( X ) - TO TEST-FLD - IF TEST-FLD NOT = 8 - DISPLAY 'LENGTH "00128" wrong: ' TEST-FLD - END-DISPLAY - END-IF - MOVE FUNCTION LENGTH ( N ) - TO TEST-FLD - IF TEST-FLD NOT = 9 - DISPLAY 'LENGTH N(9) wrong: ' TEST-FLD - END-DISPLAY - END-IF - - MOVE FUNCTION LENGTH ( '00128' ) - TO TEST-FLD - IF TEST-FLD NOT = 5 - DISPLAY 'LENGTH "00128" wrong: ' TEST-FLD - END-DISPLAY - END-IF - * note: we currently do not support items of category boolean... - *> MOVE FUNCTION LENGTH ( b'100' ) - *> TO TEST-FLD - *> IF TEST-FLD NOT = 3 - *> DISPLAY 'LENGTH b"100" wrong: ' TEST-FLD - *> END-DISPLAY - *> END-IF - MOVE FUNCTION LENGTH ( x'a0' ) - TO TEST-FLD - IF TEST-FLD NOT = 1 - DISPLAY 'LENGTH x"a0" wrong: ' TEST-FLD - END-DISPLAY - END-IF - MOVE FUNCTION LENGTH ( z'a0' ) - TO TEST-FLD - IF TEST-FLD NOT = 3 - DISPLAY 'LENGTH z"a0" wrong: ' TEST-FLD - END-DISPLAY - END-IF - MOVE FUNCTION LENGTH ( n'a0' ) - TO TEST-FLD - IF TEST-FLD NOT = 2 - DISPLAY 'LENGTH n"a0" wrong: ' TEST-FLD - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], -[prog.cob:7: warning: handling of USAGE NATIONAL is unfinished; implementation is likely to be changed -prog.cob:48: warning: handling of national literal is unfinished; implementation is likely to be changed -]) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOCALE-COMPARE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION LOCALE-COMPARE ("A", "B") NOT = "<" - DISPLAY "Test 1 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOCALE-COMPARE ("B", "A") NOT = ">" - DISPLAY "Test 2 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOCALE-COMPARE ("A", "A") NOT = "=" - DISPLAY "Test 3 fail" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOCALE-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(32) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION LOCALE-DATE ( "19630302" ) TO X. - IF X NOT = SPACES - DISPLAY "OK" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[OK -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOCALE-TIME]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(32) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION LOCALE-TIME ( "233012" ) TO X. - IF X NOT = SPACES - DISPLAY "OK" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[OK -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOCALE-TIME-FROM-SECONDS]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(32) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION LOCALE-TIME-FROM-SECONDS ( 33012 ) TO X. - IF X NOT = SPACES - DISPLAY "OK" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[OK -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOG]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION LOG ( 1.5 ) TO Y. - IF Y NOT = 0.405465108108164381978013115464349 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION LOG10]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION LOG10 ( 1.5 ) TO Y. - IF Y NOT = 0.176091259055681242081289008530622 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION LOWER-CASE]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(10) VALUE "A#B.C%D+E$". - 01 TEST-FLD PIC X(12) VALUE ALL '_'. - PROCEDURE DIVISION. - STRING FUNCTION LOWER-CASE ( X ) - DELIMITED BY SIZE - INTO TEST-FLD - END-STRING - IF TEST-FLD NOT = 'a#b.c%d+e$__' - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOWER-CASE with reference modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(10) VALUE "A#B.C%D+E$". - 01 TEST-FLD PIC X(03). - PROCEDURE DIVISION. - MOVE FUNCTION LOWER-CASE ( X ) (1 : 3) - TO TEST-FLD - IF TEST-FLD NOT = 'a#b' - DISPLAY TEST-FLD - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION LOWEST-ALGEBRAIC]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 F1 PIC S999. - 01 F2 PIC S9(4) BINARY. - 01 F3 PIC 99V9(3). - 01 F4 PIC $**,**9.99BCR. - 01 F5 PIC $**,**9.99. - 01 F6 USAGE BINARY-CHAR SIGNED. - 01 F7 USAGE BINARY-CHAR UNSIGNED. - PROCEDURE DIVISION. - IF FUNCTION LOWEST-ALGEBRAIC (F1) NOT = -999 - DISPLAY "Test 1 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOWEST-ALGEBRAIC (F2) NOT = -9999 - DISPLAY "Test 2 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOWEST-ALGEBRAIC (F3) NOT = 0 - DISPLAY "Test 3 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOWEST-ALGEBRAIC (F4) NOT = -99999.99 - DISPLAY "Test 4 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOWEST-ALGEBRAIC (F5) NOT = 0 - DISPLAY "Test 5 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOWEST-ALGEBRAIC (F6) NOT = -128 - DISPLAY "Test 6 fail" - END-DISPLAY - END-IF. - IF FUNCTION LOWEST-ALGEBRAIC (F7) NOT = 0 - DISPLAY "Test 7 fail" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MAX]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - DISPLAY FUNCTION MAX ( 3 -14 0 8 -3 ) - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[8 -], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MEAN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 result PIC S999V999. - PROCEDURE DIVISION. - COMPUTE result = FUNCTION MEAN ( 3 -14 0 8 -3 ) - DISPLAY result - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[-001.200 -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MEDIAN]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - DISPLAY FUNCTION MEDIAN ( 3 -14 0 8 -3 ) - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[0 -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MIDRANGE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC S999V999. - PROCEDURE DIVISION. - COMPUTE RESULT = FUNCTION MIDRANGE ( 3 -14 0 8 -3 ) - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[-003.000 -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MIN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - DISPLAY FUNCTION MIN ( 3 -14 0 8 -3 ) - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[-14 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION MOD (valid)]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC 9(12) VALUE 600851475143. - 01 R PIC S9(4)V9(4) VALUE 0. - PROCEDURE DIVISION. - MOVE FUNCTION MOD ( -11 5 ) TO R - IF R NOT = 4 - DISPLAY 'first one wrong: ' R - END-DISPLAY - END-IF - MOVE FUNCTION MOD ( Y, 71 ) TO R - IF R NOT = 0 - DISPLAY 'second one wrong: ' R - END-DISPLAY - END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION MOD (invalid)]) -AT_KEYWORDS([functions exceptions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z PIC 9 VALUE 0. - 01 R PIC S9(4)V9(4) VALUE 1. - PROCEDURE DIVISION. - MOVE FUNCTION MOD ( -11 Z ) TO R - IF FUNCTION TRIM(FUNCTION EXCEPTION-STATUS) - NOT = 'EC-ARGUMENT-FUNCTION' - DISPLAY 'Wrong/missing exception: ' - FUNCTION EXCEPTION-STATUS - END-DISPLAY - END-IF - IF R NOT = 0 - DISPLAY 'result is not zero: ' R - END-DISPLAY - END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-CALLER-ID]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - CALL "prog2" - END-CALL. - STOP RUN. -]) - -AT_DATA([prog2.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog2. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - DISPLAY FUNCTION MODULE-CALLER-ID NO ADVANCING - END-DISPLAY. - EXIT PROGRAM. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COMPILE_MODULE prog2.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [prog]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-DATE PIC 9(8) VALUE 0. - PROCEDURE DIVISION. - MOVE FUNCTION MODULE-DATE TO TEST-DATE. - IF TEST-DATE NOT = 0 - DISPLAY "OK" NO ADVANCING - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-FORMATTED-DATE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-DATE PIC X(16) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION MODULE-FORMATTED-DATE TO TEST-DATE. - IF TEST-DATE NOT = SPACES - DISPLAY "OK" NO ADVANCING - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-ID]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - DISPLAY FUNCTION MODULE-ID NO ADVANCING - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [prog]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-PATH]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-PATH PIC X(16) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION MODULE-PATH TO TEST-PATH. - IF TEST-PATH NOT = SPACES - DISPLAY "OK" NO ADVANCING - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-SOURCE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - DISPLAY FUNCTION MODULE-SOURCE NO ADVANCING - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [prog.cob]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MODULE-TIME]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-TIME PIC 9(6) VALUE 0. - PROCEDURE DIVISION. - MOVE FUNCTION MODULE-TIME TO TEST-TIME. - IF TEST-TIME NOT = 0 - DISPLAY "OK" NO ADVANCING - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MONETARY-DECIMAL-POINT]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC X(8) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION MONETARY-DECIMAL-POINT TO TEST-FLD. - DISPLAY "OK" NO ADVANCING - END-DISPLAY - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION MONETARY-THOUSANDS-SEPARATOR]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC X(8) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION MONETARY-THOUSANDS-SEPARATOR TO TEST-FLD. - DISPLAY "OK" NO ADVANCING - END-DISPLAY - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION NUMERIC-DECIMAL-POINT]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC X(8) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION NUMERIC-DECIMAL-POINT TO TEST-FLD. - DISPLAY "OK" NO ADVANCING - END-DISPLAY - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION NUMERIC-THOUSANDS-SEPARATOR]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 TEST-FLD PIC X(8) VALUE SPACES. - PROCEDURE DIVISION. - MOVE FUNCTION NUMERIC-THOUSANDS-SEPARATOR TO TEST-FLD. - DISPLAY "OK" NO ADVANCING - END-DISPLAY - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION NUMVAL]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X1 PIC X(12) VALUE " -9876.1234 ". - 01 X2 PIC X(18) VALUE " 19876.1234 CR". - 01 N PIC s9(5)v9(5). - PROCEDURE DIVISION. - MOVE FUNCTION NUMVAL ( X1 ) TO N - IF N NOT = -9876.1234 - DISPLAY N - END-DISPLAY - END-IF - MOVE FUNCTION NUMVAL ( X2 ) TO N - IF N NOT = -19876.1234 - DISPLAY N - END-DISPLAY - END-IF - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION NUMVAL-C]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X1 PIC X(14) VALUE " -% 9876.1234 ". - 01 X2 PIC X(20) VALUE " % 19,876.1234 DB". - 01 N PIC s9(5)v9(5). - PROCEDURE DIVISION. - MOVE FUNCTION NUMVAL-C ( X1 , "%" ) TO N - IF N NOT = -9876.1234 - DISPLAY N - END-DISPLAY - END-IF - MOVE FUNCTION NUMVAL-C ( X2 , "%" ) TO N - IF N NOT = -19876.1234 - DISPLAY N - END-DISPLAY - END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION NUMVAL-C DP.COMMA]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - SPECIAL-NAMES. - DECIMAL-POINT IS COMMA - . - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X1 PIC X(20) VALUE " % 19.876,1234 DB". - 01 N PIC s9(5)v9(5). - PROCEDURE DIVISION. - MOVE FUNCTION NUMVAL-C ( X1 , "%" ) TO N - IF N NOT = -19876,1234 - DISPLAY N - END-DISPLAY - END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION NUMVAL-F]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(12) VALUE " -0.1234E+4 ". - PROCEDURE DIVISION. - DISPLAY FUNCTION NUMVAL-F ( X ) - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[-000001234 -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION ORD]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC 999. - PROCEDURE DIVISION. - MOVE FUNCTION ORD ( "k" ) TO RESULT - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[108 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION ORD-MAX]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC 999. - PROCEDURE DIVISION. - MOVE FUNCTION ORD-MAX ( 3 -14 0 8 -3 ) TO RESULT - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[004 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION ORD-MIN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC 999. - PROCEDURE DIVISION. - MOVE FUNCTION ORD-MIN ( 3 -14 0 8 -3 ) TO RESULT - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[002 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION PI]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC 9V9(32). - PROCEDURE DIVISION. - MOVE FUNCTION PI TO Y. - IF Y NOT = 3.14159265358979323846264338327950 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION PRESENT-VALUE]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC 9(5)V9(4). - PROCEDURE DIVISION. - MOVE FUNCTION PRESENT-VALUE ( 3 2 1 ) TO RESULT - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[00000.5625 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION RANDOM]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S99V99 COMP VALUE -1.0. - PROCEDURE DIVISION. - MOVE FUNCTION RANDOM ( ) TO Y. - IF Y < 0 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION RANGE]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z PIC S9(4)V9(4) COMP-5. - PROCEDURE DIVISION. - MOVE FUNCTION RANGE ( 3 -14 0 8 -3 ) TO Z. - IF Z NOT = 22 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION REM (valid)]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 R PIC S9(4)V9(4) COMP-5 VALUE 0. - PROCEDURE DIVISION. - MOVE FUNCTION REM ( -11 5 ) TO R - IF R NOT = -1 - DISPLAY R END-DISPLAY - END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION REM (invalid)]) -AT_KEYWORDS([functions exceptions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 R PIC S9(4)V9(4) COMP-5 VALUE 4.1. - 01 Z PIC 9 COMP-5 VALUE 0. - PROCEDURE DIVISION. - MOVE FUNCTION REM ( -11 Z ) TO R - IF FUNCTION TRIM(FUNCTION EXCEPTION-STATUS) - NOT = 'EC-ARGUMENT-FUNCTION' - DISPLAY 'Wrong/missing exception: ' - FUNCTION EXCEPTION-STATUS - END-DISPLAY - END-IF - IF R NOT = 0 - DISPLAY 'result is not zero: ' R - END-DISPLAY - END-IF - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION REVERSE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(10) VALUE "A#B.C%D+E$". - 01 Z PIC X(10). - PROCEDURE DIVISION. - MOVE FUNCTION REVERSE ( X ) TO Z. - IF Z NOT = "$E+D%C.B#A" - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION REVERSE with reference modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(10) VALUE "A#B.C%D+E$". - 01 Z PIC X(10). - PROCEDURE DIVISION. - MOVE FUNCTION REVERSE ( X ) (1 : 4) TO Z. - IF Z NOT = "$E+D " - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION SECONDS-FROM-FORMATTED-TIME]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 result PIC 9(8)V9(9) COMP-5. - PROCEDURE DIVISION. - MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME - ("hhmmss", "010203") - TO result. - IF result NOT = 3723 - DISPLAY "Test 1 failed: " result - END-DISPLAY - END-IF. - - MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME - ("hh:mm:ss", "01:02:03") - TO result. - IF result NOT = 3723 - DISPLAY "Test 2 failed: " result - END-DISPLAY - END-IF. - - MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME - ("hhmmss.ssssssss", "010203.04050607") - TO result. - IF result NOT = 3723.04050607 - DISPLAY "Test 3 failed: " result - END-DISPLAY - END-IF. - - MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME - ("hhmmssZ", "010203Z") - TO result. - IF result NOT = 3723 - DISPLAY "Test 4 failed: " result - END-DISPLAY - END-IF. - - MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME - ("hhmmss+hhmm", "010203+0405") - TO result. - IF result NOT = 3723 - DISPLAY "Test 5 failed: " result - END-DISPLAY - END-IF. - - MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME - ("YYYYMMDDThhmmss", "16010101T010203") - TO result. - IF result NOT = 3723 - DISPLAY "Test 6 failed: " result - END-DISPLAY - END-IF. - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION SECONDS-PAST-MIDNIGHT]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC 9(8) COMP-5. - PROCEDURE DIVISION. - MOVE FUNCTION SECONDS-PAST-MIDNIGHT TO Y. - IF Y NOT < 86402 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION SIGN]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z USAGE BINARY-LONG SIGNED. - PROCEDURE DIVISION. - MOVE FUNCTION SIGN ( 3.12345 ) TO Z. - IF Z NOT = 1 - DISPLAY "Sign 1 " Z - END-DISPLAY - END-IF. - MOVE FUNCTION SIGN ( -0.0 ) TO Z. - IF Z NOT = 0 - DISPLAY "Sign 2 " Z - END-DISPLAY - END-IF. - MOVE FUNCTION SIGN ( 0.0 ) TO Z. - IF Z NOT = 0 - DISPLAY "Sign 3 " Z - END-DISPLAY - END-IF. - MOVE FUNCTION SIGN ( -3.12345 ) TO Z. - IF Z NOT = -1 - DISPLAY "Sign 4 " Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION SIN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION SIN ( 1.5 ) TO Y. - IF Y NOT = 0.997494986604054430941723371141487 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION SQRT]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(33). - PROCEDURE DIVISION. - MOVE FUNCTION SQRT ( 1.5 ) TO Y. - IF Y NOT = 1.224744871391589049098642037352945 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION STANDARD-DEVIATION]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S9V9(32). - PROCEDURE DIVISION. - MOVE FUNCTION STANDARD-DEVIATION ( 3 -14 0 8 -3 ) TO Y. - IF Y NOT = 7.35934779718963954877237043574538 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION STORED-CHAR-LENGTH]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(24). - 01 Z USAGE BINARY-LONG. - PROCEDURE DIVISION. - MOVE "123456789012" TO Y. - MOVE FUNCTION STORED-CHAR-LENGTH ( Y ) TO Z. - IF Z NOT = 12 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION SUBSTITUTE]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(20). - 01 Z PIC X(20) VALUE ALL '_'. - PROCEDURE DIVISION. - MOVE "abc111444555defxxabc" TO Y. - STRING FUNCTION SUBSTITUTE ( Y "abc" "zz" "55" "666" ) - DELIMITED BY SIZE - INTO Z - END-STRING - IF Z NOT = "zz1114446665defxxzz_" - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION SUBSTITUTE with reference modding]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(20). - 01 Z PIC X(20). - PROCEDURE DIVISION. - MOVE "abc111444555defxxabc" TO Y. - MOVE FUNCTION SUBSTITUTE - ( Y "abc" "zz" "55" "666" ) (2 : 9) - TO Z. - IF Z NOT = "z11144466" - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION SUBSTITUTE-CASE]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(20). - 01 Z PIC X(20). - PROCEDURE DIVISION. - MOVE "ABC111444555defxxabc" TO Y. - MOVE FUNCTION SUBSTITUTE-CASE (Y "abc" "zz" "55" "666") - TO Z. - IF Z NOT = "zz1114446665defxxzz" - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION SUBSTITUTE-CASE with reference mod]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC X(20). - 01 Z PIC X(20). - PROCEDURE DIVISION. - MOVE "abc111444555defxxabc" TO Y. - MOVE FUNCTION SUBSTITUTE-CASE - ( Y "ABC" "zz" "55" "666" ) (2 : 9) - TO Z. - IF Z NOT = "z11144466" - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION SUM]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z USAGE BINARY-LONG. - PROCEDURE DIVISION. - MOVE FUNCTION SUM ( 3 -14 0 8 -3 ) TO Z. - IF Z NOT = -6 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION TAN]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Y PIC S99V9(31). - PROCEDURE DIVISION. - MOVE FUNCTION TAN ( 1.5 ) TO Y. - IF Y NOT = 14.1014199471717193876460836519877 - DISPLAY Y - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-DATE-YYYYMMDD]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC 999. - PROCEDURE DIVISION. - MOVE FUNCTION TEST-DATE-YYYYMMDD ( 20020231 ) TO RESULT - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[003 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-DAY-YYYYDDD]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RESULT PIC 999. - PROCEDURE DIVISION. - MOVE FUNCTION TEST-DAY-YYYYDDD ( 2002400 ) TO RESULT - DISPLAY RESULT - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[002 -]) -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME with dates]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "16010101") <> 0 - DISPLAY "Test 1 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYY-MM-DD", "1601-01-01") <> 0 - DISPLAY "Test 2 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYDDD", "1601001") <> 0 - DISPLAY "Test 3 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYY-DDD", "1601-001") <> 0 - DISPLAY "Test 4 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYWwwD", "1601W011") <> 0 - DISPLAY "Test 5 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYY-Www-D", "1601-W01-1") <> 0 - DISPLAY "Test 6 failed" END-DISPLAY - END-IF - - - *> How will this work with zero-length items? - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "1") <> 2 - DISPLAY "Test 7 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "160A0101") <> 4 - DISPLAY "Test 8 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "00000101") <> 1 - DISPLAY "Test 9 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "16000101") <> 4 - DISPLAY "Test 10 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "16010001") <> 6 - DISPLAY "Test 11 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "16011301") <> 6 - DISPLAY "Test 12 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "16010190") <> 7 - DISPLAY "Test 13 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "18000229") <> 8 - DISPLAY "Test 14 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYY-MM-DD", "1601 01 01") <> 5 - DISPLAY "Test 15 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "160101010") <> 9 - DISPLAY "Test 16 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYWwwD", "1601A011") <> 5 - DISPLAY "Test 17 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYWwwD", "1601W531") <> 7 - DISPLAY "Test 18 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYWwwD", "1601W601") <> 6 - DISPLAY "Test 19 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYWwwD", "2009W531") <> 0 - DISPLAY "Test 20 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYWwwD", "1601W018") <> 8 - DISPLAY "Test 21 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYDDD", "1601366") <> 7 - DISPLAY "Test 22 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYDDD", "1601370") <> 6 - DISPLAY "Test 23 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYDDD", "1601400") <> 5 - DISPLAY "Test 24 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "01") <> 1 - DISPLAY "Test 25 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDD", "1601010") <> 8 - DISPLAY "Test 26 failed" END-DISPLAY - END-IF - - STOP RUN - . -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME with times]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss.sssssssssZ", "000000.000000000Z") <> 0 - DISPLAY "Test 1 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hh:mm:ss.sssssssssZ", "00:00:00.000000000Z") <> 0 - DISPLAY "Test 2 failed" END-DISPLAY - END-IF - *> 0 instead of +/- valid in sending fields with offset of zero. - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss.sssssssss+hhmm", "000000.00000000000000") - <> 0 - DISPLAY "Test 3 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hh:mm:ss.sssssssss+hh:mm", - "00:00:00.000000000+00:00") - <> 0 - DISPLAY "Test 4 failed" END-DISPLAY - END-IF - - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss", "300000") <> 1 - DISPLAY "Test 5 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss", "250000") <> 2 - DISPLAY "Test 6 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss", "006000") <> 3 - DISPLAY "Test 7 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss", "000060") <> 5 - DISPLAY "Test 8 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hh:mm:ss", "00-00-00") <> 3 - DISPLAY "Test 9 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss.ss", "000000,00") <> 7 - DISPLAY "Test 10 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss+hhmm", "000000 0000") <> 7 - DISPLAY "Test 11 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss+hhmm", "00000000001") <> 11 - DISPLAY "Test 12 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmssZ", "000000A") <> 7 - DISPLAY "Test 13 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss", SPACE) <> 1 - DISPLAY "Test 14 failed" END-DISPLAY - END-IF - - STOP RUN - . -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME with datetimes]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 RESULT PIC 9(02). - PROCEDURE DIVISION. - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDDThhmmss", "16010101T000000") - TO RESULT - IF RESULT <> 0 - DISPLAY "Test 1 failed: " RESULT END-DISPLAY - END-IF - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("YYYY-MM-DDThh:mm:ss.sssssssss+hh:mm", - "1601-01-01T00:00:00.000000000+00:00") - TO RESULT - IF RESULT <> 0 - DISPLAY "Test 2 failed: " RESULT END-DISPLAY - END-IF - - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDDThhmmss", "16010101 000000") - TO RESULT - IF RESULT <> 9 - DISPLAY "Test 3 failed: " RESULT END-DISPLAY - END-IF - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDDThhmmss", SPACE) - TO RESULT - IF RESULT <> 1 - DISPLAY "Test 4 failed: " RESULT END-DISPLAY - END-IF - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDDThhmmss", "16010101T ") - TO RESULT - IF RESULT <> 10 - DISPLAY "Test 5 failed: " RESULT END-DISPLAY - END-IF - - STOP RUN - . -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME DP.COMMA]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - SPECIAL-NAMES. - DECIMAL-POINT IS COMMA. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss,ss", "000000,00") <> 0 - DISPLAY "Test 1 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDDThhmmss,ss", "16010101T000000,00") <> 0 - DISPLAY "Test 2 failed" END-DISPLAY - END-IF - - IF FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss,ss", "000000.00") <> 7 - DISPLAY "Test 3 failed" END-DISPLAY - END-IF - IF FUNCTION TEST-FORMATTED-DATETIME - ("YYYYMMDDThhmmss,ss", "16010101T000000.00") <> 16 - DISPLAY "Test 4 failed" END-DISPLAY - END-IF - - STOP RUN - . -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-NUMVAL]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION TEST-NUMVAL ("+ 1") NOT = 0 - DISPLAY "Test 1 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL (" + 1") NOT = 0 - DISPLAY "Test 2 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("- 1") NOT = 0 - DISPLAY "Test 3 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL (" - 1") NOT = 0 - DISPLAY "Test 4 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("+- 1") NOT = 2 - DISPLAY "Test 5 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1 +") NOT = 0 - DISPLAY "Test 6 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1 -") NOT = 0 - DISPLAY "Test 7 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1 +-") NOT = 4 - DISPLAY "Test 8 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1 -+") NOT = 4 - DISPLAY "Test 9 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("+ 1.1") NOT = 0 - DISPLAY "Test 10 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("- 1.1") NOT = 0 - DISPLAY "Test 11 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 +") NOT = 0 - DISPLAY "Test 12 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 -") NOT = 0 - DISPLAY "Test 13 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 CR") NOT = 0 - DISPLAY "Test 14 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 DB") NOT = 0 - DISPLAY "Test 15 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 -CR") NOT = 6 - DISPLAY "Test 16 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 +DB") NOT = 6 - DISPLAY "Test 17 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("1.1 CDB") NOT = 6 - DISPLAY "Test 18 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("+1.1 CR") NOT = 6 - DISPLAY "Test 19 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL ("+ ") NOT = 8 - DISPLAY "Test 20 fail" - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-NUMVAL-C]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION TEST-NUMVAL-C ("+ 1") NOT = 0 - DISPLAY "Test 1 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C (" + 1") NOT = 0 - DISPLAY "Test 2 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("- 1") NOT = 0 - DISPLAY "Test 3 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C (" - 1") NOT = 0 - DISPLAY "Test 4 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("+- 1") NOT = 2 - DISPLAY "Test 5 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1 +") NOT = 0 - DISPLAY "Test 6 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1 -") NOT = 0 - DISPLAY "Test 7 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1 +-") NOT = 4 - DISPLAY "Test 8 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1 -+") NOT = 4 - DISPLAY "Test 9 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("+ 1.1") NOT = 0 - DISPLAY "Test 10 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("- 1.1") NOT = 0 - DISPLAY "Test 11 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1.1 +") NOT = 0 - DISPLAY "Test 12 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1.1 -") NOT = 0 - DISPLAY "Test 13 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1.1 CR") NOT = 0 - DISPLAY "Test 14 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1.1 DB") NOT = 0 - DISPLAY "Test 15 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("1.1 -CR") NOT = 6 - DISPLAY "Test 16 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("+ $1.1 ") NOT = 0 - DISPLAY "Test 17 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("- $1.1 ") NOT = 0 - DISPLAY "Test 18 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("+ X1.1 ", "X") NOT = 0 - DISPLAY "Test 19 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-C ("- X1.1 ", "X") NOT = 0 - DISPLAY "Test 20 fail" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TEST-NUMVAL-F]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - PROCEDURE DIVISION. - IF FUNCTION TEST-NUMVAL-F ("+ 1") NOT = 0 - DISPLAY "Test 1 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F (" + 1") NOT = 0 - DISPLAY "Test 2 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("- 1") NOT = 0 - DISPLAY "Test 3 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F (" - 1") NOT = 0 - DISPLAY "Test 4 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("+- 1") NOT = 2 - DISPLAY "Test 5 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1 +") NOT = 0 - DISPLAY "Test 6 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1 -") NOT = 0 - DISPLAY "Test 7 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1 +-") NOT = 4 - DISPLAY "Test 8 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1 -+") NOT = 4 - DISPLAY "Test 9 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("+ 1.1") NOT = 0 - DISPLAY "Test 10 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("- 1.1") NOT = 0 - DISPLAY "Test 11 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 +") NOT = 0 - DISPLAY "Test 12 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 -") NOT = 0 - DISPLAY "Test 13 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 ") NOT = 0 - DISPLAY "Test 14 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 ") NOT = 0 - DISPLAY "Test 15 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 -CR") NOT = 6 - DISPLAY "Test 16 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 E+1") NOT = 0 - DISPLAY "Test 17 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 E -1") NOT = 0 - DISPLAY "Test 18 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("1.1 EE") NOT = 6 - DISPLAY "Test 19 fail" - END-DISPLAY - END-IF. - IF FUNCTION TEST-NUMVAL-F ("+1.1 E001") NOT = 7 - DISPLAY "Test 20 fail" - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TRIM]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(12) VALUE " a#b.c%d+e$ ". - PROCEDURE DIVISION. - DISPLAY FUNCTION TRIM ( X ) - END-DISPLAY. - DISPLAY FUNCTION TRIM ( X TRAILING ) - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[a#b.c%d+e$ - a#b.c%d+e$ -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TRIM with reference modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(12) VALUE " a#b.c%d+e$ ". - PROCEDURE DIVISION. - DISPLAY FUNCTION TRIM ( X ) (2 : 3) - END-DISPLAY. - DISPLAY FUNCTION TRIM ( X TRAILING ) (2 : 3) - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[#b. -a#b -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION TRIM zero length]) -AT_KEYWORDS([functions]) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 A2 PIC X(2) VALUE " ". - 01 A3 PIC X(3) VALUE " ". - 01 X PIC X(4) VALUE "NOOK". - PROCEDURE DIVISION. - MOVE FUNCTION TRIM ( A2 ) TO X. - DISPLAY ">" X "<" - END-DISPLAY. - DISPLAY ">" FUNCTION TRIM ( A3 ) "<" - END-DISPLAY. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[> < ->< -]) - -AT_CLEANUP - - -AT_SETUP([FUNCTION UPPER-CASE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(10) VALUE "a#b.c%d+e$". - 01 Z PIC X(10). - PROCEDURE DIVISION. - MOVE FUNCTION UPPER-CASE ( X ) TO Z. - IF Z NOT = "A#B.C%D+E$" - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION UPPER-CASE with reference modding]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 X PIC X(10) VALUE "a#b.c%d+e$". - 01 Z PIC X(4). - PROCEDURE DIVISION. - MOVE FUNCTION UPPER-CASE ( X ) (1 : 3) TO Z. - IF Z NOT = "A#B " - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION VARIANCE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z PIC S9(4)V9(4) COMP-5. - PROCEDURE DIVISION. - MOVE FUNCTION VARIANCE ( 3 -14 0 8 -3 ) TO Z. - IF Z NOT = 54.16 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION WHEN-COMPILED]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 compiled-datetime. - 03 compiled-date. - 05 millennium PIC X. - 05 FILLER PIC X(15). - 03 timezone PIC X(5). - PROCEDURE DIVISION. - *> Check millennium. - MOVE FUNCTION WHEN-COMPILED TO compiled-datetime. - IF millennium NOT = "2" - DISPLAY "Millennium NOT OK: " millennium - END-DISPLAY - END-IF. - - *> Check timezone. - IF timezone NOT = FUNCTION CURRENT-DATE (17:5) - DISPLAY "Timezone NOT OK: " timezone - END-DISPLAY - END-IF. - - *> Check date format. - INSPECT compiled-date CONVERTING "0123456789" - TO "9999999999". - IF compiled-date NOT = ALL "9" - DISPLAY "Date format NOT OK: " compiled-date - END-DISPLAY - END-IF. - - *> Check timezone format. - IF timezone NOT = "00000" - INSPECT timezone CONVERTING "0123456789" - TO "9999999999" - IF timezone NOT = "+9999" AND "-9999" - DISPLAY "Timezone format NOT OK: " timezone - END-DISPLAY - END-IF - END-IF. - - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - -AT_SETUP([FUNCTION YEAR-TO-YYYY]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 Z USAGE BINARY-LONG. - PROCEDURE DIVISION. - MOVE FUNCTION YEAR-TO-YYYY ( 50 ) TO Z. - IF Z NOT = 2050 - DISPLAY Z - END-DISPLAY - END-IF. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - -AT_CLEANUP - - AT_SETUP([Formatted funcs w/ invalid variable format]) AT_KEYWORDS([functions FORMATTED-CURRENT-DATE FORMATTED-DATE FORMATTED-TIME FORMATTED-DATETIME]) @@ -3938,50 +102,8 @@ AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) AT_CLEANUP -AT_SETUP([FORMATTED-(DATE)TIME with SYSTEM-OFFSET]) -AT_KEYWORDS([functions FORMATTED-TIME FORMATTED-DATETIME extensions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 str PIC X(30). - 77 val pic 9(02). - - PROCEDURE DIVISION. - MOVE FUNCTION FORMATTED-DATETIME - ("YYYYDDDThhmmss+hhmm", 1, 45296, SYSTEM-OFFSET) - TO str - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("YYYYDDDThhmmss+hhmm", str) TO val - IF val not = 0 - DISPLAY "Test 1 failed: " str ' - ' val END-DISPLAY - END-IF - - MOVE FUNCTION FORMATTED-TIME - ("hhmmss.ssZ", 45296, SYSTEM-OFFSET) - TO str - MOVE FUNCTION TEST-FORMATTED-DATETIME - ("hhmmss.ssZ", str) TO val - IF val not = 0 - DISPLAY "Test 2 failed: " str ' - ' val END-DISPLAY - END-IF - . -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0]) - -AT_CLEANUP - - AT_SETUP([Intrinsics without FUNCTION keyword (1)]) AT_KEYWORDS([functions]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# GNU -f - AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. PROGRAM-ID. prog. @@ -3993,22 +115,24 @@ AT_DATA([prog.cob], [ MOVE E TO Z. STOP RUN. ]) - -AT_CHECK([$COMPILE -fintrinsics=all prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - +AT_CHECK([$COMPILE prog.cob], [1], [], [prog.cob:8: syntax error: symbol 'PI' not found at 'TO' +prog.cob:9: syntax error: symbol 'E' not found at 'TO' +prog.cob:10: syntax error: symbol 'E' not found at 'TO' +cobol1: error: failed compiling prog.cob +]) AT_CLEANUP AT_SETUP([Intrinsics without FUNCTION keyword (2)]) AT_KEYWORDS([functions]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# GNU -f - AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + FUNCTION PI + FUNCTION E. DATA DIVISION. WORKING-STORAGE SECTION. 01 Z PIC 99V99. @@ -4017,10 +141,8 @@ AT_DATA([prog.cob], [ MOVE E TO Z. STOP RUN. ]) - -AT_CHECK([$COMPILE -fintrinsics=pi,e prog.cob], [0], [], []) +AT_CHECK([$COMPILE prog.cob], [0], [], []) AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) - AT_CLEANUP @@ -4076,56 +198,8 @@ AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) AT_CLEANUP - -AT_SETUP([UDF in COMPUTE]) -AT_KEYWORDS([functions]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - FUNCTION-ID. func. - - DATA DIVISION. - LINKAGE SECTION. - 01 num PIC 999. - - PROCEDURE DIVISION RETURNING num. - MOVE 100 TO num - . - END FUNCTION func. - - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - REPOSITORY. - FUNCTION func. - - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 x PIC 999. - - PROCEDURE DIVISION. - COMPUTE x = 101 + FUNCTION func - DISPLAY x - . - END PROGRAM prog. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[201 -]) - -AT_CLEANUP - - AT_SETUP([UDF replacing intrinsic function]) AT_KEYWORDS([functions SUBSTITUTE]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# GNU -f - AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. SUBSTITUTE. @@ -4156,100 +230,11 @@ AT_DATA([prog.cob], [ . END PROGRAM prog. ]) - AT_CHECK([$COMPILE -fnot-intrinsic=substitute prog.cob], [0], [], []) AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [" _ C_O_B_O_L _ " " - C-O-B-O-L - " ]) - -AT_CLEANUP - - -AT_SETUP([UDF with recursion]) -AT_KEYWORDS([functions LOCAL-STORAGE]) - -AT_SKIP_IF(true) # see bug #222 and r2291 - postponed - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - FUNCTION-ID. foo. - - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 ttl PIC 9 VALUE 1. - - LOCAL-STORAGE SECTION. - 01 num PIC 9. - - LINKAGE SECTION. - 01 arg PIC 9. - 01 ret PIC 9. - - PROCEDURE DIVISION USING arg RETURNING ret. - IF arg < 5 - ADD 1 TO arg GIVING num END-ADD - MOVE FUNCTION foo (num) TO ret - ELSE - MOVE arg TO ret - END-IF - DISPLAY "Step: " ttl ", Arg: " arg ", Return: " ret - END-DISPLAY - ADD 1 to ttl END-ADD - GOBACK. - END FUNCTION foo. - - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - REPOSITORY. - FUNCTION foo. - - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 num PIC 9 VALUE 1. - - PROCEDURE DIVISION. - DISPLAY "Return value '" FUNCTION foo (num) "'" - WITH NO ADVANCING - END-DISPLAY - GOBACK. - END PROGRAM prog. - -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) - -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], -[Step: 1, Arg: 5, Return: 5 -Step: 2, Arg: 4, Return: 5 -Step: 3, Arg: 3, Return: 5 -Step: 4, Arg: 2, Return: 5 -Step: 5, Arg: 1, Return: 5 -Return value '5'], []) - AT_CLEANUP -AT_SETUP([v2-bugs functions repository]) -AT_KEYWORDS([v1-bugs functions repository]) -# REPOSITORY FUNCTION clause -AT_XFAIL_IF(true) -# FUNCTIONS NOT IMPLEMENTED TODO -# NOT V1 ISSUE!! -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - SOURCE-COMPUTER a. - OBJECT-COMPUTER a. - REPOSITORY. - FUNCTION ALL INTRINSIC. - PROCEDURE DIVISION. - DISPLAY "OK". -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CLEANUP diff --git a/gcc/cobol/UAT/failsuite.src/run_subscripts.at b/gcc/cobol/UAT/failsuite.src/run_subscripts.at index e0658c774b7d0f95753eae110f7497f1afd4f3d7..d217feb8dc095e2ca058536d7b5bb51423b741aa 100644 --- a/gcc/cobol/UAT/failsuite.src/run_subscripts.at +++ b/gcc/cobol/UAT/failsuite.src/run_subscripts.at @@ -23,39 +23,3 @@ ## 8.4.1.2.3 General rules -AT_SETUP([383 SSRANGE and NOSSRANGE directives]) -AT_SKIP_IF([test "$COB_DIALECT" != "gnu"]) -AT_KEYWORDS([runsubscripts subscripts extensions directive]) - -# WARNING: this testcase is "broken" as those SSRANGE may only be -# defined before IDENTIFICATION DIVISION (iniatial $SET) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 x. - 03 y PIC X OCCURS 5 TIMES VALUE SPACE. - 03 z PIC X VALUE "!". - 01 idx PIC 99 VALUE 6. - - PROCEDURE DIVISION. - $SET NOSSRANGE - DISPLAY y (idx) - *> Note: MF says "sets BOUND" - $SET SSRANGE - DISPLAY y (idx) - . -]) - -AT_CHECK([$COMPILE -DTEST-SUBSCRIPT prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [1], -[! -], [libcob: prog.cob:17: error: subscript of 'y' out of bounds: 6 -note: maximum subscript for 'y': 5 -]) - -AT_CLEANUP - \ No newline at end of file diff --git a/gcc/cobol/UAT/failsuite.src/syn_copy.at b/gcc/cobol/UAT/failsuite.src/syn_copy.at index 24518b765f6ed9919c7e892a395b1ee2b2ae3dec..abfdd8fa3d590bc6dff65f9ef2ee2fb177b9d6af 100644 --- a/gcc/cobol/UAT/failsuite.src/syn_copy.at +++ b/gcc/cobol/UAT/failsuite.src/syn_copy.at @@ -21,48 +21,3 @@ ### COBOL for GCC Test Suite - testsuite adapted by Marty Heyman ## Copyright (C) 2022-23 COBOLworx, a subsidiary of Symas Corp. -AT_SETUP([392 COPY: IN / OF / -I: 3]) -AT_KEYWORDS([copy cobc]) -AT_SKIP_IF([test "$COB_DIALECT" != "gnu"]) -# TODO: Should default to auto-folding when IN "sub2" (literal) is -# used and therefore don't work on case-sensitive file-systems -# -# jkl: This test is invalid. "The implementor shall define the -# rules for locating the library text referenced by text-name-1 -# or literal-1. When neither library-name-1 nor literal-2 is -# specified, a default COBOL library is used. The implementor -# defines the mechanism for identifying the default COBOL -# library." -# -# This implementation consults the environment. If an environment -# variable is defined for the library or copybook name, the value of -# that variable is used. If not, the name from the Cobol text is -# used. If a literal is supplied instead of a user-defined Cobol -# word, no change is made to the name; it either exists, literally, or -# it does not. If a user-defined Cobol word is supplied, the name is -# tried with various suffixes (and without) using glob(3) to match. -# -# In no event is either name transformed to upper case. Perhaps that -# should be a future option, but I suggest waiting for real-world -# examples and a willing customer. -# -AT_DATA([prog3.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - COPY "copy.inc" IN sub2. - PROCEDURE DIVISION. - DISPLAY TEST-VAR. - STOP RUN. -]) -AT_CHECK([mkdir -p SUB/UNDER], [0], [], []) -AT_DATA([SUB/UNDER/copy.inc], [ - 77 TEST-VAR PIC X VALUE '3'. -]) -AT_DATA([copy.inc], [ - 77 TEST-VAR PIC X VALUE '4'. -]) -AT_CHECK([$COMPILE_ONLY prog3.cob], [0], [], []) -AT_CLEANUP - diff --git a/gcc/cobol/UAT/failsuite.src/syn_definition.at b/gcc/cobol/UAT/failsuite.src/syn_definition.at index 3a8100269485fd038340db3103adaf8c23fbdea0..88282ffb9cc957af1afc3f90018262286eb686d8 100644 --- a/gcc/cobol/UAT/failsuite.src/syn_definition.at +++ b/gcc/cobol/UAT/failsuite.src/syn_definition.at @@ -18,64 +18,21 @@ ### GnuCOBOL Test Suite -### -### Invalid PROGRAM-ID -### - -AT_SETUP([401 Invalid source name]) -AT_KEYWORDS([definition]) -AT_SKIP_IF([test "$COB_DIALECT" != "gnu"]) -# FIXME: message - -# The message is currently inscrutable, but so is the test bogus. -# gcobol doesn't consider "short" to be an invalid program-id because -# it's not generating C code. The message could be better, but it's -# tedious to fix, and there's no program. - -AT_DATA([short.cob], []) - -AT_CHECK([$COMPILE_ONLY short.cob], [1], [], -[short.cob: error: invalid file base name 'short' - name duplicates a 'C' keyword -]) - -AT_CLEANUP - - -AT_SETUP([402 Invalid PROGRAM-ID]) -AT_XFAIL_IF([test "$int128_to_field" != "ok"]) -AT_KEYWORDS([definition]) - -AT_DATA([SHORT.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. short. - PROCEDURE DIVISION. - STOP RUN. -]) - -AT_CHECK([$COMPILE_ONLY SHORT.cob], [0], [], []) -AT_CHECK([./prog], [0], [], []) -AT_CLEANUP - - AT_SETUP([Redefinition of function-prototype name]) AT_KEYWORDS([definition]) -AT_XFAIL_IF([test "UDF" != "implemented"]) - +# This needs a better error message AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. PROGRAM-ID. prog. - ENVIRONMENT DIVISION. CONFIGURATION SECTION. REPOSITORY. - FUNCTION func - . + FUNCTION func . DATA DIVISION. WORKING-STORAGE SECTION. 01 func PIC X. ]) - AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], [prog.cob:8: warning: no definition/prototype seen for FUNCTION 'func' prog.cob:12: error: syntax error, unexpected user function name @@ -83,11 +40,56 @@ prog.cob:12: error: syntax error, unexpected user function name AT_CLEANUP -AT_SETUP([PROCEDURE DIVISION RETURNING item]) -AT_KEYWORDS([definition runmisc]) -AT_SKIP_IF([test "FUNCTION-ID" != "IMPLEMENTED"]) +AT_SETUP([Data item with same name as program-name]) +AT_KEYWORDS([definition]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + FUNCTION-ID. x. + DATA DIVISION. + LINKAGE SECTION. + 01 ret PIC 99. + PROCEDURE DIVISION RETURNING ret. + CONTINUE + . + END FUNCTION x. + + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 x PIC 999 VALUE 134. + PROCEDURE DIVISION. + DISPLAY X. + GOBACK. + END PROGRAM prog. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([./a.out], [0], [134 +], []) +AT_CLEANUP + +AT_SETUP([Screen section starts with 78-level]) +AT_KEYWORDS([screen definition]) +AT_XFAIL_IF([test "SCREEN" != "implemented"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + DATA DIVISION. + SCREEN SECTION. + 78 const VALUE "x". +]) + +AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], +[prog.cob:6: syntax error at 'SCREEN' +]) +AT_CLEANUP + +AT_SETUP([PROCEDURE DIVISION RETURNING item (1)]) +AT_KEYWORDS([definition runmisc]) AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. func. @@ -99,31 +101,52 @@ AT_DATA([prog.cob], [ GOBACK. END FUNCTION func. ]) +AT_CHECK([$COMPILE_ONLY -c prog.cob], [0], [], []) +AT_CLEANUP + +AT_SETUP([PROCEDURE DIVISION RETURNING item (2)]) +AT_KEYWORDS([definition runmisc]) AT_DATA([prog2.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. func. DATA DIVISION. WORKING-STORAGE SECTION. 01 PAR-OUT PIC 9. + *> The following line is an error, because PAR-OUT has to be in the + *> LINKAGE section PROCEDURE DIVISION RETURNING PAR-OUT. MOVE 4 TO PAR-OUT GOBACK. END FUNCTION func. ]) +AT_CHECK([$COMPILE_ONLY -c prog2.cob], [1], [], +[prog2.cob:7: error: RETURNING item is not defined in LINKAGE SECTION +]) +AT_CLEANUP +AT_SETUP([PROCEDURE DIVISION RETURNING item (3)]) +AT_KEYWORDS([definition runmisc]) AT_DATA([prog3.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. func. DATA DIVISION. LINKAGE SECTION. + *> The level 01 can't have an OCCURS clause 01 PAR-OUT PIC 9 OCCURS 10. PROCEDURE DIVISION RETURNING PAR-OUT. MOVE 4 TO PAR-OUT (1) GOBACK. END FUNCTION func. ]) +AT_CHECK([$COMPILE_ONLY -c prog3.cob], [0], [], +[prog3.cob:7: error: RETURNING item should not have OCCURS +prog3.cob:9: error: 'PAR-OUT' requires one subscript +]) +AT_CLEANUP +AT_SETUP([PROCEDURE DIVISION RETURNING item (4)]) +AT_KEYWORDS([definition runmisc]) AT_DATA([prog4.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. func. @@ -136,18 +159,34 @@ AT_DATA([prog4.cob], [ GOBACK. END FUNCTION func. ]) +AT_CHECK([$COMPILE_ONLY -c prog4.cob], [0], [], +[prog4.cob:8: error: RETURNING item must have level 01 +]) +AT_CLEANUP +AT_SETUP([PROCEDURE DIVISION RETURNING item (5)]) +AT_KEYWORDS([definition runmisc]) AT_DATA([prog5.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. func. DATA DIVISION. LINKAGE SECTION. 01 PAR PIC 9. + *> This is an error; USING and RETURNING can't be the same variable PROCEDURE DIVISION USING PAR RETURNING PAR. MOVE 4 TO PAR GOBACK. END FUNCTION func. +]) +AT_CHECK([$COMPILE_ONLY -c prog5.cob], [1], [], +[prog5.cob:7: error: 'PAR' USING item duplicates RETURNING item +prog5.cob:18: error: 'PAR-OUT' REDEFINES field not allowed here +]) +AT_CLEANUP +AT_SETUP([PROCEDURE DIVISION RETURNING item (6)]) +AT_KEYWORDS([definition runmisc]) +AT_DATA([prog6.cob], [ IDENTIFICATION DIVISION. FUNCTION-ID. func2. DATA DIVISION. @@ -159,92 +198,7 @@ AT_DATA([prog5.cob], [ GOBACK. END FUNCTION func2. ]) - -AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], []) -AT_CHECK([$COMPILE_ONLY prog2.cob], [1], [], -[prog2.cob:7: error: RETURNING item is not defined in LINKAGE SECTION +AT_CHECK([$COMPILE_ONLY -c prog6.cob], [1], [], +[RETURNING item can't have REDEFINES or BASED clause ]) -AT_CHECK([$COMPILE_ONLY prog3.cob], [1], [], -[prog3.cob:7: error: RETURNING item should not have OCCURS -prog3.cob:9: error: 'PAR-OUT' requires one subscript -]) -AT_CHECK([$COMPILE_ONLY prog4.cob], [1], [], -[prog4.cob:8: error: RETURNING item must have level 01 -]) -AT_CHECK([$COMPILE_ONLY prog5.cob], [1], [], -[prog5.cob:7: error: 'PAR' USING item duplicates RETURNING item -prog5.cob:18: error: 'PAR-OUT' REDEFINES field not allowed here -]) - AT_CLEANUP - - -AT_SETUP([Data item with same name as program-name]) -AT_KEYWORDS([definition]) -AT_SKIP_IF([test "FUNCTION-ID" != "IMPLEMENTED"]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - FUNCTION-ID. x. - DATA DIVISION. - LINKAGE SECTION. - 01 ret PIC 99. - PROCEDURE DIVISION RETURNING ret. - CONTINUE - . - END FUNCTION x. - - - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 x PIC 999 VALUE 134. -]) - -AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], []) -AT_CLEANUP - -AT_SETUP([Screen section starts with 78-level]) -AT_KEYWORDS([screen definition]) -AT_XFAIL_IF([test "SCREEN" != "implemented"]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - DATA DIVISION. - SCREEN SECTION. - 78 const VALUE "x". -]) - -AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], -[prog.cob:6: syntax error at 'SCREEN' -]) -AT_CLEANUP - -AT_SETUP([ALPHABET definition]) -AT_KEYWORDS([definition]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - SPECIAL-NAMES. - ALPHABET TESTME IS - 'A' THROUGH 'Z', x'00' thru x'05'; - x'41' ALSO x'42', ALSO x'00', x'C1' ALSO x'C2'. - ALPHABET FINE - 'A' also 'B' also 'C' also 'd' also 'e' ALSO 'f', - 'g' also 'G', '1' thru '9', x'00'. -]) - -AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], -[prog.cob:9: error: ALPHABET , character 'A' (x'41') in position 32 already defined at position 0 at 'ALSO' -prog.cob:10: 1 errors in DATA DIVISION, compilation ceases at 'ALPHABET' -cobol1: error: failed compiling prog.cob -]) -AT_CLEANUP - - diff --git a/gcc/cobol/UAT/failsuite.src/syn_misc.at b/gcc/cobol/UAT/failsuite.src/syn_misc.at index 1cb7ccf94ea82a2eefc95449a1294fab25bdf1ed..9c4e1cd40b66be8af979fa6e2c7593ba5146f12c 100644 --- a/gcc/cobol/UAT/failsuite.src/syn_misc.at +++ b/gcc/cobol/UAT/failsuite.src/syn_misc.at @@ -98,3 +98,22 @@ AT_CHECK([$COMPILE prog.cob], [0], [], []) AT_CHECK([./a.out], [0], [], []) AT_CLEANUP +AT_SETUP([X literals]) +AT_KEYWORDS([misc]) +# TODO - Needs better error messages +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + PROCEDURE DIVISION. + *> Valid form + DISPLAY X"0123456789ABCDEF" + + *> invalid form + DISPLAY X"GH" + X"1" + END-DISPLAY. +]) +AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], []) +AT_CLEANUP + + diff --git a/gcc/cobol/UAT/failsuite.src/syn_move.at b/gcc/cobol/UAT/failsuite.src/syn_move.at index cc9878d78774946bc1a93709ced2ced79f78d466..010e2acae0da823001e0b07588ab250b6e690ef8 100644 --- a/gcc/cobol/UAT/failsuite.src/syn_move.at +++ b/gcc/cobol/UAT/failsuite.src/syn_move.at @@ -22,35 +22,3 @@ ## 14.8.24.2 Syntax rules -AT_SETUP([invalid source for MOVE (2)]) -AT_KEYWORDS([move label program-prototype]) -AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) -# cobc is wrong: repo-prog is an error, not warning. It must have -# been previously defined, or exist as a program-prototype (which we -# don't support). gcobol stops compiling instead of continuing on to -# identify the MOVE errors. -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - REPOSITORY. - PROGRAM repo-prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 MAIN-VAR PIC X(3). - PROCEDURE DIVISION. - MAIN. - MOVE MAIN TO MAIN-VAR. - MOVE repo-prog TO MAIN. - STOP RUN. -]) -AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], -[prog.cob:7: warning: no definition/prototype seen for PROGRAM 'repo-prog' -prog.cob: in paragraph 'MAIN': -prog.cob:13: error: 'MAIN' is not a field -prog.cob:14: error: 'repo-prog' is not a field -]) -AT_CLEANUP - - diff --git a/gcc/cobol/UAT/skipsuite.at b/gcc/cobol/UAT/skipsuite.at index 7f90546d28cfb71a3f48e67f02143edd606328f0..8ec5ef129ae4de7e03e22bfc4274fb5c82b11985 100644 --- a/gcc/cobol/UAT/skipsuite.at +++ b/gcc/cobol/UAT/skipsuite.at @@ -38,9 +38,9 @@ m4_include([run_accept.at]) AT_BANNER([FILE]) m4_include([run_file.at]) -# functions DEFERRED -# AT_BANNER([FUNCTIONS]) -# m4_include([functions.at]) +functions DEFERRED +AT_BANNER([FUNCTIONS]) +m4_include([run_functions.at]) AT_BANNER([FUNDAMENTAL]) m4_include([run_fundamental.at]) diff --git a/gcc/cobol/UAT/skipsuite.src/run_functions.at b/gcc/cobol/UAT/skipsuite.src/run_functions.at new file mode 100644 index 0000000000000000000000000000000000000000..3b2cd37ed2666c07926f2080731b86cf4ec8351f --- /dev/null +++ b/gcc/cobol/UAT/skipsuite.src/run_functions.at @@ -0,0 +1,499 @@ +## Copyright (C) 2003-2012, 2014-2020 Free Software Foundation, Inc. +## Written by Keisuke Nishida, Roger While, Simon Sobisch, Edward Hart +## +## This file is part of GnuCOBOL. +## +## The GnuCOBOL compiler is free software: you can redistribute it +## and/or modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation, either version 3 of the +## License, or (at your option) any later version. +## +## GnuCOBOL is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with GnuCOBOL. If not, see <https://www.gnu.org/licenses/>. + +### GnuCOBOL Test Suite + +### ISO+IEC+1989-2002 15 Intrinsic Functions +### ISO+IEC+1989-2002 9.4 User-Defined Functions + +AT_SETUP([FUNCTION CONTENT-LENGTH]) +AT_KEYWORDS([functions length]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 P USAGE POINTER. + 01 X PIC X(4) VALUE Z"ABC". + 01 TEST-FLD USAGE BINARY-LONG. + PROCEDURE DIVISION. + MOVE FUNCTION CONTENT-LENGTH ( P ) + TO TEST-FLD. + IF TEST-FLD NOT = 0 + DISPLAY 'CONTENT-LENGTH NULL wrong: ' TEST-FLD + END-DISPLAY + END-IF + SET P TO ADDRESS OF X + MOVE FUNCTION CONTENT-LENGTH ( P ) + TO TEST-FLD + IF TEST-FLD NOT = 3 + DISPLAY 'CONTENT-LENGTH z"abc" wrong: ' TEST-FLD + END-DISPLAY + END-IF + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION CONTENT-OF]) +AT_KEYWORDS([functions POINTER literal BASED ALLOCATE FREE EXCEPTION-STATUS]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 P USAGE POINTER. + 01 X PIC X(4) VALUE Z"ABC". + 01 B PIC X(10) BASED. + PROCEDURE DIVISION. + SET P TO ADDRESS OF X + IF FUNCTION CONTENT-OF ( P ) NOT EQUAL 'ABC' THEN + DISPLAY 'CONTENT-OF(ptr) wrong' END-DISPLAY + END-IF + IF FUNCTION CONTENT-OF ( P, 2 ) NOT EQUAL 'AB' THEN + DISPLAY 'CONTENT-OF(ptr, len) wrong' END-DISPLAY + END-IF + IF FUNCTION EXCEPTION-STATUS NOT = SPACES THEN + DISPLAY 'unexpected exception (1): ' + FUNCTION EXCEPTION-STATUS + END-DISPLAY + END-IF + SET P TO NULL + MOVE 'PPPP' TO X + STRING FUNCTION CONTENT-OF ( P ) + DELIMITED BY SIZE + INTO X + END-STRING + *> Note: result *should* depend on dialect option zero-length literals + IF X NOT EQUAL 'PPPP' THEN + DISPLAY 'CONTENT-OF empty POINTER wrong: "'" X "'" + END-DISPLAY + END-IF + IF FUNCTION EXCEPTION-STATUS NOT = "EC-DATA-PTR-NULL" THEN + DISPLAY 'missing exception (1)' + END-DISPLAY + END-IF + ALLOCATE B INITIALIZED + SET P TO ADDRESS OF B + IF FUNCTION CONTENT-OF ( P, 1 ) NOT EQUAL SPACES THEN + DISPLAY 'CONTENT-OF allocated BASED item wrong' + END-DISPLAY + END-IF + IF FUNCTION EXCEPTION-STATUS NOT = SPACES THEN + DISPLAY 'unexpected exception (2): ' + FUNCTION EXCEPTION-STATUS + END-DISPLAY + END-IF + FREE B + SET P TO ADDRESS OF B + MOVE 'BBBB' TO X + STRING FUNCTION CONTENT-OF ( P ) + DELIMITED BY SIZE + INTO X + END-STRING + *> Note: result *should* depend on dialect option zero-length literals + IF X NOT EQUAL 'BBBB' THEN + DISPLAY 'CONTENT-OF unallocated BASED item wrong: "' X '"' + END-DISPLAY + END-IF + IF FUNCTION EXCEPTION-STATUS NOT = "EC-DATA-PTR-NULL" THEN + DISPLAY 'missing exception (2)' + END-DISPLAY + END-IF + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION CURRENCY-SYMBOL]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC X(8) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION CURRENCY-SYMBOL TO TEST-FLD. + DISPLAY "OK" NO ADVANCING + END-DISPLAY + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-CALLER-ID]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + CALL "prog2" + END-CALL. + STOP RUN. +]) + +AT_DATA([prog2.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog2. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + DISPLAY FUNCTION MODULE-CALLER-ID NO ADVANCING + END-DISPLAY. + EXIT PROGRAM. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COMPILE_MODULE prog2.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [prog]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-DATE]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-DATE PIC 9(8) VALUE 0. + PROCEDURE DIVISION. + MOVE FUNCTION MODULE-DATE TO TEST-DATE. + IF TEST-DATE NOT = 0 + DISPLAY "OK" NO ADVANCING + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-FORMATTED-DATE]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-DATE PIC X(16) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION MODULE-FORMATTED-DATE TO TEST-DATE. + IF TEST-DATE NOT = SPACES + DISPLAY "OK" NO ADVANCING + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-ID]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + DISPLAY FUNCTION MODULE-ID NO ADVANCING + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [prog]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-PATH]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-PATH PIC X(16) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION MODULE-PATH TO TEST-PATH. + IF TEST-PATH NOT = SPACES + DISPLAY "OK" NO ADVANCING + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-SOURCE]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + DISPLAY FUNCTION MODULE-SOURCE NO ADVANCING + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [prog.cob]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MODULE-TIME]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-TIME PIC 9(6) VALUE 0. + PROCEDURE DIVISION. + MOVE FUNCTION MODULE-TIME TO TEST-TIME. + IF TEST-TIME NOT = 0 + DISPLAY "OK" NO ADVANCING + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MONETARY-DECIMAL-POINT]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC X(8) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION MONETARY-DECIMAL-POINT TO TEST-FLD. + DISPLAY "OK" NO ADVANCING + END-DISPLAY + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MONETARY-THOUSANDS-SEPARATOR]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC X(8) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION MONETARY-THOUSANDS-SEPARATOR TO TEST-FLD. + DISPLAY "OK" NO ADVANCING + END-DISPLAY + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION NUMERIC-DECIMAL-POINT]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC X(8) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION NUMERIC-DECIMAL-POINT TO TEST-FLD. + DISPLAY "OK" NO ADVANCING + END-DISPLAY + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION NUMERIC-THOUSANDS-SEPARATOR]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC X(8) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION NUMERIC-THOUSANDS-SEPARATOR TO TEST-FLD. + DISPLAY "OK" NO ADVANCING + END-DISPLAY + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [OK], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION STORED-CHAR-LENGTH]) +AT_KEYWORDS([functions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(24). + 01 Z USAGE BINARY-LONG. + PROCEDURE DIVISION. + MOVE "123456789012" TO Y. + MOVE FUNCTION STORED-CHAR-LENGTH ( Y ) TO Z. + IF Z NOT = 12 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + + + +AT_SETUP([FORMATTED-(DATE)TIME with SYSTEM-OFFSET]) +AT_KEYWORDS([functions FORMATTED-TIME FORMATTED-DATETIME extensions]) +AT_XFAIL_IF([test "$COB_DIALECT" != "gnu"]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(30). + 77 val pic 9(02). + + PROCEDURE DIVISION. + MOVE FUNCTION FORMATTED-DATETIME + ("YYYYDDDThhmmss+hhmm", 1, 45296, SYSTEM-OFFSET) + TO str + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("YYYYDDDThhmmss+hhmm", str) TO val + IF val not = 0 + DISPLAY "Test 1 failed: " str ' - ' val END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME + ("hhmmss.ssZ", 45296, SYSTEM-OFFSET) + TO str + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss.ssZ", str) TO val + IF val not = 0 + DISPLAY "Test 2 failed: " str ' - ' val END-DISPLAY + END-IF + . +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0]) + +AT_CLEANUP + + diff --git a/gcc/cobol/UAT/skipsuite.src/run_fundamental.at b/gcc/cobol/UAT/skipsuite.src/run_fundamental.at index c3a46a12e195ddfac6c4d9e5a57cd4530a60c1b0..ec7563a660e7da69ccb3998441e5016a8324dc97 100644 --- a/gcc/cobol/UAT/skipsuite.src/run_fundamental.at +++ b/gcc/cobol/UAT/skipsuite.src/run_fundamental.at @@ -441,31 +441,6 @@ AT_CHECK([./a.out], [0], ]) AT_CLEANUP - -AT_SETUP([Context sensitive words (5)]) -AT_KEYWORDS([fundamental recursive]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# TODO NOT IMPLEMENTED "RECURSIVE" - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog RECURSIVE. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 RECURSIVE PIC 9 VALUE 0. - PROCEDURE DIVISION. - DISPLAY RECURSIVE NO ADVANCING - END-DISPLAY. - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([./a.out], [0], [0], []) -AT_CLEANUP - AT_SETUP([SYNC in OCCURS]) AT_KEYWORDS([fundamental CDF SYNCHRONIZE]) AT_SKIP_IF(false) diff --git a/gcc/cobol/UAT/skipsuite.src/run_misc.at b/gcc/cobol/UAT/skipsuite.src/run_misc.at index 4066cdc881803579181f36ca9e07261fbbd027ee..1b7505cfad0bbfa675f71097eb26955778ea70a7 100644 --- a/gcc/cobol/UAT/skipsuite.src/run_misc.at +++ b/gcc/cobol/UAT/skipsuite.src/run_misc.at @@ -235,158 +235,8 @@ AT_CHECK([$COBCRUN_DIRECT ./a.out3], [0], [], []) AT_CLEANUP -AT_SETUP([240. Recursive CALL of RECURSIVE program]) -AT_KEYWORDS([misc CANCEL EXTERNAL]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([caller.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. caller IS RECURSIVE. - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC S9 EXTERNAL. - PROCEDURE DIVISION. - MOVE 0 TO STOPPER - CALL "callee" - DISPLAY 'OK' NO ADVANCING END-DISPLAY - *> FIXME: CANCEL broken on special environments - *> CANCEL "callee" , "callee2" - DISPLAY ' + FINE' NO ADVANCING END-DISPLAY - STOP RUN. -]) -AT_DATA([callee.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee IS RECURSIVE. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC S9 EXTERNAL. - PROCEDURE DIVISION. - IF STOPPER = 9 - MOVE -1 TO STOPPER - ELSE - ADD 1 TO STOPPER - CALL "callee2" - END-IF - GOBACK. -]) -AT_DATA([callee2.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee2 IS RECURSIVE. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC S9 EXTERNAL. - PROCEDURE DIVISION. - IF STOPPER NOT EQUAL -1 - CALL "callee" - END-IF - GOBACK. -]) - -AT_CHECK([$COMPILE -c callee.cob], [0], [], []) -AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) -AT_CHECK([$COMPILE -o caller caller.cob callee.o callee2.o], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./caller], [0], [OK + FINE], []) -AT_CLEANUP - - -AT_SETUP([241. Recursive CALL of INITIAL program]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([caller.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. caller. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC 9 EXTERNAL. - PROCEDURE DIVISION. - MOVE 0 TO STOPPER - CALL "callee" END-CALL. - GOBACK. -]) -AT_DATA([callee.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee IS INITIAL. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC 9 EXTERNAL. - PROCEDURE DIVISION. - IF STOPPER = 1 - DISPLAY 'INITIAL prog was called RECURSIVE' - END-DISPLAY - * Following statement not ISO, corrected below - * STOP RUN RETURNING 1 - MOVE 1 TO RETURN-CODE - STOP RUN - ELSE - MOVE 1 TO STOPPER - CALL "callee2" END-CALL - END-IF. - GOBACK. -]) -AT_DATA([callee2.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee2. - PROCEDURE DIVISION. - CALL "callee" END-CALL. - GOBACK. -]) -AT_CHECK([$COMPILE -c callee.cob], [0], [], []) -AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) -AT_CHECK([$COMPILE -o caller caller.cob callee.o callee2.o], [0], [], []) -AT_CHECK([./caller], [1], [INITIAL prog was called RECURSIVE -], []) -AT_CLEANUP - - -AT_SETUP([Recursive CALL with RECURSIVE assumed]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([caller.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. caller. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC 9 EXTERNAL. - PROCEDURE DIVISION. - MOVE 0 TO STOPPER - CALL "callee" END-CALL. - GOBACK. -]) -AT_DATA([callee.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee IS INITIAL. - DATA DIVISION. - WORKING-STORAGE SECTION. - 77 STOPPER PIC 9 EXTERNAL. - PROCEDURE DIVISION. - IF STOPPER = 8 - DISPLAY 'OK' NO ADVANCING END-DISPLAY. - IF STOPPER NOT = 9 - ADD 1 TO STOPPER END-ADD - CALL "callee2" END-CALL. - GOBACK. -]) -AT_DATA([callee2.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee2. - PROCEDURE DIVISION. - CALL "callee" END-CALL. - GOBACK. -]) -AT_CHECK([$COMPILE -c callee.cob], [0], [], []) -AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) -AT_CHECK([$COMPILE -o caller caller.cob callee.o callee2.o], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./caller], [0], [OK], []) -AT_CLEANUP - - AT_SETUP([Recursive CALL with ON EXCEPTION]) AT_KEYWORDS([misc EXCEPTION-STATUS]) -AT_SKIP_IF(false) AT_XFAIL_IF(true) AT_DATA([caller.cob], [ IDENTIFICATION DIVISION. @@ -440,7 +290,6 @@ AT_CLEANUP AT_SETUP([Multiple calls of INITIAL program]) AT_KEYWORDS([misc CALL]) -AT_SKIP_IF(false) AT_XFAIL_IF(true) AT_DATA([caller.cob], [ IDENTIFICATION DIVISION. @@ -820,181 +669,6 @@ AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) AT_CLEANUP -AT_SETUP([PERFORM inline (1)]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 INDVAL PIC 9(4). - PROCEDURE DIVISION. - PERFORM VARYING INDVAL FROM 1 - BY 1 UNTIL INDVAL > 2 - CONTINUE - END-PERFORM - IF INDVAL NOT = 3 - DISPLAY INDVAL NO ADVANCING - END-DISPLAY - END-IF - STOP RUN - . -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([PERFORM inline (2)]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 INDVAL PIC 9(4). - PROCEDURE DIVISION. - PERFORM VARYING INDVAL FROM 1 - BY 1 UNTIL INDVAL > 2 - CONTINUE - END-PERFORM - IF INDVAL NOT = 3 - DISPLAY INDVAL NO ADVANCING - END-DISPLAY - END-IF - . -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([UNSTRING DELIMITER IN]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - ENVIRONMENT DIVISION. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 WK-CMD PIC X(8) VALUE "WWADDBCC". - 01 WK-SIGNS PIC XX VALUE "AB". - 01 WKS REDEFINES WK-SIGNS. - 03 WK-SIGN PIC X OCCURS 2. - 01 . - 02 WK-DELIM PIC X OCCURS 2. - 01 . - 02 WK-DATA PIC X(2) OCCURS 3. - PROCEDURE DIVISION. - UNSTRING WK-CMD DELIMITED BY WK-SIGN(1) OR WK-SIGN(2) - INTO WK-DATA(1) DELIMITER IN WK-DELIM(1) - WK-DATA(2) DELIMITER IN WK-DELIM(2) - WK-DATA(3) - END-UNSTRING - IF WK-DATA(1) NOT = "WW" - OR WK-DATA(2) NOT = "DD" - OR WK-DATA(3) NOT = "CC" - OR WK-DELIM(1) NOT = "A" - OR WK-DELIM(2) NOT = "B" - DISPLAY """" WK-DATA(1) - WK-DATA(2) - WK-DATA(3) - WK-DELIM(1) - WK-DELIM(2) """" - END-DISPLAY - END-IF. - STOP RUN. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([PERFORM type OSVS]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 MYOCC PIC 9(8) COMP VALUE 0. - PROCEDURE DIVISION. - ASTART SECTION. - A01. - PERFORM BTEST. - IF MYOCC NOT = 2 - DISPLAY MYOCC - END-DISPLAY - END-IF. - STOP RUN. - BTEST SECTION. - B01. - PERFORM B02 VARYING MYOCC FROM 1 BY 1 - UNTIL MYOCC > 5. - GO TO B99. - B02. - IF MYOCC > 1 - GO TO B99 - END-IF. - B99. - EXIT. -]) -AT_CHECK([$COMPILE prog.cob], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) -AT_CLEANUP - - -AT_SETUP([Sticky LINKAGE]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -AT_DATA([callee.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. callee. - DATA DIVISION. - LINKAGE SECTION. - 01 P1 PIC X. - 01 P2 PIC X(6). - 01 P3 PIC X(6). - PROCEDURE DIVISION USING P1 P2. - IF P1 = "A" - SET ADDRESS OF P3 TO ADDRESS OF P2 - ELSE - IF P3 NOT = "OKOKOK" - DISPLAY P3 - END-DISPLAY - END-IF - END-IF. - EXIT PROGRAM. -]) -AT_DATA([caller.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. caller. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 P1 PIC X VALUE "A". - 01 P2 PIC X(6) VALUE "NOT OK". - PROCEDURE DIVISION. - CALL "callee" USING P1 P2 - END-CALL. - MOVE "B" TO P1. - MOVE "OKOKOK" TO P2. - CALL "callee" USING P1 - END-CALL. - STOP RUN. -]) -AT_CHECK([$COMPILE -c callee.cob], [0], [], []) -AT_CHECK([$COMPILE -o caller caller.cob callee.o], [0], [], []) -AT_CHECK([$COBCRUN_DIRECT ./caller], [0], [], []) -AT_CLEANUP AT_SETUP([COB_PRE_LOAD with entry points]) diff --git a/gcc/cobol/UAT/skipsuite.src/run_subscripts.at b/gcc/cobol/UAT/skipsuite.src/run_subscripts.at index 6a7563469323acafd129b492314ee6f4637fcdd6..b9166cde6e4b1609d7c30bd822c02afb94ecc438 100644 --- a/gcc/cobol/UAT/skipsuite.src/run_subscripts.at +++ b/gcc/cobol/UAT/skipsuite.src/run_subscripts.at @@ -194,7 +194,37 @@ note: maximum subscript for 'y': 5 ]) AT_CHECK([$COBCRUN_DIRECT ./a.outn], [0], [!], []) AT_CHECK([$COBCRUN_DIRECT ./a.outn2], [0], [!], []) - AT_CLEANUP +AT_SETUP([SSRANGE/NOSSRANGE directives (IBM, not ISO)]) +AT_KEYWORDS([runsubscripts subscripts extensions directive]) +AT_XFAIL_IF(true) +# WARNING: this testcase is "broken" as those SSRANGE may only be +# defined before IDENTIFICATION DIVISION (iniatial $SET) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 x. + 03 y PIC X OCCURS 5 TIMES VALUE SPACE. + 03 z PIC X VALUE "!". + 01 idx PIC 99 VALUE 6. + + PROCEDURE DIVISION. + $SET NOSSRANGE + DISPLAY y (idx) + *> Note: MF says "sets BOUND" + $SET SSRANGE + DISPLAY y (idx) + . +]) +AT_CHECK([$COMPILE -DTEST-SUBSCRIPT prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [1], +[! +], [libcob: prog.cob:17: error: subscript of 'y' out of bounds: 6 +note: maximum subscript for 'y': 5 +]) +AT_CLEANUP diff --git a/gcc/cobol/UAT/skipsuite.src/syn_copy.at b/gcc/cobol/UAT/skipsuite.src/syn_copy.at index 891607bdf31b3630f6830de2964ef8ce787cc656..ffc89c97356233e0ec4a829d58d7c24dd5250758 100644 --- a/gcc/cobol/UAT/skipsuite.src/syn_copy.at +++ b/gcc/cobol/UAT/skipsuite.src/syn_copy.at @@ -481,3 +481,48 @@ AT_CHECK([./prog], [0], [OK], []) AT_CLEANUP +AT_SETUP([392 COPY: IN / OF / -I: 3]) +AT_KEYWORDS([copy cobc]) +AT_XFAIL_IF(true) +# TODO: Should default to auto-folding when IN "sub2" (literal) is +# used and therefore don't work on case-sensitive file-systems +# +# jkl: This test is invalid. "The implementor shall define the +# rules for locating the library text referenced by text-name-1 +# or literal-1. When neither library-name-1 nor literal-2 is +# specified, a default COBOL library is used. The implementor +# defines the mechanism for identifying the default COBOL +# library." +# +# This implementation consults the environment. If an environment +# variable is defined for the library or copybook name, the value of +# that variable is used. If not, the name from the Cobol text is +# used. If a literal is supplied instead of a user-defined Cobol +# word, no change is made to the name; it either exists, literally, or +# it does not. If a user-defined Cobol word is supplied, the name is +# tried with various suffixes (and without) using glob(3) to match. +# +# In no event is either name transformed to upper case. Perhaps that +# should be a future option, but I suggest waiting for real-world +# examples and a willing customer. +# +AT_DATA([prog3.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + COPY "copy.inc" IN sub2. + PROCEDURE DIVISION. + DISPLAY TEST-VAR. + STOP RUN. +]) +AT_CHECK([mkdir -p SUB/UNDER], [0], [], []) +AT_DATA([SUB/UNDER/copy.inc], [ + 77 TEST-VAR PIC X VALUE '3'. +]) +AT_DATA([copy.inc], [ + 77 TEST-VAR PIC X VALUE '4'. +]) +AT_CHECK([$COMPILE_ONLY prog3.cob], [0], [], []) +AT_CLEANUP + diff --git a/gcc/cobol/UAT/skipsuite.src/syn_definition.at b/gcc/cobol/UAT/skipsuite.src/syn_definition.at index 742409a929f32857892441cefc19b15bb07f9c83..1037a93d2935b0c3d383ef1a37ff91785df5c62e 100644 --- a/gcc/cobol/UAT/skipsuite.src/syn_definition.at +++ b/gcc/cobol/UAT/skipsuite.src/syn_definition.at @@ -38,34 +38,7 @@ AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], AT_CLEANUP -AT_SETUP([405 INITIAL / RECURSIVE before COMMON]) -AT_KEYWORDS([PROGRAM-ID definition]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# RECURSIVE not implemented. - -AT_DATA([containing-prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. containing-prog. - - PROCEDURE DIVISION. - - IDENTIFICATION DIVISION. - PROGRAM-ID. prog-1 IS INITIAL COMMON. - PROCEDURE DIVISION. - STOP RUN. - END PROGRAM prog-1. - IDENTIFICATION DIVISION. - PROGRAM-ID. prog-2 IS RECURSIVE COMMON. - PROCEDURE DIVISION. - STOP RUN. - END PROGRAM prog-2. -]) - -AT_CHECK([$COMPILE_ONLY containing-prog.cob], [0], [], []) - -AT_CLEANUP AT_SETUP([406 Undefined data name]) diff --git a/gcc/cobol/UAT/skipsuite.src/syn_misc.at b/gcc/cobol/UAT/skipsuite.src/syn_misc.at index 44eb8d06c3453583193f02f0592d7d7ff466a45e..7613b1b7b2fea7f092a7496cfb18221a4883c728 100644 --- a/gcc/cobol/UAT/skipsuite.src/syn_misc.at +++ b/gcc/cobol/UAT/skipsuite.src/syn_misc.at @@ -2533,36 +2533,6 @@ prog3.cob:26: error: syntax error, unexpected + AT_CLEANUP -AT_SETUP([X literals]) -AT_KEYWORDS([misc]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# TODO - NOT IMPLEMENTED - diagnostic messages - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - PROCEDURE DIVISION. - *> Valid form - DISPLAY X"0123456789ABCDEF" - - *> invalid form - DISPLAY X"GH" - X"1" - END-DISPLAY. -]) - -AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], -[prog.cob:9: error: invalid X literal: 'GH' -prog.cob:9: error: literal contains invalid character 'G' -prog.cob:9: error: literal contains invalid character 'H' -prog.cob:10: error: invalid X literal: '1' -prog.cob:10: error: literal does not have an even number of digits -]) - -AT_CLEANUP - - AT_SETUP([national literals]) AT_KEYWORDS([misc]) AT_SKIP_IF(false) @@ -2768,30 +2738,6 @@ AT_CHECK([$COMPILE_ONLY -fnot-reserved=DISPLAY -freserved=COMP-1=DISPLAY prog2.c AT_CLEANUP -AT_SETUP([swapped SOURCE- and OBJECT-COMPUTER]) -AT_KEYWORDS([misc extensions]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# TODO - NOT IMPLEMENTED - Diagnostic messages -# This is a test for statement order. - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - - ENVIRONMENT DIVISION. - CONFIGURATION SECTION. - OBJECT-COMPUTER. a. - SOURCE-COMPUTER. b. -]) - -# MF extension, supported by GnuCOBOL -AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], []) -# note: testing with lax configuration, otherwise there would be an error - -AT_CLEANUP - - AT_SETUP([CONF. SECTION paragraphs in wrong order]) AT_KEYWORDS([misc extensions]) AT_SKIP_IF(false) diff --git a/gcc/cobol/UAT/skipsuite.src/syn_occurs.at b/gcc/cobol/UAT/skipsuite.src/syn_occurs.at index 8c4b0d14290e56ab0ab4f45433a3b8229c2438f4..32efee69fadf000ee375106486f44dd000c4cc2b 100644 --- a/gcc/cobol/UAT/skipsuite.src/syn_occurs.at +++ b/gcc/cobol/UAT/skipsuite.src/syn_occurs.at @@ -243,32 +243,6 @@ AT_CLEANUP # 9) DONE -AT_SETUP([Nested OCCURS clause]) -AT_KEYWORDS([occurs]) -AT_SKIP_IF(false) -AT_XFAIL_IF(true) -# NOT IMPLEMENTED: Diagnostic messages - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 G-1. - 02 G-2 OCCURS 2. - 03 G-3 OCCURS 2. - 04 G-4 OCCURS 2. - 05 G-5 OCCURS 2. - 06 G-6 OCCURS 2. - 07 G-7 OCCURS 2. - 08 G-8 OCCURS 2. - 09 X PIC X. -]) - -AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], []) - -AT_CLEANUP - # 10) TODO diff --git a/gcc/cobol/UAT/testsuite.at b/gcc/cobol/UAT/testsuite.at index bac6ddf18e2e6038859f2f3d4fad215297bc8fcb..2b27be5d29c42408d0af83a569c4651056c1a693 100644 --- a/gcc/cobol/UAT/testsuite.at +++ b/gcc/cobol/UAT/testsuite.at @@ -48,9 +48,8 @@ m4_include([run_evaluate.at]) AT_BANNER([FILE]) m4_include([run_file.at]) -# functions DEFERRED -# AT_BANNER([FUNCTIONS]) -# m4_include([functions.at]) +AT_BANNER([FUNCTIONS]) +m4_include([run_functions.at]) AT_BANNER([FUNDAMENTAL]) m4_include([run_fundamental.at]) diff --git a/gcc/cobol/UAT/testsuite.src/run_evaluate.at b/gcc/cobol/UAT/testsuite.src/run_evaluate.at index 63a5f7ba280edd30004e9581f1e998f0a8cd6fa5..25aa6e2f219d14adfd25d14d07f3f2fe762eeb3a 100644 --- a/gcc/cobol/UAT/testsuite.src/run_evaluate.at +++ b/gcc/cobol/UAT/testsuite.src/run_evaluate.at @@ -31,6 +31,52 @@ AT_CHECK([./a.out], [0], [not ], []) AT_CLEANUP +AT_SETUP([EVALUATE condition (2)]) +AT_KEYWORDS([evaluate condition]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 XVAL PIC X VALUE '_'. + 88 UNDERSCORE VALUE '_'. + PROCEDURE DIVISION. + DISPLAY 'Next line should be "UNDERSCORE evaluates to TRUE"' + EVALUATE TRUE + WHEN NOT UNDERSCORE + DISPLAY + "***IMPROPERLY*** NOT UNDERSCORE evaluates to TRUE" + END-DISPLAY + END-EVALUATE. + EVALUATE TRUE + WHEN UNDERSCORE + DISPLAY "UNDERSCORE evaluates to TRUE" + END-DISPLAY + END-EVALUATE. + + DISPLAY + 'Next line should be "NOT UNDERSCORE evaluates to FALSE"' + EVALUATE FALSE + WHEN NOT UNDERSCORE + DISPLAY "NOT UNDERSCORE evaluates to FALSE" + END-DISPLAY + END-EVALUATE. + EVALUATE FALSE + WHEN UNDERSCORE + DISPLAY + "***IMPROPERLY*** UNDERSCORE evaluates to FALSE" + END-DISPLAY + END-EVALUATE. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([./a.out], [0], [Next line should be "UNDERSCORE evaluates to TRUE" +UNDERSCORE evaluates to TRUE +Next line should be "NOT UNDERSCORE evaluates to FALSE" +NOT UNDERSCORE evaluates to FALSE +], []) +AT_CLEANUP + AT_SETUP([EVALUATE with WHEN using condition-1]) # Gnu DOES NOT support condition names as Evaluate object diff --git a/gcc/cobol/UAT/testsuite.src/run_functions.at b/gcc/cobol/UAT/testsuite.src/run_functions.at new file mode 100644 index 0000000000000000000000000000000000000000..5eeb33f51782d3b697df33116ff545ed663f560e --- /dev/null +++ b/gcc/cobol/UAT/testsuite.src/run_functions.at @@ -0,0 +1,4008 @@ +## Copyright (C) 2003-2012, 2014-2020 Free Software Foundation, Inc. +## Written by Keisuke Nishida, Roger While, Simon Sobisch, Edward Hart +## +## This file is part of GnuCOBOL. +## +## The GnuCOBOL compiler is free software: you can redistribute it +## and/or modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation, either version 3 of the +## License, or (at your option) any later version. +## +## GnuCOBOL is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with GnuCOBOL. If not, see <https://www.gnu.org/licenses/>. + +### GnuCOBOL Test Suite + +### ISO+IEC+1989-2002 15 Intrinsic Functions +### ISO+IEC+1989-2002 9.4 User-Defined Functions + +AT_SETUP([FUNCTION ABS]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC S9(4)V9(4) VALUE -1.2345. + PROCEDURE DIVISION. + COMPUTE X = FUNCTION ABS( X ) + DISPLAY X + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[+0001.2345 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION ACOS]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION ACOS ( -0.2345 ) TO Z. + IF Z NOT = 1.807500521108243435101500438523210 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION ANNUITY]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION ANNUITY ( 3, 5 ) TO Z. + IF Z NOT = 3.002932551319648093841642228739002 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION ASIN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION ASIN ( -0.2345 ) TO Y. + IF Y NOT = -0.236704194313346815870178746883458 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION ATAN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION ATAN ( 1 ) TO Y. + IF Y NOT = 0.785398163397448309615660845819875 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION BYTE-LENGTH]) +AT_KEYWORDS([functions length]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(4). + 01 TEST-FLD PIC S9(04)V9(08). + PROCEDURE DIVISION. + MOVE FUNCTION BYTE-LENGTH ( TEST-FLD ) TO TEST-FLD. + DISPLAY "BYTE-LENGTH of PIC S9(04)V9(08) is " TEST-FLD + MOVE FUNCTION BYTE-LENGTH ( X ) TO TEST-FLD. + DISPLAY "BYTE-LENGTH of PIC X(4) is " TEST-FLD + MOVE FUNCTION BYTE-LENGTH ( '00128' ) TO TEST-FLD + DISPLAY "BYTE-LENGTH of PIC '00128' is " TEST-FLD + MOVE FUNCTION BYTE-LENGTH ( x'a0' ) TO TEST-FLD + DISPLAY "BYTE-LENGTH of PIC x'a0' is " TEST-FLD + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [BYTE-LENGTH of PIC S9(04)V9(08) is +0012.00000000 +BYTE-LENGTH of PIC X(4) is +0004.00000000 +BYTE-LENGTH of PIC '00128' is +0005.00000000 +BYTE-LENGTH of PIC x'a0' is +0001.00000000 +], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION CHAR]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC S9(4)V9(4) VALUE 108. + 01 TEST-FLD. + 05 TEST-DATA PIC X(01). + 88 VALID-DATA VALUE 'k'. + 05 TEST-UNSET PIC X VALUE '_'. + 88 VALID-UNSET VALUE '_'. + PROCEDURE DIVISION. + STRING FUNCTION CHAR ( X ) + DELIMITED BY SIZE + INTO TEST-FLD + END-STRING. + EVALUATE TRUE + WHEN NOT VALID-UNSET + DISPLAY "FUNCTION result too long" + END-DISPLAY + WHEN VALID-DATA + CONTINUE + WHEN OTHER + DISPLAY TEST-DATA + END-DISPLAY + END-EVALUATE. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION COMBINED-DATETIME]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(04)V9(08). + PROCEDURE DIVISION. + MOVE FUNCTION COMBINED-DATETIME ( 987, 345.6 ) + TO TEST-FLD. + IF TEST-FLD NOT = 987.003456 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION CONCAT / CONCATENATE]) +AT_KEYWORDS([functions]) + +# note: CONCAT was added in COBOL 202x with GnuCOBOL's CONCATENATE +# as blueprint +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(4). + 01 TEST-FLD. + 05 TEST-DATA PIC X(14). + 88 VALID-DATA VALUE 'defxabczz55666'. + 05 TEST-UNSET PIC X VALUE '_'. + 88 VALID-UNSET VALUE '_'. + PROCEDURE DIVISION. + MOVE "defx" TO Y. + STRING FUNCTION CONCAT ( Y "abc" "zz" "55" "666" ) + DELIMITED BY SIZE + INTO TEST-FLD + END-STRING. + EVALUATE TRUE + WHEN NOT VALID-UNSET + DISPLAY "FUNCTION result too long" + END-DISPLAY + WHEN TEST-DATA + <> FUNCTION CONCAT ( Y "abc" "zz" "55" "666" ) + DISPLAY "CONCAT issue, '" TEST-DATA + "' vs. '" + FUNCTION CONCAT ( Y "abc" "zz" "55" "666" ) "'" + END-DISPLAY + WHEN VALID-DATA + CONTINUE + WHEN OTHER + DISPLAY TEST-DATA + END-DISPLAY + END-EVALUATE. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION CONCAT with reference modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(4). + 01 TEST-FLD PIC X(9) VALUE SPACES. + PROCEDURE DIVISION. + MOVE 'defx' TO Y. + MOVE FUNCTION CONCAT + ( Y "abc" "zz" "55" "666" ) (2 : 9) + TO TEST-FLD. + IF TEST-FLD NOT = 'efxabczz5' + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + + +# Invalid test. +# prog.cob:18: ANY LENGTH valid only for 01 in LIKAGE SECTION of a contained program +# "subprog", so called, is not contained. + +AT_SETUP([FUNCTION as CALL parameter BY CONTENT]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + PROCEDURE DIVISION. + PROG-MAIN. + CALL "subprog" USING BY CONTENT + FUNCTION CONCAT("Abc" "D") + STOP RUN. + + *> ***************************** + IDENTIFICATION DIVISION. + PROGRAM-ID. subprog. + + DATA DIVISION. + LINKAGE SECTION. + 01 TESTING PIC X ANY LENGTH. + + PROCEDURE DIVISION USING TESTING. + SUBPROG-MAIN. + DISPLAY TESTING + GOBACK. + END PROGRAM subprog. + END PROGRAM prog. *> bzzt +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [AbcD +], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION COS]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION COS ( -0.2345 ) TO Y. + IF Y NOT = 0.972630641256258184713416962414561 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION CURRENT-DATE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD. + 02 WS-YEAR PIC 9(04). + 88 VALID-YEAR VALUE 1980 THRU 9999. + 02 WS-MONTH PIC 9(02). + 88 VALID-MONTH VALUE 01 THRU 12. + 02 WS-DAY PIC 9(02). + 88 VALID-DAY VALUE 01 THRU 31. + 02 WS-HOUR PIC 9(02). + 88 VALID-HOUR VALUE 00 THRU 23. + 02 WS-MIN PIC 9(02). + 88 VALID-MIN VALUE 00 THRU 59. + 02 WS-SEVALIDD PIC 9(02). + 88 VALID-SEC VALUE 00 THRU 59. + 02 WS-HUNDSEC PIC 9(02). + 88 VALID-HUNDSEC VALUE 00 THRU 99. + 02 WS-GREENW PIC X. + 88 VALID-GREENW VALUE "-", "+", "0". + 88 ZERO-GREENW VALUE "0". + 02 WS-OFFSET PIC 9(02). + 88 VALID-OFFSET VALUE 00 THRU 13. + 88 ZERO-OFFSET VALUE 00. + 02 WS-OFFSET2 PIC 9(02). + 88 VALID-OFFSET2 VALUE 00 THRU 59. + 88 ZERO-OFFSET2 VALUE 00. + 02 WS-UNSET PIC X VALUE '_'. + 88 VALID-UNSET VALUE '_'. + PROCEDURE DIVISION. + STRING FUNCTION CURRENT-DATE + DELIMITED BY SIZE + INTO TEST-FLD + END-STRING. + EVALUATE TRUE + WHEN NOT VALID-UNSET + DISPLAY "FUNCTION result too long" + END-DISPLAY + WHEN VALID-YEAR AND + VALID-MONTH AND + VALID-DAY AND + VALID-HOUR AND + VALID-MIN AND + VALID-SEC AND + VALID-HUNDSEC AND + VALID-GREENW AND + VALID-OFFSET AND + VALID-OFFSET2 AND + VALID-UNSET AND + ((NOT ZERO-GREENW) OR (ZERO-OFFSET AND ZERO-OFFSET2)) + CONTINUE + WHEN OTHER + DISPLAY "CURRENT-DATE with wrong format: " + TEST-FLD (01:21) + END-DISPLAY + END-EVALUATE. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION DATE-OF-INTEGER]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION DATE-OF-INTEGER ( 146000 ) + TO TEST-FLD. + IF TEST-FLD NOT = 20000925 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION DATE-TO-YYYYMMDD]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION DATE-TO-YYYYMMDD ( 981002, -10, 1994 ) + TO TEST-FLD. + IF TEST-FLD NOT = 018981002 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION DAY-OF-INTEGER]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION DAY-OF-INTEGER ( 146000 ) + TO TEST-FLD. + IF TEST-FLD NOT = 2000269 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION DAY-TO-YYYYDDD]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION DAY-TO-YYYYDDD ( 95005, -10, 2013 ) + TO TEST-FLD. + IF TEST-FLD NOT = 001995005 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION E]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC 9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION E TO Y. + IF Y NOT = 2.718281828459045235360287471352662 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION EXCEPTION-FILE]) +AT_KEYWORDS([functions exceptions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT TEST-FILE ASSIGN "NOTEXIST" + FILE STATUS IS TEST-STATUS. + DATA DIVISION. + FILE SECTION. + FD TEST-FILE. + 01 TEST-REC PIC X(4). + WORKING-STORAGE SECTION. + 01 TEST-STATUS PIC XX. + PROCEDURE DIVISION. + DISPLAY FUNCTION EXCEPTION-FILE '|' + NO ADVANCING + END-DISPLAY. + OPEN INPUT TEST-FILE. + DISPLAY FUNCTION EXCEPTION-FILE + NO ADVANCING + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[00|35TEST-FILE], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION EXCEPTION-LOCATION]) +AT_KEYWORDS([functions exceptions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT TEST-FILE ASSIGN "NOTEXIST" + FILE STATUS IS TEST-STATUS. + DATA DIVISION. + FILE SECTION. + FD TEST-FILE. + 01 TEST-REC PIC X(4). + WORKING-STORAGE SECTION. + 01 TEST-STATUS PIC XX. + PROCEDURE DIVISION. + DECLARATIVES. + EC-ALL-SECTION SECTION. + USE EC EC-ALL + RESUME NEXT STATEMENT. + END DECLARATIVES. + A00-MAIN SECTION. + A00. + SET LAST EXCEPTION TO OFF + DISPLAY "EXCEPTION-LOCATION before open attempt: " + """" FUNCTION EXCEPTION-LOCATION """". + B00-MAIN SECTION. + B00. + SET LAST EXCEPTION TO OFF + OPEN INPUT TEST-FILE. + DISPLAY "EXCEPTION-LOCATION after open attempt: " + """" FUNCTION EXCEPTION-LOCATION """". + C00-MAIN SECTION. + C00. + >>TURN EC-ALL CHECKING ON + SET LAST EXCEPTION TO OFF + OPEN INPUT TEST-FILE. + DISPLAY "EXCEPTION-LOCATION after CHECKED open attempt: " + """" FUNCTION EXCEPTION-LOCATION """". + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[EXCEPTION-LOCATION before open attempt: " " +EXCEPTION-LOCATION after open attempt: "prog; B00 OF B00-MAIN; prog.cob:29 " +EXCEPTION-LOCATION after CHECKED open attempt: "prog; C00 OF C00-MAIN; prog.cob:36 " +], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION EXCEPTION-STATEMENT]) +AT_KEYWORDS([functions exceptions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT TEST-FILE ASSIGN "NOTEXIST" + FILE STATUS IS TEST-STATUS. + DATA DIVISION. + FILE SECTION. + FD TEST-FILE. + 01 TEST-REC PIC X(4). + WORKING-STORAGE SECTION. + 01 TEST-STATUS PIC XX. + PROCEDURE DIVISION. + DISPLAY "EXCEPTION-STATEMENT before bad OPEN: " + """" FUNCTION EXCEPTION-STATEMENT """" + OPEN INPUT TEST-FILE. + DISPLAY "EXCEPTION-STATEMENT after bad OPEN: " + """" FUNCTION EXCEPTION-STATEMENT """" + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[EXCEPTION-STATEMENT before bad OPEN: " " +EXCEPTION-STATEMENT after bad OPEN: "OPEN" +], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION EXCEPTION-STATUS]) +AT_KEYWORDS([functions exceptions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT TEST-FILE ASSIGN "NOTEXIST" + FILE STATUS IS TEST-STATUS. + DATA DIVISION. + FILE SECTION. + FD TEST-FILE. + 01 TEST-REC PIC X(4). + WORKING-STORAGE SECTION. + 01 TEST-STATUS PIC XX. + PROCEDURE DIVISION. + DISPLAY "EXCEPTION STATUS before bad open: " + """" FUNCTION EXCEPTION-STATUS """" + OPEN INPUT TEST-FILE. + DISPLAY "EXCEPTION STATUS after bad open: " + """" FUNCTION EXCEPTION-STATUS """" + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[EXCEPTION STATUS before bad open: " " +EXCEPTION STATUS after bad open: "EC-I-O-PERMANENT-ERROR" +], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION EXP]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S99V9(31). + PROCEDURE DIVISION. + MOVE FUNCTION EXP ( 3 ) TO Y. + IF Y NOT = 20.0855369231876677409285296545817 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION EXP10]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION EXP10 ( 4 ) + TO TEST-FLD. + IF TEST-FLD NOT = 000010000 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FACTORIAL]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION FACTORIAL ( 6 ) + TO TEST-FLD. + IF TEST-FLD NOT = 000000720 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-CURRENT-DATE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Datetime-Format CONSTANT "YYYYMMDDThhmmss.ss+hhmm". + 01 str PIC X(25). + PROCEDURE DIVISION. + *> Test normal inputs. + MOVE FUNCTION FORMATTED-CURRENT-DATE ( Datetime-Format ) + TO str + IF FUNCTION TEST-FORMATTED-DATETIME ( Datetime-Format, str) + <> 0 + DISPLAY "Test 1 failed: " str END-DISPLAY + END-IF. + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-DATE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(10). + PROCEDURE DIVISION. + *> Test normal inputs. + MOVE FUNCTION FORMATTED-DATE ( "YYYYMMDD", 1 ) TO str + IF str <> "16010101" + DISPLAY "Test 1 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATE ( "YYYY-MM-DD", 1 ) TO str + IF str <> "1601-01-01" + DISPLAY "Test 2 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATE ( "YYYYDDD", 1 ) TO str + IF str <> "1601001" + DISPLAY "Test 3 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATE ( "YYYY-DDD", 1 ) TO str + IF str <> "1601-001" + DISPLAY "Test 4 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATE ( "YYYYWwwD", 1 ) TO str + IF str <> "1601W011" + DISPLAY "Test 5 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATE ( "YYYY-Www-D", 1 ) TO str + IF str <> "1601-W01-1" + DISPLAY "Test 6 failed: " str END-DISPLAY + END-IF + + *> Test week number edge cases. + *> For 2012-01-01. + MOVE FUNCTION FORMATTED-DATE ( "YYYYWwwD", 150115 ) TO str + IF str <> "2011W527" + DISPLAY "Test 7 failed: " str END-DISPLAY + END-IF + + *> and for 2013-12-30. + MOVE FUNCTION FORMATTED-DATE ( "YYYYWwwD", 150844 ) TO str + IF str <> "2014W011" + DISPLAY "Test 8 failed: " str END-DISPLAY + END-IF + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-DATE with ref modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(04). + PROCEDURE DIVISION. + MOVE FUNCTION FORMATTED-DATE ("YYYYMMDD", 1) (3:4) + TO STR + IF STR NOT = '0101' + DISPLAY STR + END-DISPLAY + END-IF + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-DATETIME]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(40). + PROCEDURE DIVISION. + *> Test normal inputs. + MOVE FUNCTION FORMATTED-DATETIME + ("YYYYMMDDThhmmss", 1, 45296) + TO str + IF str <> "16010101T123456" + DISPLAY "Test 1 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATETIME + ("YYYY-MM-DDThh:mm:ss", 1, 45296) + TO str + IF str <> "1601-01-01T12:34:56" + DISPLAY "Test 2 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATETIME + ("YYYYDDDThhmmss+hhmm", 1, 45296, -754) + TO str + IF str <> "1601001T123456-1234" + DISPLAY "Test 3 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-DATETIME + ("YYYYDDDThhmmss+hhmm", 1, 45296) + TO str + IF str <> "1601001T123456+0000" + DISPLAY "Test 4 failed: " str END-DISPLAY + END-IF + + *> Test underflow to next day due to offset + MOVE FUNCTION FORMATTED-DATETIME + ("YYYYDDDThhmmss.sssssssssZ", 150846, 0, + 1) + TO str + IF str <> "2013365T235900.000000000Z" + DISPLAY "Test 5 failed: " str END-DISPLAY + END-IF + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-DATETIME with ref modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(04). + PROCEDURE DIVISION. + MOVE FUNCTION FORMATTED-DATETIME + ("YYYYMMDDThhmmss", 1, 1) (3:4) + TO STR + IF STR NOT = '0101' + DISPLAY STR + END-DISPLAY + END-IF + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-TIME]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(20). + PROCEDURE DIVISION. + *> Test normal inputs. + MOVE FUNCTION FORMATTED-TIME ( "hhmmss", 45296 ) TO str + IF str <> "123456" + DISPLAY "Test 1 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ss", 45296 ) TO str + IF str <> "12:34:56" + DISPLAY "Test 2 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hhmmssZ", 86399, -1 ) TO str + IF str <> "000059Z" + DISPLAY "Test 3 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ssZ", 45296) + TO str + IF str <> "12:34:56Z" + DISPLAY "Test 4 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hhmmss.ss", 45296.78 ) TO str + IF str <> "123456.78" + DISPLAY "Test 5 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ss.ssZ", 0, 120) + TO str + IF str <> "22:00:00.00Z" + DISPLAY "Test 6 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 45296) + TO str + IF str <> "123456+0000" + DISPLAY "Test 7 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hh:mm:ss+hh:mm", 45296, 0 ) + TO str + IF str <> "12:34:56+00:00" + DISPLAY "Test 8 failed: " str END-DISPLAY + END-IF + + MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 45296, -754) + TO str + IF str <> "123456-1234" + DISPLAY "Test 9 failed: " str END-DISPLAY + END-IF + + *> Test with invalid/missing offset times. + MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 1, 3000 ) + TO str + DISPLAY "Test 10 " """" str """" + DISPLAY "Test 10 " """" FUNCTION EXCEPTION-STATUS """" + DISPLAY "Test 10 " """" FUNCTION EXCEPTION-LOCATION """" + + MOVE FUNCTION FORMATTED-TIME ( "hhmmss+hhmm", 1, -3000 ) + TO str + DISPLAY "Test 11 " """" str """" + DISPLAY "Test 11 " """" FUNCTION EXCEPTION-STATUS """" + DISPLAY "Test 11 " """" FUNCTION EXCEPTION-LOCATION """" + + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [Test 10 " " +Test 10 "EC-ARGUMENT-FUNCTION" +Test 10 "prog; ; prog.cob:60 " +Test 11 " " +Test 11 "EC-ARGUMENT-FUNCTION" +Test 11 "prog; ; prog.cob:66 " +], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-TIME DP.COMMA]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + SPECIAL-NAMES. + DECIMAL-POINT IS COMMA. + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(11). + + PROCEDURE DIVISION. + MOVE FUNCTION FORMATTED-TIME ("hh:mm:ss,ss", 45296) TO str + IF str <> "12:34:56,00" + DISPLAY "Test 1 failed: " str END-DISPLAY + END-IF + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FORMATTED-TIME with ref modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 str PIC X(04). + PROCEDURE DIVISION. + MOVE FUNCTION FORMATTED-TIME ("hhmmss", 45296) (3:4) + TO STR + IF STR NOT = '3456' + DISPLAY STR + END-DISPLAY + END-IF + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION FRACTION-PART]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(04)V9(04). + PROCEDURE DIVISION. + MOVE FUNCTION FRACTION-PART ( 3.12345 ) + TO TEST-FLD. + IF TEST-FLD NOT = +0000.1234 + DISPLAY 'FRACTION-PART ( +3.12345 ) wrong: ' TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION FRACTION-PART ( -3.12345 ) + TO TEST-FLD. + IF TEST-FLD NOT = -0000.1234 + DISPLAY 'FRACTION-PART ( -3.12345 ) wrong: ' TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + +AT_SETUP([FUNCTION HEX-OF]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 PAC PIC 9(5) COMP-3 VALUE 12345. + PROCEDURE DIVISION. + DISPLAY FUNCTION HEX-OF('Hello, world!') + DISPLAY FUNCTION HEX-OF(PAC) + END PROGRAM prog. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [48656C6C6F2C20776F726C6421 +12345F +], []) +AT_CLEANUP + +AT_SETUP([FUNCTION HIGHEST-ALGEBRAIC]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 F1 PIC S999. + 01 F2 PIC S9(4) BINARY. + 01 F3 PIC 99V9(3). + 01 F4 PIC $**,**9.99BCR. + 01 F5 PIC $**,**9.99. + 01 F6 USAGE BINARY-CHAR SIGNED. + 01 F7 USAGE BINARY-CHAR UNSIGNED. + 01 F8 PIC 999PPP. + 01 F9 PIC P99. + 01 TEST-FLD PIC S9(08)V9(04). + PROCEDURE DIVISION. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F1) + TO TEST-FLD. + IF TEST-FLD NOT = 999 + DISPLAY "Test 1 fail: " TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F2) + TO TEST-FLD. + IF TEST-FLD NOT = 9999 + DISPLAY "Test 2 fail: " TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F3) + TO TEST-FLD. + IF TEST-FLD NOT = 99.999 + DISPLAY "Test 3 fail: " TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F4) + TO TEST-FLD. + IF TEST-FLD NOT = 99999.99 + DISPLAY "Test 4 fail: " TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F5) + TO TEST-FLD. + IF TEST-FLD NOT = 99999.99 + DISPLAY "Test 5 fail: " TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F6) + TO TEST-FLD. + IF TEST-FLD NOT = 127 + DISPLAY "Test 6 fail: " TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION HIGHEST-ALGEBRAIC (F7) + TO TEST-FLD. + IF TEST-FLD NOT = 255 + DISPLAY "Test 7 fail: " TEST-FLD + END-DISPLAY + END-IF. + + MOVE FUNCTION HIGHEST-ALGEBRAIC (F8) + TO TEST-FLD. + IF TEST-FLD NOT = 999000 + DISPLAY "Test 7 fail: " TEST-FLD + END-DISPLAY + END-IF. + + MOVE FUNCTION HIGHEST-ALGEBRAIC (F9) + TO TEST-FLD. + IF TEST-FLD NOT = 0.099 + DISPLAY "Test 7 fail: " TEST-FLD + END-DISPLAY + END-IF. + + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + +AT_SETUP([FUNCTION INTEGER]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC S9(4)V9(4) VALUE -1.5. + 01 Y PIC 9(12) VALUE 600851475143. + 01 TEST-FLD PIC S9(14)V9(08). + PROCEDURE DIVISION. + MOVE FUNCTION INTEGER ( X ) + TO TEST-FLD. + IF TEST-FLD NOT = -2 + DISPLAY 'INTEGER ( X ) wrong: ' TEST-FLD + END-DISPLAY + END-IF. + MOVE FUNCTION INTEGER ( Y / 71 ) + TO TEST-FLD. + IF TEST-FLD NOT = 8462696833 + DISPLAY 'INTEGER ( Y / 71 ) wrong: ' TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION INTEGER-OF-DATE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION INTEGER-OF-DATE ( 20000925 ) + TO TEST-FLD. + IF TEST-FLD NOT = 000146000 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION INTEGER-OF-DAY]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-FLD PIC S9(09)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION INTEGER-OF-DAY ( 2000269 ) + TO TEST-FLD. + IF TEST-FLD NOT = 000146000 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION INTEGER-OF-FORMATTED-DATE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 day-int PIC 9(9). + + PROCEDURE DIVISION. + *> The date 2013-12-30 is used as it can also be used to + *> check the conversion of dates in week form. + MOVE FUNCTION INTEGER-OF-FORMATTED-DATE + ("YYYY-MM-DD", "2013-12-30") + TO day-int + IF day-int <> 150844 + DISPLAY "Test 1 failed: " day-int END-DISPLAY + END-IF + + MOVE FUNCTION INTEGER-OF-FORMATTED-DATE + ("YYYY-DDD", "2013-364") + TO day-int + IF day-int <> 150844 + DISPLAY "Test 2 failed: " day-int END-DISPLAY + END-IF + + MOVE FUNCTION INTEGER-OF-FORMATTED-DATE + ("YYYY-Www-D", "2014-W01-1") + TO day-int + IF day-int <> 150844 + DISPLAY "Test 3 failed: " day-int END-DISPLAY + END-IF + + MOVE FUNCTION INTEGER-OF-FORMATTED-DATE + ("YYYY-MM-DDThh:mm:ss", "2013-12-30T12:34:56") + TO day-int + IF day-int <> 150844 + DISPLAY "Test 4 failed: " day-int END-DISPLAY + END-IF + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION INTEGER-PART]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC S9(4)V9(4) VALUE -1.5. + 01 TEST-FLD PIC S9(04)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION INTEGER-PART ( X ) + TO TEST-FLD. + IF TEST-FLD NOT = -1 + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LENGTH]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC S9(4)V9(4) VALUE -1.5. + 01 TEST-FLD PIC S9(04)V9(02). + PROCEDURE DIVISION. + MOVE FUNCTION LENGTH ( X ) TO TEST-FLD + IF TEST-FLD NOT = 8 + DISPLAY 'LENGTH "00128" wrong: ' TEST-FLD + END-DISPLAY + END-IF + + MOVE FUNCTION LENGTH ( '00128' ) + TO TEST-FLD + IF TEST-FLD NOT = 5 + DISPLAY 'LENGTH "00128" wrong: ' TEST-FLD + END-DISPLAY + END-IF + + MOVE FUNCTION LENGTH ( x'a0' ) + TO TEST-FLD + IF TEST-FLD NOT = 1 + DISPLAY 'LENGTH x"a0" wrong: ' TEST-FLD + END-DISPLAY + END-IF + + MOVE FUNCTION LENGTH ( z'a0' ) + TO TEST-FLD + IF TEST-FLD NOT = 3 + DISPLAY 'LENGTH z"a0" wrong: ' TEST-FLD + END-DISPLAY + END-IF + + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION LOCALE-COMPARE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION LOCALE-COMPARE ("A", "B") NOT = "<" + DISPLAY "Test 1 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOCALE-COMPARE ("B", "A") NOT = ">" + DISPLAY "Test 2 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOCALE-COMPARE ("A", "A") NOT = "=" + DISPLAY "Test 3 fail" + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LOCALE-DATE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(32) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION LOCALE-DATE ( "19630302" ) TO X. + IF X NOT = SPACES + DISPLAY "OK" + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[OK +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LOCALE-TIME]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(32) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION LOCALE-TIME ( "233012" ) TO X. + IF X NOT = SPACES + DISPLAY "OK" + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[OK +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LOCALE-TIME-FROM-SECONDS]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(32) VALUE SPACES. + PROCEDURE DIVISION. + MOVE FUNCTION LOCALE-TIME-FROM-SECONDS ( 33012 ) TO X. + IF X NOT = SPACES + DISPLAY "OK" + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[OK +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LOG]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION LOG ( 1.5 ) TO Y. + IF Y NOT = 0.405465108108164381978013115464349 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION LOG10]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION LOG10 ( 1.5 ) TO Y. + IF Y NOT = 0.176091259055681242081289008530622 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION LOWER-CASE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(10) VALUE "A#B.C%D+E$". + 01 TEST-FLD PIC X(12) VALUE ALL '_'. + PROCEDURE DIVISION. + STRING FUNCTION LOWER-CASE ( X ) + DELIMITED BY SIZE + INTO TEST-FLD + END-STRING + IF TEST-FLD NOT = 'a#b.c%d+e$__' + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LOWER-CASE with reference modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(10) VALUE "A#B.C%D+E$". + 01 TEST-FLD PIC X(03). + PROCEDURE DIVISION. + MOVE FUNCTION LOWER-CASE ( X ) (1 : 3) + TO TEST-FLD + IF TEST-FLD NOT = 'a#b' + DISPLAY TEST-FLD + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION LOWEST-ALGEBRAIC]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 F1 PIC S999. + 01 F2 PIC S9(4) BINARY. + 01 F3 PIC 99V9(3). + 01 F4 PIC $**,**9.99BCR. + 01 F5 PIC $**,**9.99. + 01 F6 USAGE BINARY-CHAR SIGNED. + 01 F7 USAGE BINARY-CHAR UNSIGNED. + 01 F8 PIC S999PPP. + 01 F9 PIC SP99. + PROCEDURE DIVISION. + IF FUNCTION LOWEST-ALGEBRAIC (F1) NOT = -999 + DISPLAY "Test 1 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F2) NOT = -9999 + DISPLAY "Test 2 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F3) NOT = 0 + DISPLAY "Test 3 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F4) NOT = -99999.99 + DISPLAY "Test 4 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F5) NOT = 0 + DISPLAY "Test 5 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F6) NOT = -128 + DISPLAY "Test 6 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F7) NOT = 0 + DISPLAY "Test 7 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F8) NOT = -999000 + DISPLAY "Test 8 fail" + END-DISPLAY + END-IF. + IF FUNCTION LOWEST-ALGEBRAIC (F9) NOT = -0.099 + DISPLAY "Test 9 fail" + END-DISPLAY + END-IF. + + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION MAX]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + DISPLAY FUNCTION MAX ( 3 -14 0 8 -3 ) + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[8 +], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MEAN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 result PIC S999V999. + PROCEDURE DIVISION. + COMPUTE result = FUNCTION MEAN ( 3 -14 0 8 -3 ) + DISPLAY result + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[-001.200 +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MEDIAN]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + DISPLAY FUNCTION MEDIAN ( 3 -14 0 8 -3 ) + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[0 +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MIDRANGE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC S999V999. + PROCEDURE DIVISION. + COMPUTE RESULT = FUNCTION MIDRANGE ( 3 -14 0 8 -3 ) + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[-003.000 +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION MIN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + DISPLAY FUNCTION MIN ( 3 -14 0 8 -3 ) + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[-14 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION MOD (valid)]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC 9(12) VALUE 600851475143. + 01 R PIC S9(4)V9(4) VALUE 0. + PROCEDURE DIVISION. + MOVE FUNCTION MOD ( -11 5 ) TO R + IF R NOT = 4 + DISPLAY 'first one wrong: ' R + END-DISPLAY + END-IF + MOVE FUNCTION MOD ( Y, 71 ) TO R + IF R NOT = 0 + DISPLAY 'second one wrong: ' R + END-DISPLAY + END-IF + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION MOD (invalid)]) +AT_KEYWORDS([functions exceptions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z PIC 9 VALUE 0. + 01 R PIC S9(4)V9(4) VALUE 1. + PROCEDURE DIVISION. + MOVE FUNCTION MOD ( -11 Z ) TO R + IF FUNCTION EXCEPTION-STATUS + NOT = 'EC-ARGUMENT-FUNCTION' + DISPLAY 'Wrong/missing exception: ' + FUNCTION EXCEPTION-STATUS + END-DISPLAY + END-IF + IF R NOT = 0 + DISPLAY 'result is not zero: ' R + END-DISPLAY + END-IF + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + + + +AT_SETUP([FUNCTION NUMVAL]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X1 PIC X(12) VALUE " -9876.1234 ". + 01 X2 PIC X(18) VALUE " 19876.1234 CR". + 01 N PIC s9(5)v9(5). + PROCEDURE DIVISION. + MOVE FUNCTION NUMVAL ( X1 ) TO N + IF N NOT = -9876.1234 + DISPLAY N + END-DISPLAY + END-IF + MOVE FUNCTION NUMVAL ( X2 ) TO N + IF N NOT = -19876.1234 + DISPLAY N + END-DISPLAY + END-IF + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION NUMVAL-C]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X1 PIC X(14) VALUE " -% 9876.1234 ". + 01 X2 PIC X(20) VALUE " % 19,876.1234 DB". + 01 N PIC s9(5)v9(5). + PROCEDURE DIVISION. + MOVE FUNCTION NUMVAL-C ( X1 , "%" ) TO N + IF N NOT = -9876.1234 + DISPLAY N + END-DISPLAY + END-IF + MOVE FUNCTION NUMVAL-C ( X2 , "%" ) TO N + IF N NOT = -19876.1234 + DISPLAY N + END-DISPLAY + END-IF + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION NUMVAL-C DP.COMMA]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + SPECIAL-NAMES. + DECIMAL-POINT IS COMMA + . + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X1 PIC X(20) VALUE " % 19.876,1234 DB". + 01 N PIC s9(5)v9(5). + PROCEDURE DIVISION. + MOVE FUNCTION NUMVAL-C ( X1 , "%" ) TO N + IF N NOT = -19876,1234 + DISPLAY N + END-DISPLAY + END-IF + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION NUMVAL-F]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 result PIC S9(8)V9(9) COMP-5. + 01 vector. + 05 vd. + 10 FILLER PIC X(32) VALUE " - 123.456 E + 2 ". + 10 FILLER PIC X(32) VALUE "123". + 10 FILLER PIC X(32) VALUE ".456". + 10 FILLER PIC X(32) VALUE "123.456". + 10 FILLER PIC X(32) VALUE "-123.456". + 10 FILLER PIC X(32) VALUE "123.456E2". + 10 FILLER PIC X(32) VALUE "-123.456E-2". + 10 FILLER PIC X(32) VALUE "DONE". + 10 FILLER PIC X(32) OCCURS 100 TIMES. + 05 datat REDEFINES vd PIC X(32) OCCURS 100 TIMES INDEXED BY I. + PROCEDURE DIVISION. + SET I TO 1 + PERFORM UNTIL datat(I) EQUALS "DONE" + DISPLAY """"datat(I)"""" SPACE WITH NO ADVANCING + MOVE FUNCTION NUMVAL-F(datat(I)) TO result + DISPLAY result + ADD 1 TO I + END-PERFORM. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[" - 123.456 E + 2 " -00012345.600000000 +"123 " +00000123.000000000 +".456 " +00000000.456000000 +"123.456 " +00000123.456000000 +"-123.456 " -00000123.456000000 +"123.456E2 " +00012345.600000000 +"-123.456E-2 " -00000001.234560000 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION ORD]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC 999. + PROCEDURE DIVISION. + MOVE FUNCTION ORD ( "k" ) TO RESULT + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[108 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION ORD-MAX]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC 999. + PROCEDURE DIVISION. + MOVE FUNCTION ORD-MAX ( 3 -14 0 8 -3 ) TO RESULT + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[004 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION ORD-MIN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC 999. + PROCEDURE DIVISION. + MOVE FUNCTION ORD-MIN ( 3 -14 0 8 -3 ) TO RESULT + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[002 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION PI]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC 9V9(32). + PROCEDURE DIVISION. + MOVE FUNCTION PI TO Y. + IF Y NOT = 3.14159265358979323846264338327950 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION PRESENT-VALUE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC 9(5)V9(4). + PROCEDURE DIVISION. + MOVE FUNCTION PRESENT-VALUE ( 3 2 1 ) TO RESULT + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[00000.5625 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION RANDOM]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S99V99 COMP VALUE -1.0. + PROCEDURE DIVISION. + MOVE FUNCTION RANDOM ( ) TO Y. + IF Y < 0 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION RANGE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z PIC S9(4)V9(4) COMP-5. + PROCEDURE DIVISION. + MOVE FUNCTION RANGE ( 3 -14 0 8 -3 ) TO Z. + IF Z NOT = 22 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION REM (valid)]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 R PIC S9(4)V9(4) COMP-5 VALUE 0. + PROCEDURE DIVISION. + MOVE FUNCTION REM ( -11 5 ) TO R + IF R NOT = -1 + DISPLAY R END-DISPLAY + END-IF + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION REM (invalid)]) +AT_KEYWORDS([functions exceptions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 R PIC S9(4)V9(4) COMP-5 VALUE 4.1. + 01 Z PIC 9 COMP-5 VALUE 0. + PROCEDURE DIVISION. + MOVE FUNCTION REM ( -11 Z ) TO R + IF FUNCTION EXCEPTION-STATUS + NOT = 'EC-ARGUMENT-FUNCTION' + DISPLAY 'Wrong/missing exception: ' + FUNCTION EXCEPTION-STATUS + END-DISPLAY + END-IF + IF R NOT = 0 + DISPLAY 'result is not zero: ' R + END-DISPLAY + END-IF + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION REVERSE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(10) VALUE "A#B.C%D+E$". + 01 Z PIC X(10). + PROCEDURE DIVISION. + MOVE FUNCTION REVERSE ( X ) TO Z. + IF Z NOT = "$E+D%C.B#A" + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION REVERSE with reference modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(10) VALUE "A#B.C%D+E$". + 01 Z PIC X(10). + PROCEDURE DIVISION. + MOVE FUNCTION REVERSE ( X ) (1 : 4) TO Z. + IF Z NOT = "$E+D " + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION SECONDS-FROM-FORMATTED-TIME]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 result PIC 9(8)V9(9) COMP-5. + PROCEDURE DIVISION. + MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME + ("hhmmss", "010203") + TO result. + IF result NOT = 3723 + DISPLAY "Test 1 failed: " result + END-DISPLAY + END-IF. + + MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME + ("hh:mm:ss", "01:02:03") + TO result. + IF result NOT = 3723 + DISPLAY "Test 2 failed: " result + END-DISPLAY + END-IF. + + MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME + ("hhmmss.ssssssss", "010203.04050607") + TO result. + IF result NOT = 3723.04050607 + DISPLAY "Test 3 failed: " result + END-DISPLAY + END-IF. + + MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME + ("hhmmssZ", "010203Z") + TO result. + IF result NOT = 3723 + DISPLAY "Test 4 failed: " result + END-DISPLAY + END-IF. + + MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME + ("hhmmss+hhmm", "010203+0405") + TO result. + IF result NOT = 3723 + DISPLAY "Test 5 failed: " result + END-DISPLAY + END-IF. + + MOVE FUNCTION SECONDS-FROM-FORMATTED-TIME + ("YYYYMMDDThhmmss", "16010101T010203") + TO result. + IF result NOT = 3723 + DISPLAY "Test 6 failed: " result + END-DISPLAY + END-IF. + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION SECONDS-PAST-MIDNIGHT]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC 9(8) COMP-5. + PROCEDURE DIVISION. + MOVE FUNCTION SECONDS-PAST-MIDNIGHT TO Y. + IF Y NOT < 86402 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION SIGN]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z USAGE BINARY-LONG SIGNED. + PROCEDURE DIVISION. + MOVE FUNCTION SIGN ( 3.12345 ) TO Z. + IF Z NOT = 1 + DISPLAY "Sign 1 " Z + END-DISPLAY + END-IF. + MOVE FUNCTION SIGN ( -0.0 ) TO Z. + IF Z NOT = 0 + DISPLAY "Sign 2 " Z + END-DISPLAY + END-IF. + MOVE FUNCTION SIGN ( 0.0 ) TO Z. + IF Z NOT = 0 + DISPLAY "Sign 3 " Z + END-DISPLAY + END-IF. + MOVE FUNCTION SIGN ( -3.12345 ) TO Z. + IF Z NOT = -1 + DISPLAY "Sign 4 " Z + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION SIN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION SIN ( 1.5 ) TO Y. + IF Y NOT = 0.997494986604054430941723371141487 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION SQRT]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(33). + PROCEDURE DIVISION. + MOVE FUNCTION SQRT ( 1.5 ) TO Y. + IF Y NOT = 1.224744871391589049098642037352945 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION STANDARD-DEVIATION]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S9V9(32). + PROCEDURE DIVISION. + MOVE FUNCTION STANDARD-DEVIATION ( 3 -14 0 8 -3 ) TO Y. + IF Y NOT = 7.35934779718963954877237043574538 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION SUBSTITUTE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(24). + PROCEDURE DIVISION. + MOVE "abc111444555defxxabc" TO Y + DISPLAY FUNCTION TRIM (FUNCTION SUBSTITUTE ( Y "abc" "zz" "55" "666" )) + + MOVE "bobBobjimJimbobBobjimJim" TO Y + DISPLAY FUNCTION SUBSTITUTE ( Y "bob" "FILLER" "jim" "Z") + + MOVE "bobBobjimJimbobBobjimJim" TO Y + DISPLAY FUNCTION SUBSTITUTE ( Y FIRST "bob" "FILLER" "jim" "Z") + + MOVE "bobBobjimJimbobBobjimJim" TO Y + DISPLAY FUNCTION SUBSTITUTE ( Y LAST "bob" "FILLER" "jim" "Z") + + MOVE "bobBobjimJimbobBobjimJim" TO Y + DISPLAY FUNCTION SUBSTITUTE ( Y ANYCASE "bob" "FILLER" ANYCASE "jim" "Z") +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[zz1114446665defxxzz +FILLERBobZJimFILLERBobZJim +FILLERBobZJimbobBobZJim +bobBobZJimFILLERBobZJim +FILLERFILLERZZFILLERFILLERZZ +], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION SUBSTITUTE with reference modding]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(20). + 01 Z PIC X(20). + PROCEDURE DIVISION. + MOVE "abc111444555defxxabc" TO Y. + MOVE FUNCTION SUBSTITUTE + ( Y "abc" "zz" "55" "666" ) (2 : 9) + TO Z. + IF Z NOT = "z11144466" + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION SUBSTITUTE-CASE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(20). + 01 Z PIC X(20). + PROCEDURE DIVISION. + MOVE "ABC111444555defxxabc" TO Y. + MOVE FUNCTION SUBSTITUTE (Y anycase "abc" "zz" + anycase "55" "666") + TO Z. + IF Z NOT = "zz1114446665defxxzz" + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION SUBSTITUTE-CASE with reference mod]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC X(20). + 01 Z PIC X(20). + PROCEDURE DIVISION. + MOVE "abc111444555defxxabc" TO Y. + MOVE FUNCTION SUBSTITUTE + ( Y anycase "ABC" "zz" + anycase "55" "666" ) (2 : 9) + TO Z. + IF Z NOT = "z11144466" + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION SUM]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z USAGE BINARY-LONG. + PROCEDURE DIVISION. + MOVE FUNCTION SUM ( 3 -14 0 8 -3 ) TO Z. + IF Z NOT = -6 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION TAN]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Y PIC S99V9(31). + PROCEDURE DIVISION. + MOVE FUNCTION TAN ( 1.5 ) TO Y. + IF Y NOT = 14.1014199471717193876460836519877 + DISPLAY Y + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-DATE-YYYYMMDD]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC 999. + PROCEDURE DIVISION. + MOVE FUNCTION TEST-DATE-YYYYMMDD ( 20020231 ) TO RESULT + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[003 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-DAY-YYYYDDD]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RESULT PIC 999. + PROCEDURE DIVISION. + MOVE FUNCTION TEST-DAY-YYYYDDD ( 2002400 ) TO RESULT + DISPLAY RESULT + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[002 +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME with dates]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "16010101") <> 0 + DISPLAY "Test 1 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYY-MM-DD", "1601-01-01") <> 0 + DISPLAY "Test 2 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYDDD", "1601001") <> 0 + DISPLAY "Test 3 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYY-DDD", "1601-001") <> 0 + DISPLAY "Test 4 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYWwwD", "1601W011") <> 0 + DISPLAY "Test 5 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYY-Www-D", "1601-W01-1") <> 0 + DISPLAY "Test 6 failed" END-DISPLAY + END-IF + + + *> How will this work with zero-length items? + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "1") <> 2 + DISPLAY "Test 7 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "160A0101") <> 4 + DISPLAY "Test 8 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "00000101") <> 1 + DISPLAY "Test 9 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "16000101") <> 4 + DISPLAY "Test 10 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "16010001") <> 6 + DISPLAY "Test 11 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "16011301") <> 6 + DISPLAY "Test 12 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "16010190") <> 7 + DISPLAY "Test 13 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "18000229") <> 8 + DISPLAY "Test 14 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYY-MM-DD", "1601 01 01") <> 5 + DISPLAY "Test 15 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "160101010") <> 9 + DISPLAY "Test 16 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYWwwD", "1601A011") <> 5 + DISPLAY "Test 17 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYWwwD", "1601W531") <> 7 + DISPLAY "Test 18 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYWwwD", "1601W601") <> 6 + DISPLAY "Test 19 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYWwwD", "2009W531") <> 0 + DISPLAY "Test 20 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYWwwD", "1601W018") <> 8 + DISPLAY "Test 21 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYDDD", "1601366") <> 7 + DISPLAY "Test 22 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYDDD", "1601370") <> 6 + DISPLAY "Test 23 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYDDD", "1601400") <> 5 + DISPLAY "Test 24 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "01") <> 1 + DISPLAY "Test 25 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDD", "1601010") <> 8 + DISPLAY "Test 26 failed" END-DISPLAY + END-IF + + STOP RUN + . +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME with times]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss.sssssssssZ", "000000.000000000Z") <> 0 + DISPLAY "Test 1 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hh:mm:ss.sssssssssZ", "00:00:00.000000000Z") <> 0 + DISPLAY "Test 2 failed" END-DISPLAY + END-IF + *> 0 instead of +/- valid in sending fields with offset of zero. + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss.sssssssss+hhmm", "000000.00000000000000") + <> 0 + DISPLAY "Test 3 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hh:mm:ss.sssssssss+hh:mm", + "00:00:00.000000000+00:00") + <> 0 + DISPLAY "Test 4 failed" END-DISPLAY + END-IF + + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss", "300000") <> 1 + DISPLAY "Test 5 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss", "250000") <> 2 + DISPLAY "Test 6 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss", "006000") <> 3 + DISPLAY "Test 7 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss", "000060") <> 5 + DISPLAY "Test 8 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hh:mm:ss", "00-00-00") <> 3 + DISPLAY "Test 9 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss.ss", "000000,00") <> 7 + DISPLAY "Test 10 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss+hhmm", "000000 0000") <> 7 + DISPLAY "Test 11 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss+hhmm", "00000000001") <> 11 + DISPLAY "Test 12 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmssZ", "000000A") <> 7 + DISPLAY "Test 13 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss", SPACE) <> 1 + DISPLAY "Test 14 failed" END-DISPLAY + END-IF + + STOP RUN + . +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME with datetimes]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 RESULT PIC 9(02). + PROCEDURE DIVISION. + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDDThhmmss", "16010101T000000") + TO RESULT + IF RESULT <> 0 + DISPLAY "Test 1 failed: " RESULT END-DISPLAY + END-IF + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("YYYY-MM-DDThh:mm:ss.sssssssss+hh:mm", + "1601-01-01T00:00:00.000000000+00:00") + TO RESULT + IF RESULT <> 0 + DISPLAY "Test 2 failed: " RESULT END-DISPLAY + END-IF + + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDDThhmmss", "16010101 000000") + TO RESULT + IF RESULT <> 9 + DISPLAY "Test 3 failed: " RESULT END-DISPLAY + END-IF + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDDThhmmss", SPACE) + TO RESULT + IF RESULT <> 1 + DISPLAY "Test 4 failed: " RESULT END-DISPLAY + END-IF + MOVE FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDDThhmmss", "16010101T ") + TO RESULT + IF RESULT <> 10 + DISPLAY "Test 5 failed: " RESULT END-DISPLAY + END-IF + + STOP RUN + . +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-FORMATTED-DATETIME DP.COMMA]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + SPECIAL-NAMES. + DECIMAL-POINT IS COMMA. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss,ss", "000000,00") <> 0 + DISPLAY "Test 1 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDDThhmmss,ss", "16010101T000000,00") <> 0 + DISPLAY "Test 2 failed" END-DISPLAY + END-IF + + IF FUNCTION TEST-FORMATTED-DATETIME + ("hhmmss,ss", "000000.00") <> 7 + DISPLAY "Test 3 failed" END-DISPLAY + END-IF + IF FUNCTION TEST-FORMATTED-DATETIME + ("YYYYMMDDThhmmss,ss", "16010101T000000.00") <> 16 + DISPLAY "Test 4 failed" END-DISPLAY + END-IF + + STOP RUN + . +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-NUMVAL]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION TEST-NUMVAL ("+ 1") NOT = 0 + DISPLAY "Test 1 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL (" + 1") NOT = 0 + DISPLAY "Test 2 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("- 1") NOT = 0 + DISPLAY "Test 3 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL (" - 1") NOT = 0 + DISPLAY "Test 4 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("+- 1") NOT = 2 + DISPLAY "Test 5 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1 +") NOT = 0 + DISPLAY "Test 6 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1 -") NOT = 0 + DISPLAY "Test 7 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1 +-") NOT = 4 + DISPLAY "Test 8 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1 -+") NOT = 4 + DISPLAY "Test 9 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("+ 1.1") NOT = 0 + DISPLAY "Test 10 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("- 1.1") NOT = 0 + DISPLAY "Test 11 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 +") NOT = 0 + DISPLAY "Test 12 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 -") NOT = 0 + DISPLAY "Test 13 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 CR") NOT = 0 + DISPLAY "Test 14 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 DB") NOT = 0 + DISPLAY "Test 15 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 -CR") NOT = 6 + DISPLAY "Test 16 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 +DB") NOT = 6 + DISPLAY "Test 17 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("1.1 CDB") NOT = 6 + DISPLAY "Test 18 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("+1.1 CR") NOT = 6 + DISPLAY "Test 19 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL ("+ ") NOT = 8 + DISPLAY "Test 20 fail" + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION TEST-NUMVAL-C]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION TEST-NUMVAL-C ("+ 1") NOT = 0 + DISPLAY "Test 1 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C (" + 1") NOT = 0 + DISPLAY "Test 2 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("- 1") NOT = 0 + DISPLAY "Test 3 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C (" - 1") NOT = 0 + DISPLAY "Test 4 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("+- 1") NOT = 2 + DISPLAY "Test 5 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1 +") NOT = 0 + DISPLAY "Test 6 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1 -") NOT = 0 + DISPLAY "Test 7 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1 +-") NOT = 4 + DISPLAY "Test 8 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1 -+") NOT = 4 + DISPLAY "Test 9 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("+ 1.1") NOT = 0 + DISPLAY "Test 10 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("- 1.1") NOT = 0 + DISPLAY "Test 11 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1.1 +") NOT = 0 + DISPLAY "Test 12 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1.1 -") NOT = 0 + DISPLAY "Test 13 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1.1 CR") NOT = 0 + DISPLAY "Test 14 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1.1 DB") NOT = 0 + DISPLAY "Test 15 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("1.1 -CR") NOT = 6 + DISPLAY "Test 16 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("+ $1.1 ") NOT = 0 + DISPLAY "Test 17 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("- $1.1 ") NOT = 0 + DISPLAY "Test 18 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("+ X1.1 ", "X") NOT = 0 + DISPLAY "Test 19 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-C ("- X1.1 ", "X") NOT = 0 + DISPLAY "Test 20 fail" + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + +AT_SETUP([FUNCTION TEST-NUMVAL-F]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + PROCEDURE DIVISION. + IF FUNCTION TEST-NUMVAL-F ("+ 1") NOT = 0 + DISPLAY "Test 1 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F (" + 1") NOT = 0 + DISPLAY "Test 2 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("- 1") NOT = 0 + DISPLAY "Test 3 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F (" - 1") NOT = 0 + DISPLAY "Test 4 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("+- 1") NOT = 2 + DISPLAY "Test 5 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1 +") NOT = 3 + DISPLAY "Test 6 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1 -") NOT = 3 + DISPLAY "Test 7 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1 +-") NOT = 3 + DISPLAY "Test 8 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1 -+") NOT = 3 + DISPLAY "Test 9 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("+ 1.1") NOT = 0 + DISPLAY "Test 10 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("- 1.1") NOT = 0 + DISPLAY "Test 11 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 +") NOT = 5 + DISPLAY "Test 12 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 -") NOT = 5 + DISPLAY "Test 13 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 ") NOT = 0 + DISPLAY "Test 14 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 ") NOT = 0 + DISPLAY "Test 15 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 -CR") NOT = 5 + DISPLAY "Test 16 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 E+1") NOT = 0 + DISPLAY "Test 17 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 E -1") NOT = 0 + DISPLAY "Test 18 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("1.1 EE") NOT = 6 + DISPLAY "Test 19 fail" + END-DISPLAY + END-IF. + IF FUNCTION TEST-NUMVAL-F ("+1.1 E+01") NOT = 0 + DISPLAY "Test 20 fail" + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([FUNCTION TRIM]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(12) VALUE " a#b.c%d+e$ ". + PROCEDURE DIVISION. + DISPLAY FUNCTION TRIM ( X ) + END-DISPLAY. + DISPLAY FUNCTION TRIM ( X TRAILING ) + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[a#b.c%d+e$ + a#b.c%d+e$ +]) +AT_CLEANUP + + +AT_SETUP([FUNCTION TRIM with reference modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(12) VALUE " a#b.c%d+e$ ". + PROCEDURE DIVISION. + DISPLAY FUNCTION TRIM ( X ) (2 : 3) + END-DISPLAY. + DISPLAY FUNCTION TRIM ( X TRAILING ) (2 : 3) + END-DISPLAY. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[#b. +a#b +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION TRIM zero length]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 A2 PIC X(2) VALUE " ". + 01 A3 PIC X(3) VALUE " ". + 01 X PIC X(4) VALUE "NOOK". + PROCEDURE DIVISION. + MOVE FUNCTION TRIM ( A2 ) TO X. + DISPLAY ">" X "<" + END-DISPLAY. + DISPLAY ">" FUNCTION TRIM ( A3 ) "<" + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[> < +>< +]) + +AT_CLEANUP + + +AT_SETUP([FUNCTION UPPER-CASE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(10) VALUE "a#b.c%d+e$". + 01 Z PIC X(10). + PROCEDURE DIVISION. + MOVE FUNCTION UPPER-CASE ( X ) TO Z. + IF Z NOT = "A#B.C%D+E$" + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION UPPER-CASE with reference modding]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 X PIC X(10) VALUE "a#b.c%d+e$". + 01 Z PIC X(4). + PROCEDURE DIVISION. + MOVE FUNCTION UPPER-CASE ( X ) (1 : 3) TO Z. + IF Z NOT = "A#B " + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION VARIANCE]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z PIC S9(4)V9(4) COMP-5. + PROCEDURE DIVISION. + MOVE FUNCTION VARIANCE ( 3 -14 0 8 -3 ) TO Z. + IF Z NOT = 54.16 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) + +AT_CLEANUP + + +AT_SETUP([FUNCTION WHEN-COMPILED]) +AT_KEYWORDS([functions]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 compiled-datetime. + 03 compiled-date. + 05 millennium PIC X. + 05 FILLER PIC X(15). + 03 timezone PIC X(5). + PROCEDURE DIVISION. + *> Check millennium. + MOVE FUNCTION WHEN-COMPILED TO compiled-datetime. + IF millennium NOT = "2" + DISPLAY "Millennium NOT OK: " millennium + END-DISPLAY + END-IF. + + *> Check timezone. + IF timezone NOT = FUNCTION CURRENT-DATE (17:5) + DISPLAY "Timezone NOT OK: " timezone + END-DISPLAY + END-IF. + + *> Check date format. + INSPECT compiled-date CONVERTING "0123456789" + TO "9999999999". + IF compiled-date NOT = ALL "9" + DISPLAY "Date format NOT OK: " compiled-date + END-DISPLAY + END-IF. + + *> Check timezone format. + IF timezone NOT = "00000" + INSPECT timezone CONVERTING "0123456789" + TO "9999999999" + IF timezone NOT = "+9999" AND "-9999" + DISPLAY "Timezone format NOT OK: " timezone + END-DISPLAY + END-IF + END-IF. + + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + +AT_SETUP([FUNCTION YEAR-TO-YYYY]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 Z USAGE BINARY-LONG. + PROCEDURE DIVISION. + MOVE FUNCTION YEAR-TO-YYYY ( 50 ) TO Z. + IF Z NOT = 2050 + DISPLAY Z + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + +AT_SETUP([UDF RETURNING group and PIC 9(5)]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + FUNCTION-ID. COPYPAR. + DATA DIVISION. + LINKAGE SECTION. + 01 PARSA. + 02 PAR1 PICTURE X(32). + 02 PAR2 PICTURE X(32). + 01 PARSB. + 02 PAR1 PICTURE X(32). + 02 PAR2 PICTURE X(32). + PROCEDURE DIVISION USING PARSA RETURNING PARSB. + MOVE PARSA TO PARSB + DISPLAY """" PARSB """" + GOBACK. + END FUNCTION COPYPAR. + IDENTIFICATION DIVISION. + FUNCTION-ID. COPYPAR2. + DATA DIVISION. + LINKAGE SECTION. + 01 PARSB PIC 99999. + 01 PAR5 PIC 99999. + PROCEDURE DIVISION USING PAR5 RETURNING PARSB. + MOVE PAR5 TO PARSB + DISPLAY PARSB + GOBACK. + END FUNCTION COPYPAR2. + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + FUNCTION COPYPAR. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 PARS1. + 02 PAR1 PICTURE X(32) VALUE "Santa". + 02 PAR2 PICTURE X(32) VALUE "Claus". + 01 PARS2. + 02 PAR1 PICTURE X(32). + 02 PAR2 PICTURE X(32). + 01 PAR5 PICTURE 99999 VALUE 54321. + PROCEDURE DIVISION. + MOVE COPYPAR(PARS1) TO PARS2 + DISPLAY """" PARS2 """". + DISPLAY COPYPAR2(PAR5) + STOP RUN. + END PROGRAM prog. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], ["Santa Claus " +"Santa Claus " +54321 +54321 +], []) +AT_CLEANUP + +AT_SETUP([UDF in COMPUTE]) +AT_KEYWORDS([functions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + FUNCTION-ID. func. + + DATA DIVISION. + LINKAGE SECTION. + 01 num PIC 999. + + PROCEDURE DIVISION RETURNING num. + MOVE 100 TO num + . + END FUNCTION func. + + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + FUNCTION func. + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 x PIC 999. + + PROCEDURE DIVISION. + COMPUTE x = 101 + FUNCTION func + DISPLAY x + . + END PROGRAM prog. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[201 +]) +AT_CLEANUP + +AT_SETUP([UDF with recursion]) +AT_KEYWORDS([functions LOCAL-STORAGE]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + FUNCTION-ID. foo. + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 ttl PIC 9 VALUE 1. + + LOCAL-STORAGE SECTION. + 01 num PIC 9. + + LINKAGE SECTION. + 01 arg PIC 9. + 01 ret PIC 9. + + PROCEDURE DIVISION USING arg RETURNING ret. + IF arg < 5 + ADD 1 TO arg GIVING num END-ADD + MOVE FUNCTION foo (num) TO ret + ELSE + MOVE arg TO ret + END-IF + DISPLAY "Step: " ttl ", Arg: " arg ", Return: " ret + END-DISPLAY + ADD 1 to ttl END-ADD + GOBACK. + END FUNCTION foo. + + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + FUNCTION foo. + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 num PIC 9 VALUE 1. + + PROCEDURE DIVISION. + DISPLAY "Return value '" FUNCTION foo (num) "'" + WITH NO ADVANCING + END-DISPLAY + GOBACK. + END PROGRAM prog. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], +[Step: 1, Arg: 5, Return: 5 +Step: 2, Arg: 4, Return: 5 +Step: 3, Arg: 3, Return: 5 +Step: 4, Arg: 2, Return: 5 +Step: 5, Arg: 1, Return: 5 +Return value '5'], []) +AT_CLEANUP + +AT_SETUP([Program-to-program parameters and retvals]) +AT_KEYWORDS([functions parameter]) +AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 var1 pic 9 VALUE 1. + 01 var2 BINARY-CHAR VALUE 22. + 01 var3 pic s999 COMP-3 VALUE -333. + 01 var4 pic 9999 BINARY VALUE 4444. + 01 var5 pic 99.99 VALUE "12.34". + 01 var6 pic s999V999 COMP-5 VALUE -123.456. + 01 var7 float-short VALUE 1.23E10. + 01 var8 float-long VALUE -1.23E20. + 01 var9 float-extended VALUE 1.23E40. + 01 var64 pic 9(15) VALUE 987654321098765. + 01 var128 pic s9(30) VALUE -987654321098765432109876543210. + 01 filler. + 02 varpd pic 9(18) comp-5 value 1250999747361. + 02 varp redefines varpd pointer. + 01 varg. + 02 varg1 pic x(7) VALUE "That's". + 02 varg2 pic x(5) VALUE "all," . + 02 varg3 pic x(7) VALUE "folks!". + + 01 var1r pic 9 . + 01 var2r BINARY-CHAR . + 01 var3r pic s999 COMP-3 . + 01 var4r pic 9999 BINARY . + 01 var5r pic 99.99 . + 01 var6r pic s999V999 COMP-5 . + 01 var7r float-short . + 01 var8r float-long . + 01 var9r float-extended . + 01 var64r pic 9(15) . + 01 var128r pic s9(30) . + 01 varpr pointer. + 01 vargr. + 02 varg1 pic x(7). + 02 varg2 pic x(5). + 02 varg3 pic x(7). + + PROCEDURE DIVISION. + display var1 + call "rvar1" USING by value var1 RETURNING var1r + display var1r + + display var2 + call "rvar2" USING by reference var2 RETURNING var2r + display var2r + + display var3 + call "rvar3" USING by content var3 RETURNING var3r + display var3r + + display var4 + call "rvar4" USING by value var4 RETURNING var4r + display var4r + + display var5 + call "rvar5" USING by reference var5 RETURNING var5r + display var5r + + display var6 + call "rvar6" USING by content var6 RETURNING var6r + display var6r + + display var7 + call "rvar7" USING by reference var7 RETURNING var7r + display var7r + + display var8 + call "rvar8" USING by value var8 RETURNING var8r + display var8r + + display var9 + call "rvar9" USING by content var9 RETURNING var9r + display var9r + + display var64 + call "rvar64" USING by value var64 RETURNING var64r + display var64r + + display var128 + call "rvar128" USING by reference var128 RETURNING var128r + display var128r + + display varp + call "rvarp" USING by reference varp RETURNING varpr + display varpr + + display """"varg"""" + call "rvarg" USING by reference varg RETURNING vargr + display """"vargr"""" + + GOBACK. + END PROGRAM prog. + + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar1. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic 9 . + 01 varr pic 9 . + PROCEDURE DIVISION USING by value var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar1. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar2. + DATA DIVISION. + LINKAGE SECTION. + 01 var BINARY-CHAR . + 01 varr BINARY-CHAR . + PROCEDURE DIVISION USING by reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar2. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar3. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic s999 COMP-3 . + 01 varr pic s999 COMP-3 . + PROCEDURE DIVISION USING by reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar3. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar4. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic 9999 BINARY . + 01 varr pic 9999 BINARY . + PROCEDURE DIVISION USING by value var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar4. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar5. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic 99.99 . + 01 varr pic 99.99 . + PROCEDURE DIVISION USING by reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar5. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar6. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic s999V999 COMP-5 . + 01 varr pic s999V999 COMP-5 . + PROCEDURE DIVISION USING reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar6. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar7. + DATA DIVISION. + LINKAGE SECTION. + 01 var float-short . + 01 varr float-short . + PROCEDURE DIVISION USING by reference VAR RETURNING varr. + MOVE var TO varr. + GOBACK. + END PROGRAM rvar7. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar8. + DATA DIVISION. + LINKAGE SECTION. + 01 var float-long . + 01 varr float-long . + PROCEDURE DIVISION USING by value var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar8. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar9. + DATA DIVISION. + LINKAGE SECTION. + 01 var float-extended . + 01 varr float-extended . + PROCEDURE DIVISION USING by reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar9. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar64. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic 9(15) . + 01 varr pic 9(15) . + PROCEDURE DIVISION USING by value var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar64. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvar128. + DATA DIVISION. + LINKAGE SECTION. + 01 var pic s9(30) . + 01 varr pic s9(30) . + PROCEDURE DIVISION USING by reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvar128. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvarp. + DATA DIVISION. + LINKAGE SECTION. + 01 var pointer . + 01 varr pointer . + PROCEDURE DIVISION USING by reference var RETURNING varr. + SET varr TO var. + END PROGRAM rvarp. + + IDENTIFICATION DIVISION. + PROGRAM-ID. rvarg. + DATA DIVISION. + LINKAGE SECTION. + 01 var. + 02 varg1 pic x(7). + 02 varg2 pic x(5). + 02 varg3 pic x(7). + 01 varr. + 02 varg1 pic x(7). + 02 varg2 pic x(5). + 02 varg3 pic x(7). + PROCEDURE DIVISION USING by reference var RETURNING varr. + MOVE var TO varr. + END PROGRAM rvarg. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [1 +1 ++022 ++022 +-333 +-333 +4444 +4444 +12.34 +12.34 +-123.456 +-123.456 +1.230000026E+10 +1.230000026E+10 +-1.23E+20 +-1.23E+20 +1.23E+40 +1.23E+40 +987654321098765 +987654321098765 +-987654321098765432109876543210 +-987654321098765432109876543210 +0x0000012345654321 +0x0000012345654321 +"That's all, folks! " +"That's all, folks! " +], []) +AT_CLEANUP + +AT_SETUP([Recursive FUNCTION with local-storage]) +AT_KEYWORDS([functions parameter]) +AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. + FUNCTION-ID. callee. + DATA DIVISION. + LOCAL-STORAGE SECTION. + 01 LCL-X PIC 999 . + LINKAGE SECTION. + 01 parm PIC 999. + 01 retval PIC 999. + PROCEDURE DIVISION USING parm RETURNING retval. + display "On entry, parm is: " parm + move parm to lcl-x + move parm to retval + subtract 1 from parm + if parm > 0 + display "A The function returns " function callee(parm). + if lcl-x not equal to retval + display "On exit, lcl-s and retval are: " lcl-x " and " retval + display "But they should be equal to each other" + end-if + goback. + end function callee. + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + FUNCTION callee. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 val PIC 999 VALUE 5. + PROCEDURE DIVISION. + DISPLAY "Starting value is: " val + display "B The function returns " function callee(val). + STOP RUN. + end program caller. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [Starting value is: 005 +On entry, parm is: 005 +On entry, parm is: 004 +On entry, parm is: 003 +On entry, parm is: 002 +On entry, parm is: 001 +A The function returns 001 +A The function returns 002 +A The function returns 003 +A The function returns 004 +B The function returns 005 +], []) +AT_CLEANUP + +AT_SETUP([Repository functions clause]) +AT_KEYWORDS([functions repository]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + SOURCE-COMPUTER. a. + OBJECT-COMPUTER. a. + REPOSITORY. + FUNCTION ALL INTRINSIC. + PROCEDURE DIVISION. + DISPLAY "OK". +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([./a.out], [0], [OK +]) +AT_CLEANUP + +AT_SETUP([FUNCTION BIGGER-POINTER]) +AT_KEYWORDS([functions POINTER ]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 N PIC S9(8) COMP-5 value 0. + 01 P REDEFINES N POINTER. + 01 FILLER. + 05 X PIC A(4) VALUE "ABC". + 05 E REDEFINES X PIC A(1) OCCURS 4. + LINKAGE SECTION. + 77 B PIC A. + + PROCEDURE DIVISION. + set P to address of E(1). + + display FUNCTION trim(x) '.' + + set address of B to p. + perform until B = SPACE + display B no advancing + set p up by 1 + set address of B to p + end-perform + display '.' + + set P to address of E(1) + set address of B to p + perform until B = SPACES + display B no advancing + add 1 to N + set address of B to p + end-perform + display '.' + + STOP RUN. +]) +AT_CHECK([$COMPILE -dialect ibm prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [ABC. +ABC. +ABC. +], []) +AT_CLEANUP + +AT_SETUP([FUNCTION BIGGER-POINTER (2)]) +AT_KEYWORDS([functions POINTER ]) +AT_DATA([prog.cob], [ + identification division. + program-id. prog. + data division. + working-storage section. + 01 n4 pic s9(8) comp-5 value 0. + 01 p4 redefines n4 pointer. + 01 n8 pic s9(16) comp-5 value 0. + 01 p8 redefines n8 pointer. + procedure division. + move -1 to n8 + set p4 to p8 + display "P4 and P8 before: " p4 space p8 + display "Increment N4 and N8" + add 1 to n4 n8 + display "P4 and P8 after: " p4 space p8 + goback. + end program prog. +]) +AT_CHECK([$COMPILE -dialect ibm prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [P4 and P8 before: 0xffffffffffffffff 0xffffffffffffffff +Increment N4 and N8 +P4 and P8 after: 0x0000000000000000 0x0000000000000000 +], []) +AT_CLEANUP + diff --git a/gcc/cobol/UAT/testsuite.src/run_fundamental.at b/gcc/cobol/UAT/testsuite.src/run_fundamental.at index 018a815e963abdd299cd89b87c4d52221d665105..7c9465117718a7536adb7f0065af3e204127e60b 100644 --- a/gcc/cobol/UAT/testsuite.src/run_fundamental.at +++ b/gcc/cobol/UAT/testsuite.src/run_fundamental.at @@ -914,6 +914,25 @@ AT_CHECK([./a.out], [0], [0], []) AT_CLEANUP +AT_SETUP([Context sensitive words (5)]) +AT_KEYWORDS([fundamental recursive]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog RECURSIVE. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 RECURSIVE PIC 9 VALUE 0. + PROCEDURE DIVISION. + DISPLAY RECURSIVE NO ADVANCING + END-DISPLAY. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([./a.out], [0], [0], []) +AT_CLEANUP + AT_SETUP([Context sensitive words (6)]) AT_KEYWORDS([fundamental normal]) AT_DATA([prog.cob], [ diff --git a/gcc/cobol/UAT/testsuite.src/run_misc.at b/gcc/cobol/UAT/testsuite.src/run_misc.at index 8b01f5d16f34b83987f807daf6fc97ac95d6d35c..b9bdc50d834e617431e6b5d251b08a8e76a080a7 100644 --- a/gcc/cobol/UAT/testsuite.src/run_misc.at +++ b/gcc/cobol/UAT/testsuite.src/run_misc.at @@ -3213,7 +3213,6 @@ AT_DATA([prog.cob], [ ]) AT_CHECK([$COMPILE prog.cob], [1], [], [prog.cob:18: error: literal 'SUB ' must be a COBOL or C identifier at '"' -prog.cob:22: 1 errors in DATA DIVISION, compilation ceases at 'PROCEDURE DIVISION' cobol1: error: failed compiling prog.cob ]) AT_CLEANUP @@ -3338,3 +3337,396 @@ AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) AT_CHECK([$COMPILE -o prog caller.cob callee.o callee2.o], [0], [], []) AT_CHECK([./prog], [0], [abc000], []) AT_CLEANUP + +AT_SETUP([LOCAL-STORAGE (3) with recursive PROGRAM-ID]) +AT_KEYWORDS([misc]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + PROCEDURE DIVISION. + CALL "callee" + END-CALL. + STOP RUN. + end program caller. + + IDENTIFICATION DIVISION. + PROGRAM-ID. callee. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 WRK-X PIC 999 VALUE 5. + LOCAL-STORAGE SECTION. + 01 LCL-X PIC 999 . + PROCEDURE DIVISION. + display "On entry: " wrk-x + move wrk-x to lcl-x + subtract 1 from wrk-x + if wrk-x > 0 + call "callee". + display "On exit: " lcl-x + goback. + end program callee. +]) +AT_CHECK([$COMPILE -o prog prog.cob], [0], [], []) +AT_CHECK([./prog], [0], [On entry: 005 +On entry: 004 +On entry: 003 +On entry: 002 +On entry: 001 +On exit: 001 +On exit: 002 +On exit: 003 +On exit: 004 +On exit: 005 +], []) +AT_CLEANUP + +AT_SETUP([LOCAL-STORAGE (4) with recursive PROGRAM-ID ... USING]) +AT_KEYWORDS([misc]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + PROCEDURE DIVISION. + CALL "callee" + END-CALL. + STOP RUN. + end program caller. + + IDENTIFICATION DIVISION. + PROGRAM-ID. callee. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 WRK-X PIC 999 VALUE 5. + LOCAL-STORAGE SECTION. + 01 LCL-X PIC 999 . + PROCEDURE DIVISION. + display "On entry: " wrk-x + move wrk-x to lcl-x + subtract 1 from wrk-x + if wrk-x > 0 + call "callee". + display "On exit: " lcl-x + goback. + end program callee. +]) +AT_CHECK([$COMPILE -o prog prog.cob], [0], [], []) +AT_CHECK([./prog], [0], [On entry: 005 +On entry: 004 +On entry: 003 +On entry: 002 +On entry: 001 +On exit: 001 +On exit: 002 +On exit: 003 +On exit: 004 +On exit: 005 +], []) +AT_CLEANUP + +AT_SETUP([Recursive CALL of RECURSIVE program]) +AT_KEYWORDS([misc CANCEL EXTERNAL]) +AT_DATA([caller.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. caller IS RECURSIVE. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC S9 EXTERNAL. + PROCEDURE DIVISION. + MOVE 0 TO STOPPER + CALL "callee" + DISPLAY 'OK' NO ADVANCING END-DISPLAY + *> FIXME: CANCEL broken on special environments + *> CANCEL "callee" , "callee2" + DISPLAY ' + FINE' NO ADVANCING END-DISPLAY + STOP RUN. +]) +AT_DATA([callee.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee IS RECURSIVE. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC S9 EXTERNAL. + PROCEDURE DIVISION. + IF STOPPER = 9 + MOVE -1 TO STOPPER + ELSE + ADD 1 TO STOPPER + CALL "callee2" + END-IF + GOBACK. +]) +AT_DATA([callee2.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee2 IS RECURSIVE. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC S9 EXTERNAL. + PROCEDURE DIVISION. + IF STOPPER NOT EQUAL -1 + CALL "callee" + END-IF + GOBACK. +]) + +AT_CHECK([$COMPILE -c callee.cob], [0], [], []) +AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) +AT_CHECK([$COMPILE -o caller caller.cob callee.o callee2.o], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./caller], [0], [OK + FINE], []) +AT_CLEANUP + + +AT_SETUP([Recursive CALL of INITIAL program]) +AT_KEYWORDS([misc]) +AT_DATA([caller.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC 9 EXTERNAL. + PROCEDURE DIVISION. + MOVE 0 TO STOPPER + CALL "callee" END-CALL. + GOBACK. +]) +AT_DATA([callee.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee IS INITIAL. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC 9 EXTERNAL. + PROCEDURE DIVISION. + IF STOPPER = 1 + DISPLAY 'INITIAL prog was called RECURSIVE' + END-DISPLAY + * Following statement not ISO, corrected below + * STOP RUN RETURNING 1 + MOVE 1 TO RETURN-CODE + STOP RUN + ELSE + MOVE 1 TO STOPPER + CALL "callee2" END-CALL + END-IF. + GOBACK. +]) +AT_DATA([callee2.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee2. + PROCEDURE DIVISION. + CALL "callee" END-CALL. + GOBACK. +]) +AT_CHECK([$COMPILE -c callee.cob], [0], [], []) +AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) +AT_CHECK([$COMPILE -o caller caller.cob callee.o callee2.o], [0], [], []) +AT_CHECK([./caller], [1], [INITIAL prog was called RECURSIVE +], []) +AT_CLEANUP + + +AT_SETUP([Recursive CALL with RECURSIVE assumed]) +AT_KEYWORDS([misc]) +AT_DATA([caller.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC 9 EXTERNAL. + PROCEDURE DIVISION. + MOVE 0 TO STOPPER + CALL "callee" END-CALL. + GOBACK. +]) +AT_DATA([callee.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee IS INITIAL. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 STOPPER PIC 9 EXTERNAL. + PROCEDURE DIVISION. + IF STOPPER = 8 + DISPLAY 'OK' NO ADVANCING END-DISPLAY. + IF STOPPER NOT = 9 + ADD 1 TO STOPPER END-ADD + CALL "callee2" END-CALL. + GOBACK. +]) +AT_DATA([callee2.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee2. + PROCEDURE DIVISION. + CALL "callee" END-CALL. + GOBACK. +]) +AT_CHECK([$COMPILE -c callee.cob], [0], [], []) +AT_CHECK([$COMPILE -c callee2.cob], [0], [], []) +AT_CHECK([$COMPILE -o caller caller.cob callee.o callee2.o], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./caller], [0], [OK], []) +AT_CLEANUP + + +AT_SETUP([PERFORM inline (1)]) +AT_KEYWORDS([misc]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 INDVAL PIC 9(4). + PROCEDURE DIVISION. + PERFORM VARYING INDVAL FROM 1 + BY 1 UNTIL INDVAL > 2 + CONTINUE + END-PERFORM + IF INDVAL NOT = 3 + DISPLAY INDVAL NO ADVANCING + END-DISPLAY + END-IF + STOP RUN + . +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([PERFORM inline (2)]) +AT_KEYWORDS([misc]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 INDVAL PIC 9(4). + PROCEDURE DIVISION. + PERFORM VARYING INDVAL FROM 1 + BY 1 UNTIL INDVAL > 2 + CONTINUE + END-PERFORM + IF INDVAL NOT = 3 + DISPLAY INDVAL NO ADVANCING + END-DISPLAY + END-IF + . +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([UNSTRING DELIMITER IN]) +AT_KEYWORDS([misc]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 WK-CMD PIC X(8) VALUE "WWADDBCC". + 01 WK-SIGNS PIC XX VALUE "AB". + 01 WKS REDEFINES WK-SIGNS. + 03 WK-SIGN PIC X OCCURS 2. + 01 . + 02 WK-DELIM PIC X OCCURS 2. + 01 . + 02 WK-DATA PIC X(2) OCCURS 3. + PROCEDURE DIVISION. + UNSTRING WK-CMD DELIMITED BY WK-SIGN(1) OR WK-SIGN(2) + INTO WK-DATA(1) DELIMITER IN WK-DELIM(1) + WK-DATA(2) DELIMITER IN WK-DELIM(2) + WK-DATA(3) + END-UNSTRING + IF WK-DATA(1) NOT = "WW" + OR WK-DATA(2) NOT = "DD" + OR WK-DATA(3) NOT = "CC" + OR WK-DELIM(1) NOT = "A" + OR WK-DELIM(2) NOT = "B" + DISPLAY """" WK-DATA(1) + WK-DATA(2) + WK-DATA(3) + WK-DELIM(1) + WK-DELIM(2) """" + END-DISPLAY + END-IF. + STOP RUN. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([PERFORM type OSVS]) +AT_KEYWORDS([misc]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 MYOCC PIC 9(8) COMP VALUE 0. + PROCEDURE DIVISION. + ASTART SECTION. + A01. + PERFORM BTEST. + IF MYOCC NOT = 2 + DISPLAY MYOCC + END-DISPLAY + END-IF. + STOP RUN. + BTEST SECTION. + B01. + PERFORM B02 VARYING MYOCC FROM 1 BY 1 + UNTIL MYOCC > 5. + GO TO B99. + B02. + IF MYOCC > 1 + GO TO B99 + END-IF. + B99. + EXIT. +]) +AT_CHECK([$COMPILE prog.cob], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./a.out], [0], [], []) +AT_CLEANUP + + +AT_SETUP([Sticky LINKAGE]) +AT_KEYWORDS([misc]) +AT_DATA([callee.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. callee. + DATA DIVISION. + LINKAGE SECTION. + 01 P1 PIC X. + 01 P2 PIC X(6). + 01 P3 PIC X(6). + PROCEDURE DIVISION USING P1 P2. + IF P1 = "A" + SET ADDRESS OF P3 TO ADDRESS OF P2 + ELSE + IF P3 NOT = "OKOKOK" + DISPLAY P3 + END-DISPLAY + END-IF + END-IF. + EXIT PROGRAM. +]) +AT_DATA([caller.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 P1 PIC X VALUE "A". + 01 P2 PIC X(6) VALUE "NOT OK". + PROCEDURE DIVISION. + CALL "callee" USING P1 P2 + END-CALL. + MOVE "B" TO P1. + MOVE "OKOKOK" TO P2. + CALL "callee" USING P1 + END-CALL. + STOP RUN. +]) +AT_CHECK([$COMPILE -c callee.cob], [0], [], []) +AT_CHECK([$COMPILE -o caller caller.cob callee.o], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./caller], [0], [], []) +AT_CLEANUP diff --git a/gcc/cobol/UAT/testsuite.src/syn_copy.at b/gcc/cobol/UAT/testsuite.src/syn_copy.at index 81b913fce99b7b2988f418c1209157bbb1f6ee8b..c43387d4580f80ea9d5f734273f5bc456f9ac840 100644 --- a/gcc/cobol/UAT/testsuite.src/syn_copy.at +++ b/gcc/cobol/UAT/testsuite.src/syn_copy.at @@ -287,10 +287,6 @@ cobol1: 3 1 copy2.CPY cobol1: 2 1 copy3.CPY cobol1: 1 1 ./copy1.CPY copy1.CPY:1: recursive copybook: 'copy2.CPY' includes itself detected at end of file -copy1.CPY:3: error: could not open copybook file for 'copy2' -copy1.CPY:3: >>CDF parser failed -copy1.CPY:3: syntax error -.:9: 4 errors in DATA DIVISION, compilation ceases at 'PROCEDURE DIVISION' cobol1: error: failed compiling prog.cob ]) AT_CLEANUP diff --git a/gcc/cobol/UAT/testsuite.src/syn_definition.at b/gcc/cobol/UAT/testsuite.src/syn_definition.at index a8b97b2a996f5f8ccc146c651cdd0a070ac8a6e0..37812ea0998892698f71acd412645f73b1e8bffb 100644 --- a/gcc/cobol/UAT/testsuite.src/syn_definition.at +++ b/gcc/cobol/UAT/testsuite.src/syn_definition.at @@ -98,6 +98,27 @@ AT_CHECK([$COMPILE_ONLY SHORT.cob], [0], [], []) AT_CHECK([./prog], [0], [], []) AT_CLEANUP +AT_SETUP([INITIAL / RECURSIVE before COMMON]) +AT_KEYWORDS([PROGRAM-ID definition]) +AT_DATA([containing-prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. containing-prog. + PROCEDURE DIVISION. + IDENTIFICATION DIVISION. + PROGRAM-ID. prog-1 IS INITIAL COMMON. + PROCEDURE DIVISION. + STOP RUN. + END PROGRAM prog-1. + + IDENTIFICATION DIVISION. + PROGRAM-ID. prog-2 IS RECURSIVE COMMON. + PROCEDURE DIVISION. + STOP RUN. + END PROGRAM prog-2. +]) +AT_CHECK([$COMPILE_ONLY containing-prog.cob], [0], [], []) +AT_CLEANUP + AT_SETUP([Invalid PROGRAM-ID type clause (1)]) AT_KEYWORDS([definition]) @@ -111,7 +132,6 @@ AT_DATA([prog.cob], [ AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], [prog.cob:3: error: COMMON may be used only in a contained program at 'COMMON' -prog.cob:4: 1 errors in DATA DIVISION, compilation ceases at 'PROCEDURE DIVISION' cobol1: error: failed compiling prog.cob ]) @@ -665,6 +685,28 @@ badprog.cob:12: error: 77 OUTPUT-NAME SAME AS MESSAGE-TEXT-2: must be elementary .:13: 5 errors in DATA DIVISION, compilation ceases detected at end of file cobol1: error: failed compiling badprog.cob ]) +AT_CLEANUP +AT_SETUP([ALPHABET definition]) +AT_KEYWORDS([definition]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + SPECIAL-NAMES. + ALPHABET TESTME IS + 'A' THROUGH 'Z', x'00' thru x'05'; + x'41' ALSO x'42', ALSO x'00', x'C1' ALSO x'C2'. + ALPHABET FINE + 'A' also 'B' also 'C' also 'd' also 'e' ALSO 'f', + 'g' also 'G', '1' thru '9', x'00'. +]) +AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], +[prog.cob:9: error: ALPHABET , character 'A' (x'41') in position 32 already defined at position 0 at 'ALSO' +prog.cob:10: 1 errors in DATA DIVISION, compilation ceases at 'ALPHABET' +cobol1: error: failed compiling prog.cob +]) AT_CLEANUP + diff --git a/gcc/cobol/UAT/testsuite.src/syn_misc.at b/gcc/cobol/UAT/testsuite.src/syn_misc.at index 564e459cbcbaa2d5cc9222b20984e703a00bc852..2901cc0580e49db156ddc72664eec3bb936f65e7 100644 --- a/gcc/cobol/UAT/testsuite.src/syn_misc.at +++ b/gcc/cobol/UAT/testsuite.src/syn_misc.at @@ -477,12 +477,12 @@ AT_DATA([prog2.cob], [ . ]) AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], -[prog.cob:7: ANY LENGTH valid only for 01 in LIKAGE SECTION of a contained program at 'LENGTH' +[prog.cob:7: ANY LENGTH valid only for 01 in LINKAGE SECTION of a contained program at 'LENGTH' prog.cob:9: 1 errors in DATA DIVISION, compilation ceases at 'PROCEDURE DIVISION' cobol1: error: failed compiling prog.cob ]) AT_CHECK([$COMPILE_ONLY prog2.cob], [1], [], -[prog2.cob:7: ANY LENGTH valid only for 01 in LIKAGE SECTION of a contained program at 'LENGTH' +[prog2.cob:7: ANY LENGTH valid only for 01 in LINKAGE SECTION of a contained program at 'LENGTH' prog2.cob:9: 1 errors in DATA DIVISION, compilation ceases at 'PROCEDURE DIVISION' cobol1: error: failed compiling prog2.cob ]) @@ -505,7 +505,7 @@ AT_DATA([prog.cob], [ ]) AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], -[prog.cob:7: ANY LENGTH valid only for 01 in LIKAGE SECTION of a contained program at 'LENGTH' +[prog.cob:7: ANY LENGTH valid only for 01 in LINKAGE SECTION of a contained program at 'LENGTH' prog.cob:9: 1 errors in DATA DIVISION, compilation ceases at 'PROCEDURE DIVISION' cobol1: error: failed compiling prog.cob ]) @@ -841,3 +841,16 @@ cobol1: error: failed compiling prog.cob ]) AT_CLEANUP +AT_SETUP([swapped SOURCE- and OBJECT-COMPUTER]) +AT_KEYWORDS([misc extensions]) +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + OBJECT-COMPUTER. a. + SOURCE-COMPUTER. b. +]) +AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], []) +AT_CLEANUP diff --git a/gcc/cobol/UAT/testsuite.src/syn_move.at b/gcc/cobol/UAT/testsuite.src/syn_move.at index 7bd6a7815f64d2bb2cfd63f3165d522bf871d7b9..962bddfc2ceeb134b39550ea881e88d49dec35ae 100644 --- a/gcc/cobol/UAT/testsuite.src/syn_move.at +++ b/gcc/cobol/UAT/testsuite.src/syn_move.at @@ -1038,6 +1038,7 @@ AT_DATA([prog.cob], [ STOP RUN. ]) + AT_CHECK([$COMPILE_ONLY -dialect mf prog.cob], [1], [], [prog.cob:15: syntax error: symbol 'MAIN' not found prog.cob:15: error: invalid MOVE receiving operand @@ -1053,6 +1054,33 @@ cobol1: error: failed compiling prog.cob AT_CLEANUP +AT_SETUP([invalid source for MOVE (2)]) +AT_KEYWORDS([move label program-prototype]) +# cobc is wrong: repo-prog is an error, not warning. It must have +# been previously defined, or exist as a program-prototype (which we +# don't support). gcobol stops compiling instead of continuing on to +# identify the MOVE errors. +AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + PROGRAM repo-prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 77 MAIN-VAR PIC X(3). + PROCEDURE DIVISION. + MAIN. + MOVE MAIN TO MAIN-VAR. + MOVE repo-prog TO MAIN. + STOP RUN. +]) +AT_CHECK([$COMPILE_ONLY prog.cob], [1], [], +[prog.cob:6: error: 'repo-prog' does not name an earlier program +cobol1: error: failed compiling prog.cob +]) +AT_CLEANUP + AT_SETUP([SET error]) AT_KEYWORDS([move SET-MOVE]) AT_DATA([prog.cob], [ diff --git a/gcc/cobol/UAT/testsuite.src/syn_occurs.at b/gcc/cobol/UAT/testsuite.src/syn_occurs.at index 729bcb0bf2cbdead353ece9266a3e4e1890ea45d..33842c9480b65e9fb09438285877faeadc2a1cb9 100644 --- a/gcc/cobol/UAT/testsuite.src/syn_occurs.at +++ b/gcc/cobol/UAT/testsuite.src/syn_occurs.at @@ -21,3 +21,23 @@ ### ISO+IEC+1989-2002 13.16.36 OCCURS clause ### ISO+IEC+1989-202x 3rd WD 13.18.38 OCCURS clause +AT_SETUP([Nested OCCURS clause]) +AT_KEYWORDS([occurs]) +# NOT IMPLEMENTED: Diagnostic messages +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 G-1. + 02 G-2 OCCURS 2. + 03 G-3 OCCURS 2. + 04 G-4 OCCURS 2. + 05 G-5 OCCURS 2. + 06 G-6 OCCURS 2. + 07 G-7 OCCURS 2. + 08 G-8 OCCURS 2. + 09 X PIC X. +]) +AT_CHECK([$COMPILE_ONLY prog.cob], [0], [], []) +AT_CLEANUP diff --git a/gcc/cobol/cdf.y b/gcc/cobol/cdf.y index 9995f806a30833385670db2cda138a9d3b846b30..428ed9d59f7e24b0c45ace22b4096e4052947cf8 100644 --- a/gcc/cobol/cdf.y +++ b/gcc/cobol/cdf.y @@ -259,39 +259,39 @@ apply_cdf_turn( exception_turns_t& turns ) { %type <file> filename %type <files> filenames -%token BY 467 -%token COPY 356 -%token CDF_DISPLAY 378 -%token IN 578 +%token BY 470 +%token COPY 358 +%token CDF_DISPLAY 380 +%token IN 583 %token NAME 286 %token NUMSTR 304 -%token OF 650 -%token PSEUDOTEXT 686 -%token REPLACING 708 +%token OF 662 +%token PSEUDOTEXT 698 +%token REPLACING 720 %token LITERAL 297 -%token SUPPRESS 373 +%token SUPPRESS 375 -%token LSUB 361 SUBSCRIPT 372 RSUB 366 +%token LSUB 363 SUBSCRIPT 374 RSUB 368 -%token CDF_DEFINE 377 -%token CDF_IF 379 CDF_ELSE 380 CDF_END_IF 381 -%token CDF_EVALUATE 382 CDF_WHEN 383 CDF_END_EVALUATE 384 +%token CDF_DEFINE 379 +%token CDF_IF 381 CDF_ELSE 382 CDF_END_IF 383 +%token CDF_EVALUATE 384 CDF_WHEN 385 CDF_END_EVALUATE 386 -%token AS 451 CONSTANT 355 DEFINED 357 +%token AS 454 CONSTANT 357 DEFINED 359 %type <boolean> DEFINED -%token OTHER 662 PARAMETER_kw 362 OFF 651 OVERRIDE 363 -%token THRU 897 -%token TRUE_kw 772 +%token OTHER 674 PARAMETER_kw 364 OFF 663 OVERRIDE 365 +%token THRU 910 +%token TRUE_kw 785 -%token TURN 774 CHECKING 475 LOCATION 614 ON 653 WITH 798 +%token TURN 787 CHECKING 478 LOCATION 626 ON 665 WITH 811 -%left OR 898 -%left AND 899 -%right NOT 900 -%left '<' '>' '=' NE 901 LE 902 GE 903 +%left OR 911 +%left AND 912 +%right NOT 913 +%left '<' '>' '=' NE 914 LE 915 GE 916 %left '-' '+' %left '*' '/' -%right NEG 904 +%right NEG 917 %define api.prefix {ydf} %define api.token.prefix{YDF_} diff --git a/gcc/cobol/cdf_text.h b/gcc/cobol/cdf_text.h index 396fc44cd23a71bd76da25abf32eae2807dbcfaa..8c86bc770db7d034d01462523099a9d0904850dc 100644 --- a/gcc/cobol/cdf_text.h +++ b/gcc/cobol/cdf_text.h @@ -28,7 +28,51 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +static const char * +find_filter( const char filter[] ) { + + if( 0 == access(filter, X_OK) ) { + return filter; + } + + const char *path = getenv("PATH"); + if( ! path ) return NULL; + char *p = strdup(path), *eopath = p + strlen(p); + + while( *p != '\0' ) { + auto pend = std::find( p, eopath, ':' ); + if( *pend == ':' ) *pend++ = '\0'; + + static char name[PATH_MAX]; + + snprintf( name, sizeof(name), "%s/%s", p, filter ); + + if( 0 == access(name, X_OK) ) { + return name; + } + p = pend; + } + return NULL; +} + +bool verbose_file_reader = false; +static std::list<char *> preprocessor_filters; + #include "lexio.h" + +#include <sys/types.h> +#include <sys/wait.h> + +bool preprocess_filter_add( const char filter[] ) { + auto filename = find_filter(filter); + if( !filename ) { + warnx("error: preprocessor '%s/%s' not found", getcwd(NULL, 0), filter); + return false; + } + preprocessor_filters.push_back( strdup(filename) ); + return true; +} + FILE * cdftext::lex_open( const char filename[] ) { int input = open_input( filename ); @@ -39,6 +83,29 @@ cdftext::lex_open( const char filename[] ) { cobol_filename(filename); process_file( mfile, output ); + for( auto filter : preprocessor_filters ) { + input = output; + output = open_output(); + + pid_t pid = fork(); + + switch(pid){ + case -1: err(EXIT_FAILURE, "%s", __func__); + case 0: // child + if( -1 == dup2(input, STDIN_FILENO) ) { + errx(EXIT_FAILURE, "%s: could not dup input", __func__); + } + if( -1 == dup2(output, STDOUT_FILENO) ) { + errx(EXIT_FAILURE, "%s: could not dup output", __func__); + } + _exit( execl( filter, filter, "/dev/stdin", NULL ) ); + } + int status; + if( pid != wait(&status) ) { + err(EXIT_FAILURE, "error: %s failed with exit status %d", filter, status); + } + } + return fdopen( output, "r"); } @@ -49,6 +116,12 @@ cdftext::open_input( const char filename[] ) { if( fd == -1 ) { if( yydebug ) warn( "error: could not open '%s'", filename ); } + + verbose_file_reader = NULL != getenv("GCOBOL_TEMPDIR"); + + if( verbose_file_reader ) { + warnx("verbose: opening %s for input", filename); + } return fd; } @@ -58,7 +131,7 @@ cdftext::open_output() { char *name = getenv("GCOBOL_TEMPDIR"); int fd; - if( name ) { + if( name && 0 != strcmp(name, "/") ) { sprintf(stem, "%sXXXXXX", name); if( -1 == (fd = mkstemp(stem)) ) { err(EXIT_FAILURE, @@ -151,6 +224,10 @@ cdftext::free_form_reference_format( int input ) { char *indcol = indicated(mfile.cur, mfile.eol); // true only for fixed format + if( is_fixed_format() && !indcol ) { // short line + erase_source(mfile.cur, mfile.eol); + } + if( indcol ) { // Set to blank columns 1-6 and anything past the right margin. erase_source(mfile.cur, indcol); diff --git a/gcc/cobol/cobol1.cc b/gcc/cobol/cobol1.cc index 452d29d490a94b10907dd1958cfdbb4289c5f5f0..4cc4f3c9e7e4253c549526b0e056063d5b79babc 100644 --- a/gcc/cobol/cobol1.cc +++ b/gcc/cobol/cobol1.cc @@ -155,6 +155,8 @@ void parser_internal_is_ebcdic(bool is_ebcdic); bool use_static_call( bool yn ); void add_cobol_exception( int ); +bool preprocess_filter_add( const char filter[] ); + bool max_errors_exceeded( int nerr ) { return flag_max_errors != 0 && flag_max_errors <= nerr; } @@ -251,6 +253,10 @@ cobol_langhook_handle_option (size_t scode, } return true; + case OPT_preprocess: + preprocess_filter_add(arg); + return true; + case OPT_main: // This isn't right. All OPT_main should be replaced error("We should never see a non-equal dash-main in cobol1.c"); diff --git a/gcc/cobol/failures/playpen/input.txt b/gcc/cobol/failures/playpen/input.txt index 6e4186aca58aa94ddf74133dabe2a63b25e333bd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/gcc/cobol/failures/playpen/input.txt +++ b/gcc/cobol/failures/playpen/input.txt @@ -1,16 +0,0 @@ -Iowa -100000 -Georgia -FL -DE -Missouri -Indiana -1500000 -nh -10000000 -New Hampshirex -Phoenixx -Albanyx -Salemx -MX -North Dakotax diff --git a/gcc/cobol/failures/playpen/playpen.cbl b/gcc/cobol/failures/playpen/playpen.cbl index f5c2b8f4289b8bb4231cd18f2a7493602ab68483..b7539b01fb76d903a1c39aa5826b940c77ce49ce 100644 --- a/gcc/cobol/failures/playpen/playpen.cbl +++ b/gcc/cobol/failures/playpen/playpen.cbl @@ -1,34 +1,17 @@ - IDENTIFICATION DIVISION. - PROGRAM-ID. caller. - DATA DIVISION. - WORKING-STORAGE SECTION. - 01 P1 PIC X VALUE "A". - 01 P2 PIC X(6) VALUE "NOT OK". - PROCEDURE DIVISION. - CALL "callee" USING P1 P2 - END-CALL. - MOVE "B" TO P1. - MOVE "OKOKOK" TO P2. - CALL "callee" USING P1 - END-CALL. - STOP RUN. - END PROGRAM caller. - - IDENTIFICATION DIVISION. - PROGRAM-ID. callee. - DATA DIVISION. - LINKAGE SECTION. - 01 P1 PIC X. - 01 P2 PIC X(6). - 01 P3 PIC X(6). - PROCEDURE DIVISION USING P1 P2. - IF P1 = "A" - SET ADDRESS OF P3 TO ADDRESS OF P2 - ELSE - IF P3 NOT = "OKOKOK" - DISPLAY P3 - END-DISPLAY - END-IF - END-IF. - EXIT PROGRAM. - END PROGRAM callee. \ No newline at end of file + identification division. + program-id. prog. + data division. + working-storage section. + 01 n4 pic s9(8) comp-5 value 0. + 01 p4 redefines n4 pointer. + 01 n8 pic s9(16) comp-5 value 0. + 01 p8 redefines n8 pointer. + procedure division. + move -1 to n8 + set p4 to p8 + display "P4 and P8 before: " p4 space p8 + display "Increment N4 and N8" + add 1 to n4 n8 + display "P4 and P8 after: " p4 space p8 + goback. + end program prog. diff --git a/gcc/cobol/failures/recursive_function/Makefile b/gcc/cobol/failures/recursive_function/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f77e46b3451abf45cb70ed9dc161be56b3b063c7 --- /dev/null +++ b/gcc/cobol/failures/recursive_function/Makefile @@ -0,0 +1 @@ +include ../Makefile.inc diff --git a/gcc/cobol/failures/playpen/known-good.txt b/gcc/cobol/failures/recursive_function/input.txt similarity index 100% rename from gcc/cobol/failures/playpen/known-good.txt rename to gcc/cobol/failures/recursive_function/input.txt diff --git a/gcc/cobol/failures/recursive_function/playpen.cbl b/gcc/cobol/failures/recursive_function/playpen.cbl new file mode 100644 index 0000000000000000000000000000000000000000..80755fc361bdbd4613c7a34de9eb99cd5d458656 --- /dev/null +++ b/gcc/cobol/failures/recursive_function/playpen.cbl @@ -0,0 +1,37 @@ + IDENTIFICATION DIVISION. + FUNCTION-ID. callee. + DATA DIVISION. + LOCAL-STORAGE SECTION. + 01 LCL-X PIC 999 . + LINKAGE SECTION. + 01 parm PIC 999. + 01 retval PIC 999. + PROCEDURE DIVISION USING parm RETURNING retval. + display "On entry, parm is: " parm + move parm to lcl-x + move parm to retval + subtract 1 from parm + if parm > 0 + display "A The function returns " function callee(parm). + if lcl-x not equal to retval + display "On exit, lcl-s and retval are: " lcl-x " and " retval + display "But they should be equal to each other" + end-if + goback. + end function callee. + + IDENTIFICATION DIVISION. + PROGRAM-ID. caller. + ENVIRONMENT DIVISION. + CONFIGURATION SECTION. + REPOSITORY. + FUNCTION callee. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 val PIC 999 VALUE 5. + PROCEDURE DIVISION. + DISPLAY "Starting value is: " val + display "B The function returns " function callee(val). + STOP RUN. + end program caller. + diff --git a/gcc/cobol/gcobol.1 b/gcc/cobol/gcobol.1 index ed930fc5e0362493314a88f61af7e60d6b4c7e69..4d16622bca071bafd2f717971d6d74279041cde5 100644 --- a/gcc/cobol/gcobol.1 +++ b/gcc/cobol/gcobol.1 @@ -19,6 +19,7 @@ .Op Fl findicator-column .Op Fl finternal-ebcdic .Op Fl dialect Ar dialect-name +.Op Fl preprocess Ar preprocess-filter .Op Fl fflex-debug .Op Fl fyacc-debug .Ar filename Op ... @@ -174,13 +175,17 @@ With .Fl fno-static-call , .Nm never uses static linking for -.D1 Sy CALL Ar program Ns . +.D1 Sy CALL Ar program By default, or with .Fl fstatic-call , +if +.Ar program +is an alphanumeric literal, .Nm -uses static linkage if +uses static linkage, meaning the compiler produces an external symbol .Ar program -is an alphanumeric literal. (In the future, for +for the linker to resolve. +(In the future, that will work with .Sy CONSTANT data items, too.) With static linkage, if .Ar program @@ -228,6 +233,25 @@ Only a few such non-standard constructs are accepted, and makes no claim or aspiration 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 preprocess Ar preprocess-filter +After all CDF text-manipulation has been applied, and before the +prepared \*[lang] is sent to the cobol1 compiler, the input may be +further altered by one or more filters. In the tradition of +.Xr sed 1 , +each +.Ar preprocess-filter +reads from standard input and writes to standard output. +No options or arguments are supported for +.Ar preprocess-filter . +.Nm +searches the current working directory and the PATH environment +variable directories for the existence of an executable file whose +name matches +.Ar preprocess-filter . +The first one found is used. If none is found, 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 @@ -267,6 +291,26 @@ At the present time, this is an all-or-nothing setting. Support for and .Sy CODESET , which would allow conversion between encodings, remains a future goal. +.Ss REDEFINES ... USAGE POINTER +Per ISO, an item that +.Sy REDEFINES +another may not be larger than the item it redefines, unless that item +has LEVEL 01 and is not EXTERNAL. In +.Nm , +using +.Fl dialect Ar ibm , +this rule is relaxed for +.Sy REDEFINES +with +.Sy USAGE POINTER +whose redefined member is a 4-byte +.Sy USAGE COMP-5 +(usually +.Sy PIC S9(8) Ns ). +In that case, the redefined member is re-sized to be 8 bytes, to +accommodate the pointer. This feature allows pointer arithmetic on a +64-bit system with source code targeted at a 32-bit system. +.Sy . .Sh IMPLEMENTATION NOTES .Nm @@ -277,20 +321,22 @@ specification, any such conflicts are resolved in favor of gcc. .Ss Linking Unlike, C, the \*[lang] .Sy CALL -statement implies dynamic linking, because the -.Sy CALL -parameter can be a variable whose value is determined at runtime. +statement implies dynamic linking, because for +.D1 Sy CALL Ar program +.Ar program +can be a variable whose value is determined at runtime. However, the parameter may also be compile-time constant, either an alphanumeric literal, or a .Sy CONSTANT data item. .Pp .Nm -supports static linking where possible. If the parameter value is -known at compile time, the compiler produces an external reference to -be resolved by the linker. The referenced program is normally supplied -via an object module, a static library, or a shared object. If it is -not supplied, the linker will report an +supports static linking where possible, unless defeated by +.Fl no-static-call . +If the parameter value is known at compile time, the compiler produces +an external reference to be resolved by the linker. The referenced +program is normally supplied via an object module, a static library, +or a shared object. If it is not supplied, the linker will report an .Dq "unresolved symbol" error, either at build time or, if using a shared object, when the program is executed. This feature informs the programmer of the error @@ -300,6 +346,16 @@ Programs that are expected to execute correctly in the presence of an unresolved symbol (perhaps because the program logic won't require that particular .Sy CALL ) +can use the +.Fl no-static-call +option. That forces all +.Sy CALL +statements to be resolved dynamically, at runtime. +.ig +Programs that are expected to execute +correctly in the presence of an unresolved symbol (perhaps because the +program logic won't require that particular +.Sy CALL ) can use linker options to produce an executable anyway. .Pp One corner case yet remains. The @@ -333,7 +389,8 @@ Should your program meet those particular conditions, all is not lost. There are workarounds, and an option could be added to use dynamic linking for all .Sy CALL -statement, regardless of compile-time constants. +statement, regardless of compile-time constants. +.. . .Sh EXTENSIONS TO ISO \*[lang] Standard \*[lang] has no provision for environment variables as defined @@ -671,7 +728,7 @@ statement, with or without its .Sy REPLACING component. For any statement .sp -.D1 "COPY copybook" +.D1 COPY Ar copybook .sp .Nm looks first for an environment variable named @@ -875,7 +932,8 @@ through where 0-7 indicates a bit position. The value of the UPSI switches is taken from the .Ev UPSI -environment variable, whose value is a string of up to eight 1's and 0's. The first character represents the value of +environment variable, whose value is a string of up to eight 1's and +0's. The first character represents the value of .Sy UPSI-0 , and missing values are assigned 0. For example, .Sy UPSI=1000011 @@ -885,6 +943,15 @@ in the environment sets bits 0, 5, and 6 on, which means that and .Sy UPSI-6 are on. +.It Ev GCOBOL_TEMPDIR +causes any temporary files created during CDF processing to be written +to a file whose name is specified in the value of +.Ev GCOBOL_TEMPDIR . +If the value is just +.Dq / , +the effect is different: each copybook read is reported on standard +error. This feature is meant to help diagnose mysterious copybook +errors. .El . .Sh FILES diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc index 3354e1f9125363c4dbb7873939c35c63d53e652e..fe71f9b6baa5e734365323a0d6b59577f9789028 100644 --- a/gcc/cobol/genapi.cc +++ b/gcc/cobol/genapi.cc @@ -127,7 +127,7 @@ static bool auto_advance_is_AFTER_advancing = 0; casts. For example, main() returns an INT, as do functions that return the default RETURN-CODE will have */ -#define COBOL_FUNCTION_RETURN_TYPE SIZE_T +#define COBOL_FUNCTION_RETURN_TYPE SSIZE_T #define MAX_AFTERS 8 @@ -234,7 +234,6 @@ build_main_that_calls_something(const char *something) TRACE1_END } - //gg_call(VOID, "__gg__pop_jmp_buf", 0); gg_return(gg_cast(INT, gg_call_expr( COBOL_FUNCTION_RETURN_TYPE, cobol_name_mangler(something), 0))); @@ -695,7 +694,8 @@ parser_call_target_update( size_t caller, static tree function_handle_from_name(cbl_refer_t &name, - size_t narg) + size_t narg, + tree function_return_type) { // We need to set up for narg parameters: tree *arg_types = (tree *)xmalloc(narg * sizeof(tree)); @@ -703,7 +703,7 @@ function_handle_from_name(cbl_refer_t &name, { arg_types[i] = VOID_P; }; - tree function_type = build_varargs_function_type_array (COBOL_FUNCTION_RETURN_TYPE, + tree function_type = build_varargs_function_type_array (function_return_type, narg, arg_types); tree function_pointer = build_pointer_type(function_type); @@ -717,7 +717,7 @@ function_handle_from_name(cbl_refer_t &name, tree unmangled_name = gg_define_char_star(); tree mangled_name = gg_define_char_star(); - gg_assign(unmangled_name, gg_call_expr( CHAR_P, + gg_assign(unmangled_name, gg_call_expr( CHAR_P, "__gg__name_not_mangled", 1, gg_get_address_of(name.refer_decl_node))); @@ -835,11 +835,11 @@ function_handle_from_name(cbl_refer_t &name, // If it's a literal, call the target literally using the // undecorated name. At END-PROGRAM, the parser will replace // in-scope plain names with mangled names. - if( use_static_call() && is_literal(name.field) ) + if( use_static_call() && is_literal(name.field) ) { // A literal name is always "found". We create a reference to // it, which is later resolved by the linker. - tree addr_expr = gg_get_function_address(SIZE_T, name.field->data.initial); + tree addr_expr = gg_get_function_address(function_return_type, name.field->data.initial); gg_assign(function_handle, addr_expr); if( DECL_P(function_handle) ) @@ -881,7 +881,7 @@ function_handle_from_name(cbl_refer_t &name, { // A literal name is always "found". We create a reference to // it, which is later resolved by the linker. - tree addr_expr = gg_get_function_address(SIZE_T, name.field->data.initial); + tree addr_expr = gg_get_function_address(function_return_type, name.field->data.initial); gg_assign(function_handle, addr_expr); if( DECL_P(function_handle) ) @@ -969,7 +969,7 @@ 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], 0); + tree function_handle = function_handle_from_name(progs[i], 0, COBOL_FUNCTION_RETURN_TYPE); gg_call(VOID, "__gg__to_be_canceled", 1, @@ -3018,6 +3018,7 @@ register_main_switch(const char *main_string) if( p ) { *p = '\0'; + main_string = p+1; } main_strings[mstr] = main_string; free(mstr); @@ -3233,7 +3234,8 @@ enter_program_common(const char *funcname, const char *funcname_) } void -parser_enter_program(const char *funcname_) +parser_enter_program( const char *funcname_, + bool is_function) // True for user-defined-function { // 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 @@ -3262,18 +3264,21 @@ parser_enter_program(const char *funcname_) SHOW_PARSE_END } - if( next_program_is_main ) + if( !is_function ) { - 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( next_program_is_main ) { - build_main_that_calls_something(funcname); + 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 + { + build_main_that_calls_something(funcname); + } } } @@ -3286,6 +3291,7 @@ parser_enter_program(const char *funcname_) } enter_program_common(funcname, funcname_); + current_function->is_function = is_function; TRACE1 { @@ -4722,44 +4728,92 @@ parser_move(size_t ntgt, cbl_refer_t *tgts, cbl_refer_t src, cbl_round_t rounded static tree -field_type_to_tree_type(cbl_field_t *field) +tree_type_from_field_type(cbl_field_t *field, size_t &nbytes) { - // This maps a Fldxxx to a C-style variable type: - switch(field->type) + /* This routine is used to determine what action is taken with type of a + CALL ... USING <var> and the matching PROCEDURE DIVISION USING <var> of + a PROGRAM-ID or FUNCTION-ID + */ + tree retval = COBOL_FUNCTION_RETURN_TYPE; + nbytes = 8; + if( field ) { - case FldGroup: - case FldAlphanumeric: - case FldAlphaEdited: - return CHAR_P; + // This maps a Fldxxx to a C-style variable type: + switch(field->type) + { + case FldGroup: + case FldAlphanumeric: + case FldAlphaEdited: + case FldNumericEdited: + retval = CHAR_P; + nbytes = field->data.capacity; + break; - case FldNumericEdited: - case FldNumericDisplay: - case FldNumericBinary: - case FldPacked: - case FldNumericBin5: - case FldIndex: - case FldPointer: - return SSIZE_T; + case FldNumericDisplay: + case FldNumericBinary: + case FldPacked: + if( field->data.digits > 18 ) + { + retval = UINT128; + nbytes = 16; + } + else + { + retval = SIZE_T; + nbytes = 8; + } + break; - case FldFloat: - return DOUBLE; + case FldNumericBin5: + case FldIndex: + case FldPointer: + if( field->data.capacity > 8 ) + { + retval = UINT128; + nbytes = 16; + } + else + { + retval = SIZE_T; + nbytes = 8; + } + break; - case FldInvalid: - case FldClass: - case FldConditional: - case FldForward: - warnx( "%s(): Invalid field type %s:", - __func__, - cbl_field_type_str(field->type)); - gcc_assert(false); - break; + case FldFloat: + if( field->data.capacity == 8 ) + { + retval = DOUBLE; + nbytes = 8; + } + else if( field->data.capacity == 4 ) + { + retval = FLOAT; + nbytes = 4; + } + else + { + retval = FLOAT128; + nbytes = 16; + } + break; - default: - warnx("%s(): Unknown field type %d:", __func__, field->type); - gcc_assert(false); - break; + default: + warnx( "%s(): Invalid field type %s:", + __func__, + cbl_field_type_str(field->type)); + gcc_assert(false); + break; + } } - return NULL_TREE; // This shuts the compiler up. + if( retval == SIZE_T && field->attr & signable_e ) + { + retval = SSIZE_T; + } + if( retval == UINT128 && field->attr & signable_e ) + { + retval = INT128; + } + return retval; } static void @@ -4832,82 +4886,75 @@ parser_exit(void) if( current_function->returning ) { - tree return_type = field_type_to_tree_type(current_function->returning); + cbl_field_type_t field_type = current_function->returning->type; + size_t nbytes = 0; + tree return_type = tree_type_from_field_type(current_function->returning, + nbytes); + tree retval = gg_define_variable(return_type); - if( return_type == CHAR_P ) - { - // When an external routine is called, the ->returning value has - // to be treated as if it were in the console codeset space + gg_modify_function_type(current_function->function_decl, + return_type); - gg_call(VOID, - "__gg__internal_to_console_in_place", - 2, - member(current_function->returning, "data"), - member(current_function->returning, "capacity")); - - //gg_call(VOID, "__gg__pop_jmp_buf", 0); - restore_local_variables(); - gg_return( gg_cast(COBOL_FUNCTION_RETURN_TYPE, - member(current_function->returning, "data"))); - } - else if( return_type == DOUBLE ) + if( is_numeric( field_type ) ) { - // We need to do the equivalent of - // return *(size_t *)&(double_thing) - // - // but I haven't yet investigated how to do that in GENERIC. - // - // By and by.... - tree dubble = gg_define_variable(DOUBLE); - if( current_function->returning->type == FldFloat - && current_function->returning->data.capacity == 4) + // The field being returned is numeric. + if( field_type == FldNumericBin5 + || field_type == FldFloat + || field_type == FldPointer + || field_type == FldIndex ) { - tree flote = gg_define_variable(FLOAT); - gg_memcpy( gg_get_address_of(flote), - member(current_function->returning, "data"), - member(current_function->returning, "capacity") ); - gg_assign(dubble, gg_cast(DOUBLE, flote)); - } - else if( current_function->returning->type == FldFloat - && current_function->returning->data.capacity == 8) - - { - gg_memcpy( gg_get_address_of(dubble), - member(current_function->returning, "data"), - member(current_function->returning, "capacity") ); + // These are easily handled because they are all little-endian. + gg_memcpy(gg_get_address_of(retval), + member(current_function->returning, "data"), + build_int_cst_type(SIZE_T, nbytes)); } else { - warnx("%s(): No use ramblin', walkin' in the shadows, trailin' a wandering star...", __func__); - gcc_assert(false); + // The field_type has a PICTURE string, so we need to convert from the + // COBOL form to little-endian binary: + tree rdigits = gg_define_int(); + tree value = gg_define_int128(); + get_binary_value( value, + rdigits, + NULL, + current_function->returning); +// gg_printf("KILROY returning %ld\n", gg_cast(LONG, value), NULL_TREE); + gg_memcpy(gg_get_address_of(retval), + gg_get_address_of(value), + build_int_cst_type(SIZE_T, nbytes)); } - tree retval = gg_define_size_t(); - gg_memcpy( gg_get_address_of(retval), - gg_get_address_of(dubble), - sizeof_size_t ); - //gg_call(VOID, "__gg__pop_jmp_buf", 0); - restore_local_variables(); - gg_return(retval); - } - else if( return_type == SSIZE_T) - { - tree rdigits = gg_define_int(); - tree value = gg_define_int128(); - get_binary_value( value, - rdigits, - NULL, - current_function->returning); - tree retval = gg_define_size_t(); - gg_assign(retval, gg_cast(SIZE_T, retval)); - //gg_call(VOID, "__gg__pop_jmp_buf", 0); restore_local_variables(); gg_return(retval); } else { - //gg_call(VOID, "__gg__pop_jmp_buf", 0); + // The RETURNING type is a group or alphanumeric + + // When an external routine is called, the ->returning value has + // to be treated as if it were in the console codeset space + gg_call(VOID, + "__gg__internal_to_console_in_place", + 2, + member(current_function->returning, "data"), + member(current_function->returning, "capacity")); + + // The byte array to be returned is in returning, which is a local + // variable on the stack. We need to make a copy of it to avoid the + // error of returning a pointer to data on the stack. + + tree array_type = build_array_type_nelts(UCHAR, + current_function->returning->data.capacity); + tree retval = gg_define_variable(array_type, + NULL, + vs_static); + gg_memcpy(gg_get_address_of(retval), + member(current_function->returning->var_decl_node, "data"), + member(current_function->returning->var_decl_node, "capacity")); + + tree actual = gg_cast(COBOL_FUNCTION_RETURN_TYPE, gg_get_address_of(retval)); + restore_local_variables(); - gg_return(gg_cast(COBOL_FUNCTION_RETURN_TYPE, integer_zero_node)); + gg_return(actual); } } else @@ -4932,8 +4979,6 @@ parser_exit(void) rdigits, NULL, our_return_code); - - //gg_call(VOID, "__gg__pop_jmp_buf", 0); restore_local_variables(); gg_return(gg_cast(COBOL_FUNCTION_RETURN_TYPE, value)); @@ -5336,21 +5381,19 @@ parser_division(cbl_division_t division, // RETURNING variables are supposed to be in the linkage section, which // means that we didn't assign any storage to them during // parser_symbol_add(). We do that here. + + // returning also needs to behave like local storage, even though it is + // in linkage. - if( returning && !returning->data_decl_node ) + // This counter is used to help keep track of local variables + gg_increment(var_decl_unique_prog_id); + if( returning ) { - char achDataName[256]; - sprintf(achDataName, "..vardata_%lu", sv_data_name_counter++); - - tree array_type = build_array_type_nelts(UCHAR, returning->data.capacity); - returning->data_decl_node = gg_define_variable( array_type, - achDataName, - vs_static); - gg_assign( member(returning->var_decl_node, "data"), - gg_get_address_of(returning->data_decl_node) ); + parser_local_add(returning); + current_function->returning = returning; } - - // Stash the returning variable for use during parser_return() + + // Stash the returning variables for use during parser_return() current_function->returning = returning; if( gg_trans_unit.function_stack.size() == 1 ) @@ -5379,7 +5422,22 @@ parser_division(cbl_division_t division, char ach[2*sizeof(cbl_name_t)]; sprintf(ach, "_p_%s", args[i].refer.field->name); - chain_parameter_to_function(current_function->function_decl, VOID_P, ach); + + 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); } if( nusing ) @@ -5411,6 +5469,21 @@ parser_division(cbl_division_t division, // 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", + 1, + gg_get_address_of(args[i].refer.field->var_decl_node)); + } + ELSE + ENDIF + tree base = gg_define_variable(UCHAR_P); gg_assign(rt_i, build_int_cst_type(INT, i)); IF( rt_i, lt_op , var_decl_call_parameter_count ) @@ -5447,12 +5520,59 @@ parser_division(cbl_division_t division, if( args[i].crv == by_value_e ) { - // 'parameter' is the 64-bit value that was placed on the stack + // 'parameter' is the 64-bit or 128-bit value that was placed on the stack + + cbl_field_t *new_var = args[i].refer.field; + + 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, parameter), + 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(parameter), + 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_stack); + 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", 2, - gg_get_address_of(args[i].refer.field->var_decl_node), - parameter); + gg_get_address_of(new_var->var_decl_node), + parm); } else { @@ -5470,9 +5590,6 @@ parser_division(cbl_division_t division, gg_assign(var_decl_call_parameter_count, build_int_cst_type(INT, A_ZILLION)); } - // This counter is used to help keep track of local variables - gg_increment(var_decl_unique_prog_id); - gg_call(VOID, "__gg__pseudo_return_bookmark", 0); @@ -7408,6 +7525,8 @@ parser_file_add(struct cbl_file_t *file) 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 ) { @@ -7496,6 +7615,7 @@ parser_file_open( struct cbl_file_t *file, int mode_char ) quoted_name = true; } + store_location_stuff("OPEN"); gg_call(VOID, "__gg__file_open", 4, @@ -7547,6 +7667,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 + store_location_stuff("CLOSE"); gg_call(VOID, "__gg__file_close", 2, @@ -7634,6 +7755,7 @@ parser_file_read( struct cbl_file_t *file, where = 1; } + store_location_stuff("READ"); gg_call(VOID, "__gg__file_read", 2, @@ -7769,6 +7891,7 @@ parser_file_write( cbl_file_t *file, gg_assign(location, member(record_area, "data")); gg_assign(length, member(record_area, "capacity")); + store_location_stuff("WRITE"); gg_call(VOID, "__gg__file_write", 6, @@ -7836,6 +7959,7 @@ parser_file_delete( struct cbl_file_t *file, bool /*sequentially*/ ) SHOW_PARSE_END } + store_location_stuff("DELETE"); gg_call(VOID, "__gg__file_delete", 2, @@ -7892,6 +8016,7 @@ parser_file_rewrite(cbl_file_t *file, tree length = gg_define_size_t(); gg_assign(length, member(record_area, "capacity")); + store_location_stuff("REWRITE"); gg_call(VOID, "__gg__file_rewrite", 3, @@ -7996,6 +8121,7 @@ parser_file_start(struct cbl_file_t *file, get_binary_value(length, rdigits, &length_ref); } + store_location_stuff("START"); gg_call(VOID, "__gg__file_start", 4, @@ -8346,265 +8472,110 @@ parser_inspect_conv(cbl_refer_t input, gg_get_address_of(before.identifier_4.refer_decl_node) ); } -#if 0 -static tree -ctype_to_tree(cbl_ctype_t type) - { - switch(type) - { - case c_bool: - return BOOL; - case c_char: - return CHAR; - case c_wchar: - return USHORT; - case c_byte: - return SCHAR; - case c_ubyte: - return UCHAR; - case c_short: - return SHORT; - case c_ushort: - return USHORT; - case c_int: - return INT; - case c_uint: - return UINT; - case c_long: - return LONG; - case c_ulong: - return ULONG; - case c_longlong: - return LONGLONG; - case c_ulonglong: - return ULONGLONG; - case c_size_t: - return SIZE_T; - case c_ssize_t: - return SSIZE_T; - case c_int128: - return INT128; - case c_float: - return FLOAT; - case c_double: - return DOUBLE; - case c_longdouble: - return LONGDOUBLE; - case c_nts: - case c_char_p: - return CHAR_P; - case c_wchar_p: - return WCHAR_P; - case c_void_p: - return VOID_P; - default: - break; +void +parser_intrinsic_numval_c( cbl_field_t *f, + cbl_refer_t& input, + bool locale, + cbl_refer_t& currency, + bool anycase, + bool test_numval_c ) // true for TEST-NUMVAL-C + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_END + } + TRACE1 + { + TRACE1_HEADER + TRACE1_END + } + refer_fill_source(input); + refer_fill_source(currency); + if( locale || anycase ) + { + gcc_assert(false); + } + if( test_numval_c ) + { + gg_call(INT, + "__gg__test_numval_c", + 3, + gg_get_address_of(f->var_decl_node), + gg_get_address_of(input .refer_decl_node), + gg_get_address_of(currency.refer_decl_node)); + } + else + { + gg_call(INT, + "__gg__numval_c", + 3, + gg_get_address_of(f->var_decl_node), + gg_get_address_of(input .refer_decl_node), + gg_get_address_of(currency.refer_decl_node)); } - warnx("%s(): Unhandled c_type %d", __func__, type); - gcc_assert(false); } -#endif - -#if 0 -static void -intrinsic_convert_return(cbl_ctype_t returned_type, cbl_field_t *dest, tree stmt) - { - tree intrinsic_return = gg_define_variable(ctype_to_tree(returned_type)); - gg_assign(intrinsic_return, gg_cast(ctype_to_tree(returned_type), stmt)); - - // All integer return values have to become INT128: - tree as_int128 = gg_define_int128(); - - // We now have the return value. We need to go through some gyrations - // to do the assignment. - - switch(returned_type) - { - case c_bool: - case c_char: - case c_wchar: - case c_byte: - case c_ubyte: - case c_short: - case c_ushort: - case c_int: - case c_uint: - case c_long: - case c_ulong: - case c_longlong: - case c_ulonglong: - case c_size_t: - case c_ssize_t: - case c_int128: - // Those all come back as a flavor of integer, which is in - //intrinsic_return - - gg_assign(as_int128, gg_cast(INT128, intrinsic_return)); - switch( dest->type ) - { - case FldGroup: - case FldAlphanumeric: - { - // We have to convert the binary value to a string - tree array = gg_define_array(CHAR, 128); - tree parray = gg_define_char_star(gg_get_address_of(array)); - - // Turn the integer value into a string: - gg_call(VOID, - "__gg__binary_to_string", - 3, - parray, - gg_cast(INT128, as_int128), - member(dest, "capacity")); - - // We need to terminate that string: - gg_assign( gg_array_value( parray, - member(dest, "capacity")), - integer_zero_node); - // Move those characters into the destination: - move_tree_to_field( dest, - parray); - } - break; - - case FldNumericDisplay: - case FldNumericEdited: - case FldNumericBinary: - case FldNumericBin5: - case FldIndex: - gg_call(VOID, - "__gg__int128_to_field", - 5, - gg_get_address_of(dest->var_decl_node), - as_int128, - integer_zero_node, - build_int_cst_type(INT, rounded_e), - null_pointer_node ); - // gg_call(VOID, - // "__gg__ascii_to_internal_field", - // 1, - // gg_get_address_of(dest->var_decl_node)); - break; - - case FldAlphaEdited: - { - // We have to convert the binary value to a string - tree array = gg_define_array(CHAR, 128); - tree parray = gg_define_char_star(gg_get_address_of(array)); - - // Turn the integer value into a string: - gg_call(VOID, - "__gg__binary_to_string", - 3, - parray, - as_int128, - member(dest, "capacity")); - - // We need to terminate that string: - gg_assign( gg_array_value( parray, - member(dest, "capacity")), - integer_zero_node); - // Move those characters into the destination: - gg_call(VOID, - "__gg__string_to_alpha_edited", - 4, - member(dest, "data"), - parray, - gg_strlen(parray), - member(dest, "picture")); - break; - } - - default: - break; - } - - break; - - case c_float: - // Those all come back as a floating-point number, which is in - // intrinsic_return. We have an interim routine that does a reasonable - // conversion of floating point numbers: - gg_call(VOID, - "__gg__float_to_target", - 3, - gg_get_address_of(dest->var_decl_node), - gg_cast(FLOAT, intrinsic_return), - build_int_cst_type(INT, rounded_e)); - break; - - case c_double: - // Those all come back as a floating-point number, which is in - // intrinsic_return. We have an interim routine that does a reasonable - // conversion of floating point numbers: - gg_call(VOID, - "__gg__double_to_target", - 3, - gg_get_address_of(dest->var_decl_node), - gg_cast(DOUBLE, intrinsic_return), - build_int_cst_type(INT, rounded_e)); - break; - - case c_longdouble: - // Those all come back as a floating-point number, which is in - // intrinsic_return. We have an interim routine that does a reasonable - // conversion of floating point numbers: - gg_call(VOID, - "__gg__long_double_to_target", - 3, - gg_get_address_of(dest->var_decl_node), - gg_cast(LONGDOUBLE, intrinsic_return), - build_int_cst_type(INT, rounded_e)); - break; - - case c_char_p: - // intrinsic_return is a char pointer. Assume he actually wants - // the string: - move_tree_to_field( dest, - intrinsic_return); - - // We have to assume that all char * pointers returned from the - // library were allocated in the library, and thus need - // to be sent to free(). - XXX; - gg_free(intrinsic_return); - break; - - case c_nts: - // We are playing with fire, a little bit. - - // This code is designed to handle things like FUNCTION TRIM(xxx). - // The results are being assigned to a intermediate variable that - // was allocated with the same number of bytes as XXX. TRIM() will - // return either the same length, or something smaller. So, we - // are going to reduce the destination capacity to be the length - // of the returned string. - - gcc_assert(dest->type == FldAlphanumeric); - gcc_assert(dest->attr & intermediate_e); +void +parser_intrinsic_subst( cbl_field_t *f, + cbl_refer_t& ref1, + size_t argc, + cbl_substitute_t * argv ) + { + SHOW_PARSE + { + SHOW_PARSE_HEADER + SHOW_PARSE_END + } + TRACE1 + { + TRACE1_HEADER + TRACE1_END + } + + 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)); - gg_assign( member(dest, "capacity"), - gg_strlen(intrinsic_return)); - move_tree_to_field( dest, - intrinsic_return); + refer_fill_source(ref1); + for(size_t i=0; i<argc; i++) + { + control_bytes[i] = (argv[i].anycase ? + substitute_anycase_e : 0) + + (argv[i].first_last == cbl_substitute_t::subst_first_e ? + substitute_first_e : 0) + + (argv[i].first_last == cbl_substitute_t::subst_last_e ? + substitute_last_e : 0); + refer_fill_source(argv[i].orig); + arg1[i] = argv[i].orig; + + refer_fill_source(argv[i].replacement); + arg2[i] = argv[i].replacement; + } + + tree control = gg_array_of_bytes(argc, control_bytes); + tree arg1t = build_array_of_cblc_refer(argc, arg1); + tree arg2t = build_array_of_cblc_refer(argc, arg2); - // We have to assume that all char * pointers returned from the - // library were allocated in the library, and thus need - // to be sent to free(). - XXX; - gg_free(intrinsic_return); - break; + gg_call(VOID, + "__gg__substitute", + 6, + gg_get_address_of(f->var_decl_node), + gg_get_address_of(ref1.refer_decl_node), + build_int_cst_type(SIZE_T, argc), + control, + arg1t, + arg2t); + + gg_free(arg2t); + gg_free(arg1t); + gg_free(control); - case c_wchar_p: - case c_void_p: - default: - warnx("%s(): We don't know what to do with c_type %d", __func__, returned_type); - gcc_assert(false); - break; - } + free(arg2); + free(arg1); + free(control_bytes); } -#endif void parser_intrinsic_callv( cbl_field_t *tgt, @@ -8646,6 +8617,7 @@ parser_intrinsic_callv( cbl_field_t *tgt, tree ncount = build_int_cst_type(SIZE_T, nrefs); tree args = build_array_of_cblc_refer(nrefs, refs); + store_location_stuff(function_name); gg_call(VOID, function_name, 3, @@ -8697,6 +8669,7 @@ parser_intrinsic_call_0(cbl_field_t *tgt, struct timespec tp; clock_gettime(CLOCK_REALTIME, &tp); // time_t tv_sec; long tv_nsec + store_location_stuff(function_name); gg_call(VOID, function_name, 3, @@ -8706,6 +8679,7 @@ parser_intrinsic_call_0(cbl_field_t *tgt, } else { + store_location_stuff(function_name); gg_call(VOID, function_name, 1, @@ -8822,6 +8796,7 @@ parser_intrinsic_call_2( cbl_field_t *tgt, TRACE1_INDENT TRACE1_REFER("parameter 2: ", ref2, "") } + store_location_stuff(function_name); gg_call(VOID, function_name, 3, @@ -8868,6 +8843,7 @@ parser_intrinsic_call_3( cbl_field_t *tgt, TRACE1_REFER("parameter 3: ", ref3, "") } + store_location_stuff(function_name); gg_call(VOID, function_name, 4, @@ -8919,6 +8895,8 @@ parser_intrinsic_call_4( cbl_field_t *tgt, TRACE1_REFER("parameter 4: ", ref4, "") } + + store_location_stuff(function_name); gg_call(VOID, function_name, 5, @@ -10504,11 +10482,12 @@ parser_call_exception_end( cbl_label_t *name ) void parser_call( cbl_refer_t name, - cbl_refer_t returning, + cbl_refer_t returned, // This is set by RETURNING clause size_t narg, cbl_ffi_arg_t args[], cbl_label_t *except, - cbl_label_t *not_except ) + cbl_label_t *not_except, + bool /*is_function*/) { SHOW_PARSE { @@ -10587,27 +10566,23 @@ 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. - refer_fill_dest(returning); - tree returning_location = gg_define_uchar_star(); - tree returning_length = gg_define_size_t(); + refer_fill_dest(returned); + tree returned_location = gg_define_uchar_star(); + tree returned_length = gg_define_size_t(); - tree returned_value_type; - if(returning.field) - { - // we were given a returning::field, so find its location and length: - gg_assign(returning_location, - member(returning.refer_decl_node, "qual_data")); - gg_assign(returning_length, - member(returning.refer_decl_node, "qual_size")); - returned_value_type = field_type_to_tree_type(returning.field); - } - else + size_t nbytes; + tree returned_value_type = tree_type_from_field_type(returned.field, nbytes); + + if(returned.field) { - returned_value_type = INT; + // we were given a returned::field, so find its location and length: + gg_assign(returned_location, + member(returned.refer_decl_node, "qual_data")); + gg_assign(returned_length, + member(returned.refer_decl_node, "qual_size")); } - tree function_handle = function_handle_from_name(name, narg); - + tree function_handle = function_handle_from_name(name, narg, returned_value_type); IF( function_handle, ne_op, @@ -10619,7 +10594,7 @@ parser_call( cbl_refer_t name, int *allocated = NULL; if(narg) { - arguments = (tree *)xmalloc(narg * sizeof(tree)); + arguments = (tree *)xmalloc(2*narg * sizeof(tree)); allocated = (int * )xmalloc(narg * sizeof(int)); } @@ -10628,6 +10603,7 @@ parser_call( cbl_refer_t name, build_int_cst_type(INT, narg)); // Put the arguments onto the stack: + size_t arg_count = 0; for( size_t i=0; i<narg; i++ ) { allocated[i] = 0; @@ -10645,47 +10621,96 @@ parser_call( cbl_refer_t name, { case by_reference_e: { + // CALL BY REFERENCE <group item> + // The pointer gets passed; 14.8.2.2 1) Requires that the + // receiving formal parameter has the same number or fewer + // bytes then the sending argument. + // + // CALL BY REFERENCE <pointer> + // Both the sending argument and the receiving formal parameter + // have to be pointers (14.8.2.3.2) (In our implementation, + // pointers can be handled like any other data-item) + // + // CALL BY REFERENCE <data-item> + // The activated element is not in the REPOSITORY paragraph: + // the receiving formal argument ahsll be the same length as + // the sending argument. + // + // UDF FUNCTION BY REFERENCE + // The definitions of the formal parameter and the argument + // shall have the same ALIGN, BLANK WHEN ZERO, DYNAMIC LENGTH, + // JUSTIFIED, PICTURE, SIGN, and USAGE clauses + // Pass the pointer to the data location, so that the called program // can both access and change the data. - arguments[i] = location; - //gg_printf(">>>>>Calling %ld location is %p\n", build_int_cst_type(SIZE_T, i), arguments[i], NULL_TREE); + arguments[arg_count] = location; - // BY REFERENCE variables might -- might! -- be going into an ANY LENGTH + // BY REFERENCE variables might be going into an ANY LENGTH // linkage variable in the called program. So, just in case, we need - // to provide a length through the global table. + // to provide a length through the global table. gg_assign(gg_array_value(var_decl_call_parameter_lengths, i), length); break; } case by_content_e: { + // The ISO spec doesn't distinguish between by_content and by_value + + // There are differences based on whether this is a CALL <program-id> + // or a user-defined function activation: + // BY CONTENT means that the called program gets a copy of the data. // We'll free this copy after the called program returns. // Allocate the memory, and make the copy: - arguments[i] = gg_define_char_star(); + arguments[arg_count] = gg_define_char_star(); allocated[i] = 1; - gg_assign(arguments[i], gg_malloc(length) ) ; - gg_memcpy(arguments[i], location, length); + gg_assign(arguments[arg_count], gg_malloc(length) ) ; + gg_memcpy(arguments[arg_count], location, length); - //gg_printf(">>>>>Calling %ld location is %p\n", build_int_cst_type(SIZE_T, i), arguments[i], NULL_TREE); + //gg_printf(">>>>>Calling %ld location is %p\n", build_int_cst_type(SIZE_T, i), arguments[arg_count], NULL_TREE); + + // BY CONTENT variables might be going into an ANY LENGTH + // linkage variable in the called program. So, just in case, we need + // to provide a length through the global table. + gg_assign(gg_array_value(var_decl_call_parameter_lengths, i), length); break; } case by_value_e: // For BY VALUE, we take whatever we've been given and do our best to - // make a 64-bit value out of it. That value gets placed on the stack + // make a 64-bit value out of it, although we move to 128 bits when + // necessary. - arguments[i] = gg_define_size_t(); - gg_assign(arguments[i], - gg_call_expr( - SIZE_T, - "__gg__fetch_call_by_value_value", - 1, - gg_get_address_of(args[i].refer.refer_decl_node))); + if( (args[i].refer.field && args[i].refer.field->data.digits > 18) + || ( args[i].refer.field + && args[i].refer.field->type == FldFloat + && args[i].refer.field->data.capacity == 16 ) ) + { + arguments[arg_count] = gg_define_variable(INT128); + gg_assign(arguments[arg_count], + gg_cast(INT128, + gg_call_expr( + INT128, + "__gg__fetch_call_by_value_value", + 1, + gg_get_address_of(args[i].refer.refer_decl_node)))); + } + else + { + arguments[arg_count] = gg_define_size_t(); + gg_assign(arguments[arg_count], + gg_cast(SIZE_T, + gg_call_expr( + INT128, + "__gg__fetch_call_by_value_value", + 1, + gg_get_address_of(args[i].refer.refer_decl_node)))); + } break; } + arg_count += 1; } gg_call(VOID, @@ -10693,26 +10718,27 @@ parser_call( cbl_refer_t name, 1, gg_cast(SIZE_T, function_handle)); - tree call_expr = gg_call_expr_list( COBOL_FUNCTION_RETURN_TYPE, + tree call_expr = gg_call_expr_list( returned_value_type, function_handle, - narg, + arg_count, arguments ); tree returned_value; - if( returning.field ) + if( returned.field ) { - // We are expecting a return value: + // We are expecting a return value of type CHAR_P, SSIZE_T, SIZE_T, + // UINT128 or INT128 + returned_value = gg_define_variable(returned_value_type); - // Before doing the call, we save the COBOL program_state: + push_program_state(); gg_assign(returned_value, gg_cast(returned_value_type, call_expr)); - // And after the call, we restore it: pop_program_state(); - // Because the CALL had a RETURNING clause, RETURN-CODE doesn't return a - // value. So, we make sure it is zero - cbl_field_t *return_code = cbl_field_of(symbol_at(return_code_register())); - gg_call(VOID, + // Because the CALL had a RETURNING clause, RETURN-CODE doesn't return a + // value. So, we make sure it is zero + cbl_field_t *return_code = cbl_field_of(symbol_at(return_code_register())); + gg_call(VOID, "__gg__int128_to_field", 5, gg_get_address_of(return_code->var_decl_node), @@ -10721,61 +10747,75 @@ parser_call( cbl_refer_t name, build_int_cst_type(INT, truncation_e), null_pointer_node ); - if(returned_value_type == CHAR_P) + + if( returned_value_type == CHAR_P ) { - // The returned value is to a null-terminated string: + // 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: IF( returned_value, eq_op, - gg_cast(TREE_TYPE(returned_value), - null_pointer_node ) ) + gg_cast(returned_value_type, null_pointer_node ) ) { // Somebody was discourteous enough to return a NULL pointer // We'll jam in spaces: - gg_memset( returning_location, + gg_memset( returned_location, char_nodes[' '], - returning_length ); + returned_length ); } ELSE { - // There is a valid pointer - //gg_printf("returned_value is %s\n", returned_value, NULL_TREE); - move_tree( returning, - returned_value); + // There is a valid pointer. Use move_tree to do the assignment, it + // does a reasonable job of handling nul-terminated strings that often + // are returned from C routines. + move_tree(returned, returned_value); } ENDIF TRACE1 { TRACE1_HEADER - TRACE1_REFER("returned value: ", returning, "") + TRACE1_REFER("returned value: ", returned, "") TRACE1_END } } - else if(returned_value_type == SSIZE_T) + else if( returned_value_type == SSIZE_T + || returned_value_type == SIZE_T + || returned_value_type == INT128 + || returned_value_type == UINT128) { - // We got back a 64-bit integer - // Assume that the two programmers agreed on the number of rdigits - // that were supposed to come back from the other routine: + // 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_refer", 5, - gg_get_address_of(returning.refer_decl_node), + gg_get_address_of(returned.refer_decl_node), gg_cast(INT128, returned_value), - member(returning.field->var_decl_node, "rdigits"), + member(returned.field->var_decl_node, "rdigits"), build_int_cst_type(INT, truncation_e), null_pointer_node ); TRACE1 { TRACE1_HEADER - TRACE1_REFER("returned value: ", returning, "") + TRACE1_REFER("returned value: ", returned, "") TRACE1_END } } - else if(returned_value_type == DOUBLE) + else if( returned_value_type == FLOAT + || returned_value_type == DOUBLE + || returned_value_type == FLOAT128) { - warnx("%s(): We don't yet know how to handle a function" - " that returns a double", - __func__); - gcc_assert(false); + // We are doing float-to-float, and we require that those be identical + // one the caller and callee sides. + gg_memcpy( returned_location, + gg_get_address_of(returned_value), + returned_length); + + TRACE1 + { + TRACE1_HEADER + TRACE1_REFER("returned value: ", returned, "") + TRACE1_END + } } else { @@ -11050,6 +11090,17 @@ parser_set_pointers( size_t ntgt, cbl_refer_t *tgts, cbl_refer_t source ) SHOW_PARSE { SHOW_PARSE_HEADER + SHOW_PARSE_FIELD(" source ", source.field); + char ach[128]; + sprintf(ach, + " source.addr_of %s", + source.addr_of ? "TRUE" : "FALSE" ); + SHOW_PARSE_TEXT(ach); + for( size_t i=0; i<ntgt; i++ ) + { + SHOW_PARSE_INDENT + SHOW_PARSE_FIELD("target ", tgts[i].field) + } SHOW_PARSE_END } refer_fill_source(source); @@ -11064,8 +11115,8 @@ parser_set_pointers( size_t ntgt, cbl_refer_t *tgts, cbl_refer_t source ) } else { - // When ADDRESS OF TARGET, the target must be linkage: - gcc_assert( tgts[i].field->attr & linkage_e ); + // When ADDRESS OF TARGET, the target must be linkage or based + gcc_assert( tgts[i].field->attr & (linkage_e | based_e) ); } if( source.field && !source.addr_of ) @@ -11595,6 +11646,48 @@ stash_exceptions( const cbl_enabled_exceptions_array_t *enabled ) } } +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_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 ) @@ -11610,44 +11703,7 @@ parser_exception_prepare( const cbl_name_t statement_name, if( enabled->nec ) { stash_exceptions(enabled); - - 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_line_number, build_int_cst_type(INT, - CURRENT_LINE_NUMBER)); - } - gg_assign(var_decl_exception_statement, gg_string_literal(statement_name)); + store_location_stuff(statement_name); } } @@ -13071,7 +13127,7 @@ move_helper(cbl_refer_t destref, if( (sourceref.field->attr & (linkage_e | based_e)) || ( destref.field->attr & (linkage_e | based_e)) ) { - goto dont_be_clever; + //goto dont_be_clever; } if( !moved ) @@ -13120,7 +13176,7 @@ move_helper(cbl_refer_t destref, size_error); } - dont_be_clever: + //dont_be_clever: if( !moved ) { @@ -13694,22 +13750,15 @@ initial_from_float128(cbl_field_t *field, _Float128 value) case FldFloat: { retval = (char *)xmalloc(field->data.capacity); -// char ach[128]; switch( field->data.capacity ) { case 4: - // strfromf128(ach, sizeof(ach), "%.9E", value); - // *(_Float32 *)retval = strtof32(ach, NULL); *(_Float32 *)retval = (_Float32) value; break; case 8: - // strfromf128(ach, sizeof(ach), "%.14E", value); - // *(_Float64 *)retval = strtof64(ach, NULL); *(_Float64 *)retval = (_Float64) value; break; case 16: - // strfromf128(ach, sizeof(ach), "%.33E", value); - // *(_Float128 *)retval = strtof128(ach, NULL); *(_Float128 *)retval = (_Float128) value; break; } @@ -14034,7 +14083,7 @@ psa_new_var_decl(cbl_field_t *new_var, const char *external_record_base) static size_t literal_count = 1; sprintf(base_name, "%s_%zd", "literal", literal_count++); } - else if( new_var->attr & (temporary_e | intermediate_e) )// | linkage_e) ) + else if( new_var->attr & (temporary_e | intermediate_e) ) { static size_t temp_count = 1; sprintf(base_name, "%s_%zd", "_temporary", temp_count++); @@ -14053,11 +14102,11 @@ psa_new_var_decl(cbl_field_t *new_var, const char *external_record_base) base_name, vs_external); } - else if( new_var->attr & (temporary_e | intermediate_e) )// | linkage_e) ) + else if( new_var->attr & (temporary_e | intermediate_e) ) { new_var_decl = gg_define_variable( cblc_field_type_node, base_name, - vs_static); // was vs_stack + vs_static); } else { @@ -14079,24 +14128,27 @@ parser_local_add(struct cbl_field_t *new_var ) SHOW_PARSE_END } - gg_call(VOID, - "__gg__push_local_variable", - 1, - gg_get_address_of(new_var->var_decl_node)); + IF( member(new_var->var_decl_node, "data"), + ne_op, + gg_cast(UCHAR_P, null_pointer_node) ) + { + gg_call(VOID, + "__gg__push_local_variable", + 1, + gg_get_address_of(new_var->var_decl_node)); + } + ELSE + ENDIF if( new_var->level == LEVEL01 || new_var->level == LEVEL77 ) { // We need to allocate memory on the stack for this variable - char achDataName[256]; - sprintf(achDataName, "..vardata_%lu", sv_data_name_counter++); - tree array_type = build_array_type_nelts(UCHAR, new_var->data.capacity); tree data_decl_node = gg_define_variable( array_type, - achDataName, + NULL, vs_stack); gg_assign( member(new_var->var_decl_node, "data"), gg_get_address_of(data_decl_node) ); - } cbl_refer_t wrapper; wrapper.field = new_var; @@ -14106,7 +14158,7 @@ parser_local_add(struct cbl_field_t *new_var ) void parser_symbol_add(struct cbl_field_t *new_var ) { - //fprintf(stderr, ">>>>>>> parser_symbol_add %s %s \n", cbl_field_type_str(new_var->type), new_var->name); + // fprintf(stderr, ">>>>>>> parser_symbol_add %s %s \n", cbl_field_type_str(new_var->type), new_var->name); // fprintf(stderr, // "parser_symbol_add %s %s", // new_var->name, @@ -14116,7 +14168,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) // fprintf(stderr, " %s\n", dch); const char *new_initial = NULL; - + if( !(new_var->attr & initialized_e) ) { if( is_register_field(new_var) ) @@ -14237,6 +14289,8 @@ parser_symbol_add(struct cbl_field_t *new_var ) case FldLiteralA: case FldNumericEdited: case FldAlphaEdited: + + case FldFloat: figconst = normal_value_e; break; default: @@ -14716,7 +14770,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) free(level_88_string); } - if( !(new_var->attr & (linkage_e | based_e)) ) + if( !(new_var->attr & ( linkage_e | based_e)) ) { IF( gg_attribute_bit_get(new_var, initialized_e), eq_op, size_t_zero_node ) { diff --git a/gcc/cobol/genapi.h b/gcc/cobol/genapi.h index 7a763052229319a49885df346d8808211f60f435..fe13e65f884a20dd8b70fbbc24363a484561572d 100644 --- a/gcc/cobol/genapi.h +++ b/gcc/cobol/genapi.h @@ -52,7 +52,7 @@ void parser_next_is_main(bool is_main); void parser_internal_is_ebcdic(bool is_ebcdic); void parser_division( cbl_division_t division, cbl_field_t *ret, size_t narg, cbl_ffi_arg_t args[] ); -void parser_enter_program(const char *funcname); +void parser_enter_program(const char *funcname, bool is_function); void parser_leave_program(); void parser_accept( struct cbl_refer_t refer, enum special_name_t special_e); @@ -459,6 +459,20 @@ parser_inspect_conv( cbl_refer_t input, void parser_exception_file( cbl_field_t *tgt, cbl_file_t* file = NULL ); +void +parser_intrinsic_numval_c( cbl_field_t *f, + cbl_refer_t& input, + bool locale, + cbl_refer_t& currency, + bool anycases, + bool test_numval_c = false); + +void +parser_intrinsic_subst( cbl_field_t *f, + cbl_refer_t& ref1, + size_t argc, + cbl_substitute_t * argv ); + void parser_intrinsic_callv( cbl_field_t *f, const char name[], @@ -556,10 +570,12 @@ size_t parser_call_target_update( size_t caller, void parser_file_stash( struct cbl_file_t *file ); -void parser_call( cbl_refer_t name, cbl_refer_t returning, - size_t narg, cbl_ffi_arg_t args[], - cbl_label_t *except, - cbl_label_t *not_except ); +void parser_call( cbl_refer_t name, + cbl_refer_t returning, + size_t narg, cbl_ffi_arg_t args[], + cbl_label_t *except, + cbl_label_t *not_except, + bool is_function); void parser_entry_activate( size_t iprog, const cbl_label_t *declarative ); diff --git a/gcc/cobol/gengen.cc b/gcc/cobol/gengen.cc index b2220e6bed187c0d0955c4581783fc35c07a8305..9c779326d7574229ece49527fa5cb0b9d308092a 100644 --- a/gcc/cobol/gengen.cc +++ b/gcc/cobol/gengen.cc @@ -632,10 +632,6 @@ gg_start_building_a_union(const char *type_name, tree type_context) return typedecl; } - - - - static tree gg_start_building_a_struct(const char *type_name, tree type_context) { @@ -2629,6 +2625,18 @@ 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; + } + tree gg_define_function_with_no_parameters(tree return_type, const char *funcname, @@ -3596,7 +3604,7 @@ 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))))); + gg_assign(retval, gg_cast(build_pointer_type(SIZE_T), gg_malloc( build_int_cst_type(SIZE_T, N * sizeof(size_t))))); for(size_t i=0; i<N; i++) { gg_assign(gg_array_value(retval, i), build_int_cst_type(SIZE_T, values[i])); @@ -3604,6 +3612,18 @@ gg_array_of_size_t( size_t N, size_t *values) return retval; } +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))))); + for(size_t i=0; i<N; i++) + { + gg_assign(gg_array_value(retval, i), build_int_cst_type(UCHAR, values[i])); + } + return retval; + } + tree gg_string_literal(const char *string) { diff --git a/gcc/cobol/gengen.h b/gcc/cobol/gengen.h index def32c88f06c9b81ee3aaf2fa9c24c71c49c7673..9ef5dfad6378652d598e927ad301bb3dd19ad9be 100644 --- a/gcc/cobol/gengen.h +++ b/gcc/cobol/gengen.h @@ -119,6 +119,13 @@ enum gg_variable_scope_t { struct gg_function_t { + // Nomenclature Alert: The "function" in gg_function_t was chosen + // originally because a PROGRAM-ID is implemented as a C-style "function", + // and there are numerous tree variables that refer to "functions". + // Eventually the COBOL compiler grew to handle not just COBOL PROGRAM-ID + // "programs", but also user-defined COBOL FUNCTION-ID "functions". This + // inevitably is confusing. Sorry about that. + // This structure contains state variables for a single function. const char *our_unmangled_name; @@ -188,7 +195,7 @@ struct gg_function_t // When parser_division(PROCEDURE) is called, it provides a cbl_field_t // *returning parameter. We stash it here; it's used during parser_exit() // to provide the data for the program's return value. - cbl_field_t *returning; + cbl_field_t *returning; // This one is on the stack, like a LOCAL-STORAGE // When a function is defined as having formal parameters (that is, there // is a USING clause in the function definition), we need to convert @@ -223,6 +230,10 @@ struct gg_function_t // back to the first declarative of its immediate parent. tree first_declarative_section; + // is_function is true when this structure is describing a COBOL FUNCTION-ID + // and is false for a PROGRAM-ID + bool is_function; + }; struct cbl_translation_unit_t @@ -513,6 +524,7 @@ extern size_t gg_sizeof(tree decl_node); extern tree gg_array_of_field_pointers( size_t N, cbl_field_t **fields ); extern tree gg_array_of_size_t( size_t N, size_t *values); +extern tree gg_array_of_bytes( size_t N, unsigned char *values); extern tree gg_indirect(tree pointer, tree byte_offset = NULL_TREE); extern tree gg_string_literal(const char *string); @@ -529,4 +541,5 @@ 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); #endif diff --git a/gcc/cobol/genutil.cc b/gcc/cobol/genutil.cc index f4e85201c503629a93e6fb8d8642c6c019d03c90..1f30c936e88eb613af1acf6658fd17de3d935460 100644 --- a/gcc/cobol/genutil.cc +++ b/gcc/cobol/genutil.cc @@ -91,8 +91,6 @@ tree var_decl_exit_address; // This is for implementing pseudo_return_ tree var_decl_call_parameter_count; // int __gg__call_parameter_count tree var_decl_call_parameter_lengths; // size_t *__gg__call_parameter_count - - int get_scaled_rdigits(cbl_field_t *field) { @@ -1999,7 +1997,10 @@ refer_fill_refmod(cbl_refer_t &refer) } } - if( have_offset && !any_length ) + bool temp_alpha = refer.field->type == FldAlphanumeric + && (refer.field->attr & temporary_e); + + if( have_offset && !any_length && !refmod_error && !temp_alpha ) { if( offset_val < 1 || offset_val > (int)refer.field->data.capacity ) @@ -2011,7 +2012,7 @@ refer_fill_refmod(cbl_refer_t &refer) } } - if( have_length && !any_length ) + if( have_length && !any_length && !refmod_error && !temp_alpha ) { if( length_val < 1 || length_val > (int)refer.field->data.capacity ) @@ -2023,7 +2024,7 @@ refer_fill_refmod(cbl_refer_t &refer) } } - if( have_offset && have_length && !refmod_error && !any_length ) + if( have_offset && have_length && !any_length && !refmod_error && !temp_alpha ) { if( offset_val-1 + length_val > (int)refer.field->data.capacity ) { diff --git a/gcc/cobol/genutil.h b/gcc/cobol/genutil.h index f2c13d3c6efe16684588c4483a1e1b9315b710c5..8c85c9fa17d79827233ca3f9a09e40b1d3f1e16c 100644 --- a/gcc/cobol/genutil.h +++ b/gcc/cobol/genutil.h @@ -66,7 +66,7 @@ extern tree var_decl_entry_location; // This is for managing ENTRY state extern tree var_decl_exit_address; // This is for implementing pseudo_return_pop extern tree var_decl_call_parameter_count; // int __gg__call_parameter_count -extern tree var_decl_call_parameter_lengths; // size_t *__gg__call_parameter_count +extern tree var_decl_call_parameter_lengths; // size_t *var_decl_call_parameter_lengths int get_scaled_rdigits(cbl_field_t *field); int get_scaled_digits(cbl_field_t *field); diff --git a/gcc/cobol/lang-specs.h b/gcc/cobol/lang-specs.h index 1fde026899f3b519ed78fa23aa37cf043d2cfc9d..8d7bd1ddd649d8273a0f8369bbd68fb62723e85b 100644 --- a/gcc/cobol/lang-specs.h +++ b/gcc/cobol/lang-specs.h @@ -38,6 +38,7 @@ "%{fcobol-exceptions*} " "%{fstatic-call} " "%{ffixed-form} %{ffree-form} " + "%{preprocess} " "%{dialect} " "%{!fsyntax-only:%(invoke_as)} " , 0, 0, 0}, diff --git a/gcc/cobol/lang.opt b/gcc/cobol/lang.opt index 867a619575903f0ebe8a9cd6d9ca1b055b4b2b6e..1cc4b3f1da85bc1a89370c5497d745c1e13c2148 100644 --- a/gcc/cobol/lang.opt +++ b/gcc/cobol/lang.opt @@ -114,6 +114,10 @@ fyacc-debug Cobol Var(yy_debug, 1) Init(0) Enable Cobol yacc debugging +preprocess +Cobol Joined Separate Var(cobol_preprocess) +preprocess <source_filter> before compiling + main Cobol -main Next source_file/PROGRAM-ID is called by main() diff --git a/gcc/cobol/lexio.h b/gcc/cobol/lexio.h index e619d853abfd4e80ae6ba0ced714b356c4870e45..2deca53c15c92f6bfc077f87c119111e6a45ac3d 100644 --- a/gcc/cobol/lexio.h +++ b/gcc/cobol/lexio.h @@ -230,13 +230,6 @@ struct replace_t { #include <list> class cdftext { - ////std::stack< std::list<replace_t> > replace_directives; - - //// bool parse_copy_directive( filespan_t& mfile ); - //// bool parse_replace_last_off( filespan_t& mfile ); - //// bool parse_replace_text( filespan_t& mfile, size_t current_lineno ); - //// parse_replace_directive( filespan_t& mfile, size_t current_lineno ); - static filespan_t free_form_reference_format( int fd ); static void process_file( filespan_t, int output, bool second_pass = false ); diff --git a/gcc/cobol/libgcobol.h b/gcc/cobol/libgcobol.h index 9ae955cf52dd58bb553f4d2134c07731fe06bdc8..c957905b88253e0ad213ad8872d272e21a73e91b 100644 --- a/gcc/cobol/libgcobol.h +++ b/gcc/cobol/libgcobol.h @@ -78,6 +78,13 @@ typedef struct cblc_field_t * the original value */ +enum substitute_flags_t + { + substitute_anycase_e = 1, + substitute_first_e = 2, // first and last are mutually exclusive + substitute_last_e = 4, + }; + enum cblc_file_flags_t { file_flag_optional_e = 0x00001, @@ -121,7 +128,6 @@ class supplemental_t std::vector<file_index_t> indexes; std::vector<int> uniques; }; - typedef struct cblc_file_t { char *name; // This is the name of the structure; might be the name of an environment variable diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y index 26c53222503132ff7bb541235ec6e7077595e438..ab2e387f2da85ebe449337609ce52e3da69c6cc7 100644 --- a/gcc/cobol/parse.y +++ b/gcc/cobol/parse.y @@ -58,6 +58,7 @@ set_data(len, data); return *this; } + literal_t& set( const cbl_field_t * field ) { assert(has_field_attr(field->attr, constant_e)); @@ -134,6 +135,29 @@ typedef std::map<data_category_t, struct cbl_refer_t*> category_map_t; + struct substitution_t { + enum subst_fl_t { subst_all_e, subst_first_e = 'F', subst_last_e = 'L' }; + bool anycase; + subst_fl_t first_last; + cbl_refer_t *orig, *replacement; + + substitution_t& init( bool anycase, char first_last, + cbl_refer_t *orig, cbl_refer_t *replacement ) { + this->anycase = anycase; + switch(first_last) { + case 'F': this->first_last = subst_first_e; break; + case 'L': this->first_last = subst_last_e; break; + default: + this->first_last = subst_all_e; + break; + } + this->orig = orig; + this->replacement = replacement; + return *this; + } + }; + typedef std::list<substitution_t> substitutions_t; + struct init_statement_t { bool to_value; bool to_default; @@ -274,7 +298,7 @@ /* intrinsics */ %token <string> LIST MAP NOLIST NOMAP NOSOURCE -%token <number> MIGHT_BE +%token <number> MIGHT_BE FUNCTION_UDF FUNCTION_UDF_0 %token <string> DATE_FMT TIME_FMT DATETIME_FMT @@ -293,7 +317,7 @@ %type <number> true_false posneg %type <number> open_io alphabet_etc %type <special_type> env_name1 -%type <string> numed collating_sequence context_word ctx_name +%type <string> numed collating_sequence context_word ctx_name locale_spec %type <literal> namestr alphabet_lit program_as repo_as %type <field> perform_cond kind_of_name alloc_ret %type <refer> simple_cond @@ -330,7 +354,7 @@ %type <refer> expr expr_term compute_expr free_tgt by_value_arg %type <refer> selected_name read_key read_into vary_by %type <refer> accept_refer num_operand envar search_expr any_arg -%type <refers> expr_list subscripts arg_list free_tgts +%type <refers> expr_list subscripts arg_list free_tgts udf_args %type <targets> move_tgts set_tgts %type <field> search_varying %type <field> search_term search_terms @@ -361,14 +385,14 @@ %type <arith> divide_into divide_by %type <refer> intrinsic_call -%type <field> intrinsic +%type <field> intrinsic intrinsic_locale %type <field> intrinsic0 %type <number> intrinsic_v intrinsic_I intrinsic_N intrinsic_X %type <number> intrinsic_I2 intrinsic_N2 intrinsic_X2 %type <number> lopper_case %type <number> return_body return_file -%type <field> trim_trailing +%type <field> trim_trailing function_udf %type <refer> str_input str_size %type <refer2> str_into @@ -396,9 +420,8 @@ %type <number> /* addr_len_of */ alphanum_pic %type <pic_part> alphanum_part -%type <ffi_arg> ffi_by_ref ffi_by_con ffi_by_val -%type <ffi_args> ffi_by_refs ffi_by_cons ffi_by_vals -%type <ffi_args> parameter parameters +%type <ffi_arg> parameter ffi_by_ref ffi_by_con ffi_by_val +%type <ffi_args> parameters %type <ffi_impl> call_body call_impl %type <ffi_arg> procedure_use @@ -408,8 +431,8 @@ %type <error_clauses> io_invalids read_eofs write_eops %type <boolean> io_invalid read_eof write_eop - global is_global -%type <number> mistake globally + global is_global anycase +%type <number> mistake globally first_last %type <use_culprit> culprits %type <labels> labels @@ -431,6 +454,9 @@ %type <refer> init_data stop_how stop_status %type <float128> cce_expr cce_factor const_value %type <prog_end> end_program1 +%type <substitution> subst_input +%type <substitutions> subst_inputs +%type <numval_locale_t> numval_locale %union { bool boolean; @@ -519,15 +545,20 @@ category_map_t *replacements; init_statement_t *init_stmt; struct { cbl_special_name_t *special; vargs_t *vargs; } display; + substitution_t substitution; + substitutions_t *substitutions; + struct { bool is_locale; cbl_refer_t *arg2; } numval_locale_t; + } %printer { fprintf(yyo, "clauses: 0x%04x", $$); } data_clauses %printer { fprintf(yyo, "%s %s", refer_type_str($$), $$? $$->name() : "<none>"); } <refer> %printer { fprintf(yyo, "%s", $$? name_of($$) : "[omitted]"); } alloc_ret %printer { fprintf(yyo, "%s %s '%s' (%s)", - cbl_field_type_str($$->type), name_of($$), - $$->data.initial? $$->data.initial : "<nil>", - $$->value_str() ); } <field> + $$? cbl_field_type_str($$->type) : "<%empty>", + $$? name_of($$) : "", + $$? $$->data.initial? $$->data.initial : "<nil>" : "", + $$? $$->value_str() : "" ); } <field> %printer { fprintf(yyo, "%s {%c%s %s}", $$.cond->field->name, $$.ante.invert? '!' : ' ', @@ -600,7 +631,7 @@ ALLOCATE ALPHABET ALPHABETIC ALPHABETIC_LOWER ALPHABETIC_UPPER ALPHANUMERIC ALPHANUMERIC_EDITED - ALPHED ALSO ALTERNATE ANNUITY ANY APPLY ARE + ALPHED ALSO ALTERNATE ANNUITY ANY ANYCASE APPLY ARE AREA AREAS AS ASCENDING ASIN ASSIGN AT ATAN AUTHOR @@ -635,10 +666,12 @@ FACTORIAL FALSE_kw FD FILENAME FILE_CONTROL FILE_KW FILE_LIMIT FINAL FIRST FIXED FOOTING FOR FORMATTED_CURRENT_DATE FORMATTED_DATE FORMATTED_DATETIME - FORMATTED_TIME FORM_OVERFLOW FREE FROM FUNCTION + FORMATTED_TIME FORM_OVERFLOW FREE + FRACTION_PART FROM FUNCTION FUNCTION_UDF GENERATE GIVING GLOBAL GO GROUP - HEADING HEX_OF HEX_TO_CHAR HIGH_VALUE HIGH_VALUES HOLD + HEADING HEX_OF HEX_TO_CHAR + HIGH_VALUE HIGH_VALUES HIGHEST_ALGEBRAIC HOLD IBM_360 IN INCLUDE INDEX INDEXED INDICATE INITIAL_kw INITIATE INPUT INSTALLATION INTERFACE @@ -648,11 +681,13 @@ KANJI KEY - LABEL LAST LEADING LEFT LENGTH LEVEL LEVEL66 + LABEL LAST LEADING LEFT LENGTH LENGTH_OF LEVEL LEVEL66 LEVEL88 LIMIT LIMITS LINE LINES LINE_COUNTER - LINAGE LINKAGE LOCATION LOCAL_STORAGE + LINAGE LINKAGE LOCALE LOCALE_COMPARE + LOCALE_DATE LOCALE_TIME LOCALE_TIME_FROM_SECONDS + LOCAL_STORAGE LOCATION LOCK LOCK_ON LOG LOG10 LOWER_CASE LOW_VALUE LOW_VALUES - LPAREN + LOWEST_ALGEBRAIC LPAREN MANUAL MAX MEAN MEDIAN MIDRANGE MIGHT_BE MIN MULTIPLE MOD MODE @@ -688,7 +723,7 @@ SPACES SPECIAL_NAMES SQRT STANDARD STANDARD_ALPHABET STANDARD_1 STANDARD_DEVIATION STATUS STDERR STDIN STDOUT - LITERAL SUM SWITCH SYMBOL SYMBOLIC SYNCHRONIZED + LITERAL SUBSTITUTE SUM SWITCH SYMBOL SYMBOLIC SYNCHRONIZED SYSIN SYSIPT SYSLST SYSOUT SYSPCH SYSPUNCH TALLY TALLYING TAN TERMINATE TEST TEST_DATE_YYYYMMDD @@ -822,7 +857,7 @@ statement_begin( const YYLTYPE& loc, int token ) { return perf; } - static int close_out_program( const char name[] ); + static bool close_out_program( const char name[] ); static void initialize_statement( std::list<cbl_refer_t> tgts, @@ -911,7 +946,7 @@ program_id: PROGRAM_ID dot namestr[name] program_as program_attrs[attr] dot current_division = identification_div_e; parser_division( identification_div_e, NULL, 0, NULL ); location_set(@1); - parser_enter_program( name ); + parser_enter_program( name, false ); if( symbols_begin() == symbols_end() ) { symbol_table_init(); } @@ -920,6 +955,7 @@ program_id: PROGRAM_ID dot namestr[name] program_as program_attrs[attr] dot if(false && yydebug) { warnx("current program is now %s", name); } + if( nparse_error > 0 ) YYABORT; } ; dot: %empty @@ -934,7 +970,7 @@ function_id: FUNCTION '.' NAME program_as program_attrs[attr] '.' current_division = identification_div_e; parser_division( identification_div_e, NULL, 0, NULL ); statement_begin(@1, FUNCTION); - parser_enter_program( $NAME ); + parser_enter_program( $NAME, true ); if( symbols_begin() == symbols_end() ) { symbol_table_init(); } @@ -943,10 +979,7 @@ function_id: FUNCTION '.' NAME program_as program_attrs[attr] '.' if(false && yydebug) { warnx("current program is now %s", $NAME); } - { - yyerror("error: FUNCTION-ID: almost implemented"); - YYERROR; - } + if( nparse_error > 0 ) YYABORT; } | FUNCTION '.' NAME program_as is PROTOTYPE '.' { @@ -1642,15 +1675,18 @@ repo_func: FUNCTION func_names INTRINSIC current.repository_add_all(); } | FUNCTION func_names + ; +func_names: func_name + | func_names func_name + ; +func_name: FUNCTION_UDF[udf] { - yyerrorv("syntax error: " - "user-defined functions ('%s') are unimplemented", - names.front()); - names.clear(); + current.repository_udf($udf); + } + | FUNCTION_UDF_0[udf] + { + current.repository_udf($udf); } - ; -func_names: NAME { names.clear(); names.push_back($NAME); } - | func_names NAME { names.push_back($NAME); } ; repo_program: PROGRAM_kw NAME repo_as @@ -1659,7 +1695,7 @@ repo_program: PROGRAM_kw NAME repo_as auto program = symbol_label( PROGRAM, LblProgram, 0, $NAME ); if( ! program ) { if( $repo_as.empty() ) { - yyerrorv("'%s' does not name an earlier program", $NAME); + yyerrorv("error: '%s' does not name an earlier program", $NAME); YYERROR; } program = symbol_label( PROGRAM, LblProgram, 0, @@ -1757,6 +1793,13 @@ special_name: env_name { symbol_decimal_point_set(','); } + | LOCALE NAME is locale_spec + { + current.locale($NAME, $locale_spec); + yyerrorv("%s:%d: LOCALE syntax not implemented", + __FILE__, __LINE__); + } + ; | upsi | SYMBOLIC characters symbolic is_alphabet { @@ -1764,6 +1807,10 @@ special_name: env_name __FILE__, __LINE__); } ; +locale_spec: NAME { $$ = $1; } + | LITERAL { $$ = string_of($1); } + + ; symbolic: NAME | NUMSTR ; @@ -1918,7 +1965,7 @@ alphabet_etc: alphabet_lit yyerrorv("'%c' can be only a single letter", $1.data); YYERROR; } - $$ = $1.data[0]; + $$ = (unsigned char)$1.data[0]; } | spaces_etc { // For figurative constants, pass the synmbol table index, @@ -2156,7 +2203,6 @@ data_section: FILE_SECT '.' | LINKAGE_SECT '.' { current_data_section_set(linkage_datasect_e); } fields_maybe - | cdf ; file_descrs: file_descr @@ -2417,7 +2463,8 @@ fields: field | fields field ; -field: data_descr '.' +field: cdf + | data_descr '.' { if( in_file_section() && $data_descr->level == 1 ) { if( !file_section_parent_set($data_descr) ) { @@ -2657,6 +2704,7 @@ data_descr: data_descr1 const_value: cce_expr | BYTE_LENGTH of name { $$ = $name->data.capacity; } | LENGTH of name { $$ = $name->data.capacity; } + | LENGTH_OF of name { $$ = $name->data.capacity; } ; value78: literalism @@ -2884,14 +2932,17 @@ data_descr1: level_name | level_name[field] data_clauses { +#ifndef YYNOMEM +# define YYNOMEM YYERROR +#endif assert($field == current_field()); if( $data_clauses == value_clause_e ) { // only VALUE, no PIC // Error unless VALUE is a figurative constant or (quoted) string. - if( !has_field_attr($field->attr, quoted_e) && + if( $field->type != FldPointer && + ! has_field_attr($field->attr, quoted_e) && normal_value_e == cbl_figconst_of($field->data.initial) ) { - yyerrorv("syntax error: " - "%s numeric VALUE %s requires PICTURE", + yyerrorv("error: %s numeric VALUE %s requires PICTURE", $field->name, $field->data.initial); } $field->type = FldAlphanumeric; @@ -2975,16 +3026,19 @@ data_descr1: level_name if( ! ($field->data.capacity + 1 == strlen($field->data.initial) && p[-1] == '!') ) { char *msg; - asprintf(&msg, "warning: VALUE of %s " - "has length %zu, exceeding its size (%u)", - $field->name, strlen($field->data.initial), - $field->data.capacity); + if( -1 == asprintf(&msg, "warning: VALUE of %s " + "has length %zu, exceeding its size (%u)", + $field->name, strlen($field->data.initial), + $field->data.capacity) ) { + yyerror("could not allocate VALUE size error message"); + YYNOMEM; + } yywarn(msg); } } } // Ensure signed initial VALUE is for signed numeric type - if( is_numeric($field) && $field->data.initial ) { + if( is_numeric($field) && $field->data.initial && $field->type != FldFloat) { switch( $field->data.initial[0] ) { case '-': case '+': if( !has_field_attr($field->attr, signable_e) ) { @@ -3118,10 +3172,11 @@ data_clauses: data_clause YYERROR; } cbl_field_t *field = current_field(); +#if 0 if( symbol_redefines(field) ) { redefine_field(field); } - +#endif const int globex = (global_e | external_e); if( (($$ | $2) & globex) == globex ) { yyerror("GLOBAL and EXTERNAL specified"); @@ -3459,7 +3514,38 @@ usage_clause1: usage COMPUTATIONAL[comp] native $$ = symbol_field_index_set( current_field() )->type; } // We should enforce data/code pointers with a different type. - | usage POINTER { $$ = FldPointer; } + | usage POINTER + { + $$ = FldPointer; + auto field = current_field(); + auto redefined = symbol_redefines(field); + + field->data.capacity = sizeof(void *); + + if( dialect_ibm() && redefined && + is_numeric(redefined->type) && redefined->size() == 4) { + // For now, we allow POINTER to expand a 32-bit item to 64 bits. + if( yydebug ) { + warnx("%s: expanding #%zu %s capacity %u => %u", __func__, + field_index(redefined), redefined->name, + redefined->data.capacity, field->data.capacity); + } + + redefined->data.capacity = field->data.capacity; + + if( redefined->data.initial ) { + char *s = new char[1 + redefined->data.capacity]; + if( !s ) { + yyerrorv("error: could not expand initial value of %s", field->name); + YYERROR; + } + (void)! snprintf(s, 1 + redefined->data.capacity, + "%s ", redefined->data.initial); + std::replace(s, s + strlen(s), '!', char(0x20)); + redefined->data.initial = s; + } + } + } | usage POINTER TO error { yyerror("error: unimplemented: TYPEDEF"); @@ -3572,6 +3658,7 @@ redefines_clause: REDEFINES NAME[orig] orig->level, name_of(orig), field->level, name_of(field)); } + if( valid_redefine(field, orig) ) { /* * Defer "inheriting" the parent's description until the @@ -3591,7 +3678,7 @@ any_length: ANY LENGTH current_data_section == linkage_datasect_e && 1 < current.program_level()) ) { yyerror("ANY LENGTH valid only " - "for 01 in LIKAGE SECTION of a contained program"); + "for 01 in LINKAGE SECTION of a contained program"); YYERROR; } field->attr |= any_length_e; @@ -3703,13 +3790,13 @@ procedure_args: USING procedure_uses[args] { if( !procedure_division_ready(NULL, $args) ) YYABORT; } - | USING procedure_uses[args] RETURNING scalar[ret] + | USING procedure_uses[args] RETURNING name[ret] { - if( !procedure_division_ready($ret->field, $args) ) YYABORT; + if( !procedure_division_ready($ret, $args) ) YYABORT; } - | RETURNING scalar[ret] + | RETURNING name[ret] { - if( !procedure_division_ready($ret->field, NULL) ) YYABORT; + if( !procedure_division_ready($ret, NULL) ) YYABORT; } ; procedure_uses: procedure_use { $$ = new ffi_args_t($1); } @@ -3811,7 +3898,7 @@ sentence: statements '.' { if( nparse_error > 0 ) YYABORT; do { - if( 0 != close_out_program(NULL) ) YYABORT; // no recovery + if( ! close_out_program(NULL) ) YYABORT; // no recovery } while( current.program_level() > 0 ); YYACCEPT; } @@ -5101,7 +5188,7 @@ refmod: LPAREN expr[from] ':' expr[len] ')' %prec NAME | LPAREN expr[from] ':' ')' %prec NAME { $$.from = $from; - $$.len = &null_reference; + $$.len = cbl_refer_t::empty(); } ; @@ -5371,7 +5458,7 @@ move: MOVE scalar TO move_tgts[tgts] | MOVE intrinsic_call TO move_tgts[tgts] { statement_begin(@1, MOVE); - if( !parser_move2($tgts, $2->field) ) { YYERROR; } + if( !parser_move2($tgts, *$2) ) { YYERROR; } } | MOVE CORRESPONDING scalar[from] TO scalar[to] @@ -5565,6 +5652,17 @@ num_value: scalar | num_literal { $$ = new_reference($1); } | ADDRESS OF scalar {$$ = $scalar; $$->addr_of = true; } | DETAIL OF scalar {$$ = $scalar; } + | LENGTH_OF scalar[val] { + location_set(@1); + $$ = new cbl_refer_t( new_tempnumeric_float() ); + auto r1 = $val; + if( ! dialect_ibm() ) { + yyerrorv("LENGTH OF %s requires '-dialect ibm' option", + $val->field->name); + } + if( ! intrinsic_call_1($$->field, LENGTH, r1) ) YYERROR; + } + ; @@ -5690,8 +5788,7 @@ stop: STOP RUN stop_how ; stop_how: %empty { - static cbl_refer_t nothing; - $$ = ¬hing; + $$ = cbl_refer_t::empty(); } | with NORMAL stop_status { @@ -6100,16 +6197,16 @@ varg: varg1 | ALL varg1 { $$ = $2; $$->all = true; } ; -varg1: literal +varg1: scalar + | intrinsic_call + | literal { $$ = new_reference($1); } - | scalar | reserved_value { $$ = new_reference(constant_of(constant_index($1))); } - | intrinsic_call ; literal: LITERAL @@ -6713,7 +6810,7 @@ set_operand: set_tgt | signed_literal { $$ = new_reference($1); } ; set_tgt: scalar - | ADDRESS OF scalar { $$ = $scalar; $$->addr_of = true; } + | ADDRESS of scalar { $$ = $scalar; $$->addr_of = true; } ; set: SET set_tgts[tgts] TO set_operand[src] @@ -6751,6 +6848,14 @@ set: SET set_tgts[tgts] TO set_operand[src] YYERROR; } } + | SET set_tgts[tgts] TO NULLS[src] + { + statement_begin(@1, SET); + if( !valid_set_targets(*$tgts, true) ) { + YYERROR; + } + ast_set_pointers($tgts->targets, constant_of(constant_index(NULLS))); + } | SET set_tgts TO spaces_etc[error] { yyerror("syntax error: invalid value for SET TO"); @@ -6786,7 +6891,7 @@ set: SET set_tgts[tgts] TO set_operand[src] // send the signal to clear the stashed exception values parser_exception_raise(ec_none_e); } - | SET LENGTH OF scalar TO scalar + | SET LENGTH_OF scalar TO scalar { statement_begin(@1, SET); yyerror("SET LENGTH OF is not implemented"); @@ -7519,8 +7624,7 @@ first_leading: FIRST { $$ = bound_first_e; } | LEADING { $$ = bound_leading_e; } ; -alphaval: /* scalar */ - /* | */LITERAL { $$ = new_reference(new_literal($1, quoted_e)); } +alphaval: LITERAL { $$ = new_reference(new_literal($1, quoted_e)); } | reserved_value { $$ = new_reference( constant_of(constant_index($1)) ); @@ -7649,19 +7753,21 @@ call: call_impl end_call call_impl: CALL call_body[body] { ffi_args_t *params = $body.using_params; + if( yydebug ) params->dump(); size_t narg = params? params->elems.size() : 0; cbl_ffi_arg_t args[1 + narg], *pargs = NULL; if( narg > 0 ) { pargs = use_list(params, args); } parser_call( *$body.ffi_name, - *$body.ffi_returning, narg, pargs, NULL, NULL ); + *$body.ffi_returning, narg, pargs, NULL, NULL, false ); current.declaratives_evaluate(); } ; call_cond: CALL call_body[body] call_excepts[except] { ffi_args_t *params = $body.using_params; + if( yydebug ) params->dump(); size_t narg = params? params->elems.size() : 0; cbl_ffi_arg_t args[1 + narg], *pargs = NULL; if( narg > 0 ) { @@ -7669,7 +7775,7 @@ call_cond: CALL call_body[body] call_excepts[except] } parser_call( *$body.ffi_name, *$body.ffi_returning, narg, pargs, - $except.on_error, $except.not_error ); + $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); @@ -7683,14 +7789,14 @@ call_body: ffi_name { statement_begin(@1, CALL); $$.ffi_name = $ffi_name; $$.using_params = NULL; - $$.ffi_returning = &null_reference; + $$.ffi_returning = cbl_refer_t::empty(); } | ffi_name USING parameters { statement_begin(@1, CALL); $$.ffi_name = $ffi_name; $$.using_params = $parameters; - $$.ffi_returning = &null_reference; + $$.ffi_returning = cbl_refer_t::empty(); } | ffi_name RETURNING scalar[ret] { statement_begin(@1, CALL); @@ -7737,23 +7843,17 @@ ffi_name: name | LITERAL { $$ = new_reference(new_literal($1, quoted_e)); } ; -parameters: parameter +parameters: parameter { $$ = new ffi_args_t($1); } | parameters parameter { - $1->elems.splice($1->elems.end(), $2->elems); + $1->push_back($2); $$ = $1; } ; -parameter: ffi_by_ref { $$ = new ffi_args_t($1); } - | by REFERENCE ffi_by_refs { $$ = $3; } - | by CONTENT ffi_by_cons { $$ = $3; } - | by VALUE ffi_by_vals { $$ = $3; } - ; -ffi_by_refs: ffi_by_ref { $$ = new ffi_args_t($1); } - | ffi_by_refs ffi_by_ref[ref] - { - $$ = $1->push_back($ref); - } +parameter: ffi_by_ref { $$ = $1; $$->crv = cbl_ffi_crv_t(0); } + | by REFERENCE ffi_by_ref { $$ = $3; } + | by CONTENT ffi_by_con { $$ = $3; } + | by VALUE ffi_by_val { $$ = $3; } ; ffi_by_ref: scalar_arg[refer] { @@ -7770,20 +7870,10 @@ ffi_by_ref: scalar_arg[refer] } ; -ffi_by_cons: ffi_by_con { $$ = new ffi_args_t($1); } - | ffi_by_cons ffi_by_con { $$ = $1->push_back($2); } - ; -ffi_by_con: scalar_arg - { - $$ = new cbl_ffi_arg_t(by_content_e, $1); - } - | ADDRESS OF scalar_arg[arg] - { - $$ = new cbl_ffi_arg_t(by_content_e, $arg, address_of_e); - } - | LENGTH OF scalar_arg[arg] +ffi_by_con: expr { - $$ = new cbl_ffi_arg_t(by_content_e, $arg, length_of_e); + cbl_refer_t *r = new cbl_refer_t(*$1); + $$ = new cbl_ffi_arg_t(by_content_e, r); } | LITERAL { @@ -7797,9 +7887,6 @@ ffi_by_con: scalar_arg } ; -ffi_by_vals: ffi_by_val { $$ = new ffi_args_t($1); } - | ffi_by_vals ffi_by_val { $$ = $1->push_back($2); } - ; ffi_by_val: by_value_arg { $$ = new cbl_ffi_arg_t(by_value_e, $1); @@ -7813,7 +7900,7 @@ ffi_by_val: by_value_arg { $$ = new cbl_ffi_arg_t(by_value_e, $scalar, address_of_e); } - | LENGTH OF scalar + | LENGTH_OF scalar { $$ = new cbl_ffi_arg_t(by_value_e, $scalar, length_of_e); } @@ -8283,6 +8370,38 @@ function: %empty %prec FUNCTION } ; +function_udf: FUNCTION_UDF '(' udf_args[args] ')' { + auto L = cbl_label_of(symbol_at($1)); + cbl_field_t *returning_as = symbol_valid_udf_args( $1, $args->refers ); + if( ! returning_as ) YYERROR; + $$ = new_temporary_clone(returning_as); + auto narg = $args->refers.size(); + cbl_ffi_arg_t args[narg]; + std::transform( $args->refers.begin(), $args->refers.end(), + args, []( cbl_refer_t& arg ) { + auto ar = new cbl_refer_t(arg); + return cbl_ffi_arg_t(ar); } ); + + auto name = new_literal(L->name, quoted_e); + parser_call( name, $$, narg, args, NULL, NULL, true ); + } + | FUNCTION_UDF_0 { + static const size_t narg = 0; + static cbl_ffi_arg_t *args = NULL; + + auto L = cbl_label_of(symbol_at($1)); + cbl_field_t *returning_as = symbol_valid_udf_args( $1 ); + if( ! returning_as ) YYERROR; + $$ = new_temporary_clone(returning_as); + + auto name = new_literal(L->name, quoted_e); + parser_call( name, $$, narg, args, NULL, NULL, true ); + } + ; +udf_args: %empty { static refer_list_t empty(NULL); $$ = ∅ } + | arg_list + ; + /* * The scanner returns a function-token (e.g. NUMVAL) if it was * preceded by FUNCTION, or if the name is in the program's @@ -8299,7 +8418,8 @@ function: %empty %prec FUNCTION * alpahaval: LITERAL, reserved_value, instrinsic, or scalar * Probably any numeric argument could be an expression. */ -intrinsic: intrinsic0 +intrinsic: function_udf + | intrinsic0 | intrinsic_v '(' arg_list[args] ')' { location_set(@1); size_t n = $args->size(); @@ -8365,16 +8485,6 @@ intrinsic: intrinsic0 parser_exception_file( $$, $filename ); } - | LENGTH OF name { - location_set(@1); - $$ = new_tempnumeric_float(); - auto r1 = new cbl_refer_t($name); - if( ! intrinsic_call_1($$, LENGTH, r1) ) YYERROR; - if( ! dialect_ibm() ) { - yyerrorv("LENGTH OF %s requires '-dialect ibm' option", $name->name); - } - } - | FORMATTED_DATE '(' DATE_FMT[r1] expr[r2] ')' { location_set(@1); $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATE); @@ -8388,8 +8498,9 @@ intrinsic: intrinsic0 location_set(@1); $$ = new_alphanumeric(MAXLENGTH_FORMATTED_DATETIME); auto r1 = new_reference(new_literal($r1, quoted_e)); + static cbl_refer_t r3(literally_zero); if( ! intrinsic_call_4($$, FORMATTED_DATETIME, - r1, $r2, $r3, NULL) ) YYERROR; + r1, $r2, $r3, &r3) ) YYERROR; } | FORMATTED_DATETIME '(' DATETIME_FMT[r1] expr[r2] expr[r3] expr[r4] ')' { @@ -8422,21 +8533,21 @@ intrinsic: intrinsic0 if( ! intrinsic_call_1($$, FORMATTED_CURRENT_DATE, r1) ) YYERROR; } - | TEST_FORMATTED_DATETIME '(' DATE_FMT[r1] num_operand[r2] ')' { + | TEST_FORMATTED_DATETIME '(' DATE_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); auto r1 = new_reference(new_literal($r1, quoted_e)); if( ! intrinsic_call_2($$, TEST_FORMATTED_DATETIME, r1, $r2) ) YYERROR; } - | TEST_FORMATTED_DATETIME '(' TIME_FMT[r1] num_operand[r2] ')' { + | TEST_FORMATTED_DATETIME '(' TIME_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); auto r1 = new_reference(new_literal($r1, quoted_e)); if( ! intrinsic_call_2($$, TEST_FORMATTED_DATETIME, r1, $r2) ) YYERROR; } - | TEST_FORMATTED_DATETIME '(' DATETIME_FMT[r1] num_operand[r2] ')' + | TEST_FORMATTED_DATETIME '(' DATETIME_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); @@ -8444,14 +8555,14 @@ intrinsic: intrinsic0 if( ! intrinsic_call_2($$, TEST_FORMATTED_DATETIME, r1, $r2) ) YYERROR; } - | INTEGER_OF_FORMATTED_DATE '(' DATE_FMT[r1] num_operand[r2] ')' { + | INTEGER_OF_FORMATTED_DATE '(' DATE_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); auto r1 = new_reference(new_literal($r1, quoted_e)); if( ! intrinsic_call_2($$, INTEGER_OF_FORMATTED_DATE, r1, $r2) ) YYERROR; } - | INTEGER_OF_FORMATTED_DATE '(' DATETIME_FMT[r1] num_operand[r2] ')' + | INTEGER_OF_FORMATTED_DATE '(' DATETIME_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); @@ -8459,14 +8570,14 @@ intrinsic: intrinsic0 if( ! intrinsic_call_2($$, INTEGER_OF_FORMATTED_DATE, r1, $r2) ) YYERROR; } - | SECONDS_FROM_FORMATTED_TIME '(' TIME_FMT[r1] num_operand[r2] ')' { + | SECONDS_FROM_FORMATTED_TIME '(' TIME_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); auto r1 = new_reference(new_literal($r1, quoted_e)); if( ! intrinsic_call_2($$, SECONDS_FROM_FORMATTED_TIME, r1, $r2) ) YYERROR; } - | SECONDS_FROM_FORMATTED_TIME '(' DATETIME_FMT[r1] num_operand[r2] ')' + | SECONDS_FROM_FORMATTED_TIME '(' DATETIME_FMT[r1] varg[r2] ')' { location_set(@1); $$ = new_tempnumeric(); @@ -8485,16 +8596,11 @@ intrinsic: intrinsic0 if( ! intrinsic_call_1($$, $func, $r1 )) YYERROR; } ; - | NUMVAL_C '(' varg[r1] varg[r2] ')' { - location_set(@1); - $$ = new_tempnumeric_float(); - if( ! intrinsic_call_2($$, NUMVAL_C, $r1, $r2) ) YYERROR; - } - | NUMVAL_C '(' varg[r1] ')' { + | NUMVAL_C '(' varg[r1] numval_locale[r2] anycase ')' { location_set(@1); - $$ = new_tempnumeric_float(); - cbl_refer_t dummy = {}; - if( ! intrinsic_call_2($$, NUMVAL_C, $r1, &dummy) ) YYERROR; + $$ = new_tempnumeric(); + parser_intrinsic_numval_c( $$, *$r1, $r2.is_locale, + *$r2.arg2, $anycase ); } | ORD '(' alpha_val[r1] ')' { @@ -8514,15 +8620,29 @@ intrinsic: intrinsic0 $$ = new_tempnumeric_float(); if( ! intrinsic_call_1($$, RANDOM, $r1) ) YYERROR; } - | TEST_NUMVAL_C '(' varg[r1] varg[r2] ')' { + + | SUBSTITUTE '(' varg[r1] subst_inputs[inputs] ')' { location_set(@1); - $$ = new_alphanumeric(64); // how long? - if( ! intrinsic_call_2($$, TEST_NUMVAL_C, $r1, $r2) ) YYERROR; + $$ = new_alphanumeric(64); + auto narg = $inputs->size(); + cbl_substitute_t args[narg]; + std::transform( $inputs->begin(), $inputs->end(), args, + []( const substitution_t& arg ) { + cbl_substitute_t output( arg.anycase, + char(arg.first_last), + arg.orig, + arg.replacement ); + return output; } ); + + parser_intrinsic_subst($$, *$r1, narg, args); } - | TEST_NUMVAL_C '(' varg[r1] ')' { + + + | TEST_NUMVAL_C '(' varg[r1] numval_locale[r2] anycase ')' { location_set(@1); - $$ = new_alphanumeric(64); // how long? - if( ! intrinsic_call_2($$, TEST_NUMVAL_C, $r1, NULL) ) YYERROR; + $$ = new_tempnumeric(); + parser_intrinsic_numval_c( $$, *$r1, $r2.is_locale, + *$r2.arg2, $anycase, true ); } | TRIM '(' error ')' { yyerrorv("error: invalid TRIM argument"); @@ -8531,15 +8651,21 @@ intrinsic: intrinsic0 | TRIM '(' expr[r1] trim_trailing ')' { location_set(@1); - if( $r1->field->type != FldGroup && - $r1->field->type != FldAlphanumeric && - $r1->field->type != FldLiteralA && - $r1->field->type != FldAlphaEdited && - $r1->field->type != FldNumericEdited ) { - if( 0 == ($r1->field->attr & blank_zero_e) ) { - yyerrorv("error: TRIM argument must be alphanumeric"); - YYERROR; - } + switch( $r1->field->type ) { + case FldGroup: + case FldAlphanumeric: + case FldLiteralA: + case FldAlphaEdited: + case FldNumericEdited: + break; // alphanumeric OK + default: + // BLANK WHEN ZERO implies numeric-edited, so OK + if( has_field_attr($r1->field->attr, blank_zero_e) ) { + break; + } + yyerrorv("error: TRIM argument must be alphanumeric"); + YYERROR; + break; } $$ = new_alphanumeric($r1->field->data.capacity); cbl_refer_t * how = new_reference($trim_trailing); @@ -8576,7 +8702,7 @@ intrinsic: intrinsic0 $$ = new_alphanumeric($r1->field->data.capacity); break; default: - if( $1 == NUMVAL ) + if( $1 == NUMVAL || $1 == NUMVAL_F ) { $$ = new_temporary(FldFloat); } @@ -8585,6 +8711,21 @@ intrinsic: intrinsic0 $$ = new_temporary(type); } } + if( $1 == NUMVAL_F ) { + if( is_literal($r1->field) ) { + _Float128 output __attribute__ ((__unused__)); + auto input = $r1->field->data.initial; + auto local = strdup(input), pend = local; + if( !local ) { warn("%s: %s", __func__, input); return false; } + 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' ) { + yyerrorv("error: '%s' is not a numeric string", input); + } + } + } if( ! intrinsic_call_1($$, $1, $r1) ) YYERROR; } @@ -8679,7 +8820,6 @@ intrinsic: intrinsic0 $r1, $r2, $r3) ) YYERROR; } - | YEAR_TO_YYYY '(' expr[r1] ')' { location_set(@1); @@ -8722,8 +8862,6 @@ intrinsic: intrinsic0 $r1, $r2, $r3) ) YYERROR; } - - | intrinsic_N2 '(' expr[r1] expr[r2] ')' { location_set(@1); @@ -8748,8 +8886,83 @@ intrinsic: intrinsic0 $$ = new_alphanumeric($r1->field->data.capacity); if( ! intrinsic_call_2($$, $1, $r1, $r2) ) YYERROR; } + | intrinsic_locale ; +numval_locale: %empty { + $$.is_locale = false; + $$.arg2 = cbl_refer_t::empty(); + } + | LOCALE NAME { $$.is_locale = true; $$.arg2 = NULL; + yyerror("unimplemented: NUMVAL_C LOCALE"); YYERROR; + } + | varg { $$.is_locale = false; $$.arg2 = $1; } + ; + +subst_inputs: subst_input { $$ = new substitutions_t; $$->push_back($1); } + | subst_inputs subst_input { $$ = $1; $$->push_back($2); } + ; +subst_input: anycase first_last varg[v1] varg[v2] { + $$.init( $anycase, $first_last, $v1, $v2 ); + } + ; + +intrinsic_locale: + LOCALE_COMPARE '(' varg[r1] varg[r2] ')' + { + location_set(@1); + $$ = new_alphanumeric($r1->field->data.capacity); + cbl_refer_t dummy = {}; + if( ! intrinsic_call_3($$, LOCALE_COMPARE, $r1, $r2, &dummy) ) YYERROR; + } + | LOCALE_COMPARE '(' varg[r1] varg[r2] varg[r3] ')' + { + location_set(@1); + $$ = new_alphanumeric($r1->field->data.capacity); + if( ! intrinsic_call_3($$, LOCALE_COMPARE, $r1, $r2, $r3) ) YYERROR; + } + + | LOCALE_DATE '(' varg[r1] ')' + { + location_set(@1); + $$ = new_alphanumeric($r1->field->data.capacity); + 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); + if( ! intrinsic_call_2($$, LOCALE_DATE, $r1, $r2) ) YYERROR; + } + | LOCALE_TIME '(' varg[r1] ')' + { + location_set(@1); + $$ = new_alphanumeric($r1->field->data.capacity); + 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); + if( ! intrinsic_call_2($$, LOCALE_TIME, $r1, $r2) ) YYERROR; + } + | LOCALE_TIME_FROM_SECONDS '(' varg[r1] ')' + { + location_set(@1); + $$ = new_alphanumeric($r1->field->data.capacity); + 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); + if( ! intrinsic_call_2($$, LOCALE_TIME_FROM_SECONDS, $r1, $r2) ) YYERROR; + } + ; + lopper_case: LOWER_CASE { $$ = LOWER_CASE; } | UPPER_CASE { $$ = UPPER_CASE; } ; @@ -8807,6 +9020,11 @@ intrinsic0: CURRENT_DATE { $$ = new_tempnumeric_float(); parser_intrinsic_call_0( $$, "__gg__pi" ); } + | SECONDS_PAST_MIDNIGHT { + location_set(@1); + $$ = new_tempnumeric(); + intrinsic_call_0( $$, SECONDS_PAST_MIDNIGHT ); + } | UUID4 { location_set(@1); $$ = new_alphanumeric(32); // don't know correct size @@ -8819,19 +9037,19 @@ intrinsic0: CURRENT_DATE { } ; -intrinsic_I: BYTE_LENGTH { $$ = BYTE_LENGTH; } - | DATE_OF_INTEGER { $$ = DATE_OF_INTEGER; } +intrinsic_I: DATE_OF_INTEGER { $$ = DATE_OF_INTEGER; } | DAY_OF_INTEGER { $$ = DAY_OF_INTEGER; } | FACTORIAL { $$ = FACTORIAL; } + | FRACTION_PART { $$ = FRACTION_PART; } + | HIGHEST_ALGEBRAIC { $$ = HIGHEST_ALGEBRAIC; } | INTEGER { $$ = INTEGER; } | INTEGER_OF_DATE { $$ = INTEGER_OF_DATE; } | INTEGER_OF_DAY { $$ = INTEGER_OF_DAY; } | INTEGER_PART { $$ = INTEGER_PART; } + | LOWEST_ALGEBRAIC { $$ = LOWEST_ALGEBRAIC; } | SIGN { $$ = SIGN; } | TEST_DATE_YYYYMMDD { $$ = TEST_DATE_YYYYMMDD; } | TEST_DAY_YYYYDDD { $$ = TEST_DAY_YYYYDDD; } - | TEST_NUMVAL { $$ = TEST_NUMVAL; } - | TEST_NUMVAL_F { $$ = TEST_NUMVAL_F; } | ULENGTH { $$ = ULENGTH; } | UPOS { $$ = UPOS; } | USUPPLEMENTARY { $$ = USUPPLEMENTARY; } @@ -8862,11 +9080,14 @@ intrinsic_N2: ANNUITY { $$ = ANNUITY; } ; intrinsic_X: BIT_TO_CHAR { $$ = BIT_TO_CHAR; } + | BYTE_LENGTH { $$ = BYTE_LENGTH; } | HEX_TO_CHAR { $$ = HEX_TO_CHAR; } | LENGTH { $$ = LENGTH; } | NUMVAL { $$ = NUMVAL; } | NUMVAL_F { $$ = NUMVAL_F; } | REVERSE { $$ = REVERSE; } + | TEST_NUMVAL { $$ = TEST_NUMVAL; } + | TEST_NUMVAL_F { $$ = TEST_NUMVAL_F; } ; intrinsic_X2: NATIONAL_OF { $$ = NATIONAL_OF; } @@ -8890,6 +9111,10 @@ all: %empty { $$ = false; } | ALL { $$ = true; } ; +anycase: %empty { $$ = false; } + | ANYCASE { $$ = true; } + ; + as: %empty | AS ; @@ -8926,6 +9151,11 @@ file: %empty | FILE_KW ; +first_last: %empty { $$ = 0; } + | FIRST { $$ = 'F'; } + | LAST { $$ = 'L'; } + ; + is_global: %empty %prec GLOBAL { $$ = false; } | is GLOBAL { $$ = true; } ; @@ -9050,7 +9280,7 @@ cdf_use: %empty YYERROR; } static const cbl_label_t all = { - LblNone, 0,0, false,false,false, 0, ":all:" }; + LblNone, 0,0, false,false,false, 0,0, ":all:" }; add_debugging_declarative(&all); } | USE globally mistake procedure on culprits @@ -9135,7 +9365,11 @@ procedure: %empty ; cdf_listing: STAR_CBL star_cbl_opts - | EJECT + | EJECT { + if( ! dialect_ibm() ) { + yyerror("error: EJECT is not ISO syntax, requires -dialect ibm"); + } + } | SKIP1 | SKIP2 | SKIP3 @@ -9180,7 +9414,8 @@ cdf_call_convention: void parser_call2( cbl_refer_t name, cbl_refer_t returning, size_t narg, cbl_ffi_arg_t args[], cbl_label_t *except, - cbl_label_t *not_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, @@ -9208,7 +9443,7 @@ void parser_call2( cbl_refer_t name, cbl_refer_t returning, i, crv, args[i].refer.field, args[i].refer.field->name); } } - parser_call( name, returning, narg, args, except, not_except ); + parser_call( name, returning, narg, args, except, not_except, is_function ); } @@ -9343,6 +9578,7 @@ constant_index( int token ) { case HIGH_VALUE : case HIGH_VALUES : return verify_figconst(high_value_e, 4); case QUOTES : return 5; + case NULLS : return 6; } errx(EXIT_FAILURE, "%s:%d: no such constant %d", __func__, __LINE__, token); return (size_t)-1; @@ -9849,7 +10085,7 @@ stringify( refer_collection_t *inputs, stringify_src_t sources[n]; if( inputs->lists.back().marker == NULL ) { - inputs->lists.back().marker = &null_reference; + inputs->lists.back().marker = cbl_refer_t::empty(); } assert( inputs->lists.back().marker ); std::copy( inputs->lists.begin(), inputs->lists.end(), sources ); @@ -10229,7 +10465,7 @@ symbol_group_data_members( cbl_refer_t refer, bool with_filler ) { return refers; } -static int +static bool close_out_program( const char name[] ) { const cbl_label_t *prog = current.program(); assert(prog); @@ -10237,7 +10473,7 @@ close_out_program( const char name[] ) { if( 0 != strcasecmp(prog->name, name) ) { yyerrorv( "END PROGRAM '%s' does not match PROGRAM-ID '%s'", name, prog->name); - return -1; + return false; } } @@ -10249,7 +10485,7 @@ close_out_program( const char name[] ) { // pointer still valid because name is in symbol table ast_end_program(prog->name); - return 0; + return true; } struct expand_group : public std::list<cbl_refer_t> { @@ -10612,9 +10848,6 @@ void parser_add_declaratives( size_t n, cbl_declarative_t *declaratives) { os << "};\n" << std::endl; } -cbl_field_t * -new_temporary_imply( enum cbl_field_type_t type ); - cbl_field_t * new_literal( const literal_t& lit, enum cbl_field_attr_t attr ) { cbl_field_t *field = new_temporary_imply(FldLiteral); diff --git a/gcc/cobol/parse_ante.h b/gcc/cobol/parse_ante.h index 542f95c9e306f3235a56e5e391b2bb84d4287a41..fe83e582df5f1a9b7a48d9b774092b5d6a14fb22 100644 --- a/gcc/cobol/parse_ante.h +++ b/gcc/cobol/parse_ante.h @@ -1143,23 +1143,36 @@ struct ffi_args_t { list<cbl_ffi_arg_t> elems; ffi_args_t( cbl_ffi_arg_t *arg ) { - elems.push_back(*arg); - delete arg; + this->push_back(arg); } + // set explicitly, or assume ffi_args_t * push_back( cbl_ffi_arg_t *arg ) { + if( arg->crv == cbl_ffi_crv_t(0) ) { + arg->crv = elems.empty()? by_reference_e : elems.back().crv; + } elems.push_back(*arg); delete arg; return this; } + // infer reference/content/value from previous ffi_args_t * push_back( cbl_refer_t* refer, cbl_ffi_arg_attr_t attr = none_of_e ) { - assert(!elems.empty()); - cbl_ffi_arg_t arg( elems.back().crv, refer, attr ); + cbl_ffi_crv_t crv = elems.empty()? by_reference_e : elems.back().crv; + cbl_ffi_arg_t arg( crv, refer, attr ); elems.push_back(arg); return this; } + void dump() const { + int i=0; + for( const auto& arg : elems ) { + warnx( "%8d) %-10s %-16s %s", i++, + cbl_ffi_crv_str(arg.crv), + 3 + cbl_field_type_str(arg.refer.field->type), + arg.refer.field->pretty_name() ); + } + } }; struct relop_abbr_t { @@ -1252,9 +1265,17 @@ class prog_descr_t { std::set<std::string> call_targets, subprograms; public: std::set<intrinsic_args_t> function_repository; + std::set<size_t> udfs; size_t program_index, declaratives_index; cbl_label_t *declaratives_eval; 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 ) namcpy(this->name, name); + } + } locale; cbl_call_convention_t call_convention; cbl_options_t options; @@ -1365,7 +1386,7 @@ static class current_t { yyerrorv("not implemented: Global declarative %s for %s", eval->name, name); parser_call( new_literal(name, quoted_e), - cbl_refer_t(), 0, NULL, NULL, NULL ); + cbl_refer_t(), 0, NULL, NULL, NULL, false ); } } @@ -1429,6 +1450,10 @@ static class current_t { void repository_add_all(); bool repository_add( const char name[] ); int repository_in( const char name[] ); + bool repository_udf( size_t isym ) { + auto result = programs.top().udfs.insert(isym); + return result.second; + } size_t declarative_section() const { return section; @@ -1478,13 +1503,32 @@ static class current_t { return programs.top().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 new_program ( cbl_label_type_t type, const char name[], const char os_name[], bool common, bool initial ) { size_t parent = programs.empty()? 0 : programs.top().program_index; cbl_label_t label = { - type, parent, yylineno, common, initial, false, 0, "", os_name + type, parent, yylineno, common, initial, false, 0,0, "", os_name }; if( !namcpy(label.name, name) ) { assert(false); return false; } @@ -1655,7 +1699,7 @@ static class current_t { parser_entry_activate( iprog, eval ); auto name = cbl_label_of(symbol_at(iprog))->name; parser_call( new_literal(name, quoted_e), - cbl_refer_t(), 0, NULL, NULL, NULL ); + cbl_refer_t(), 0, NULL, NULL, NULL, false ); } } } @@ -1793,15 +1837,24 @@ new_tempnumeric(void) { return new_temporary(FldNumericBin5); } static inline cbl_field_t * new_tempnumeric_float(void) { return new_temporary(FldFloat); } +static inline cbl_field_t * +new_temporary_clone( const cbl_field_t *orig) { + cbl_field_type_t type = is_literal(orig)? FldAlphanumeric : orig->type; + auto f = new_temporary_imply(type); + f->data = orig->data; + if( f->type == FldNumericBin5 ) f->type = orig->type; + f->attr = temporary_e; + + parser_symbol_add(f); + return f; +} + uint32_t type_capacity( enum cbl_field_type_t type, uint32_t digits ); bool valid_picture( enum cbl_field_type_t type, const char picture[] ); -bool -valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ); - bool move_corresponding( cbl_refer_t& tgt, cbl_refer_t& src ); @@ -1907,6 +1960,7 @@ intrinsic_call_1( cbl_field_t *output, int token, cbl_refer_t *r1 ) { yyerrorv("syntax error: invalid parameter '%s'", bad->name); return false; } + const char *name = intrinsic_cname(token); if( !name ) return false; parser_intrinsic_call_1( output, name, *r1 ); @@ -2112,12 +2166,17 @@ valid_redefine( const cbl_field_t *field, const cbl_field_t *orig ) { */ if( field->type != FldGroup && orig->type != FldGroup ) { if( orig->size() < field->size() ) { - if( orig->level > 1 || has_field_attr(orig->attr, external_e) ) + if( orig->level > 1 || has_field_attr(orig->attr, external_e) ) { + if( yydebug ) { + yyerrorv( "size error orig: %s", field_str(orig) ); + yyerrorv( "size error redef: %s", field_str(field) ); + } yyerrorv( "error: %s (%s size %u) larger than REDEFINES %s (%s size %u)", field->name, 3 + cbl_field_type_str(field->type), field->size(), orig->name, 3 + cbl_field_type_str(orig->type), orig->size() ); + } } } @@ -2480,6 +2539,7 @@ parser_move_carefully( const char */*F*/, int /*L*/, static void ast_set_pointers( const list<cbl_num_result_t>& tgts, cbl_refer_t src ) { assert(!tgts.empty()); + assert(src.field); size_t nptr = tgts.size(); cbl_refer_t ptrs[nptr]; @@ -2567,6 +2627,14 @@ procedure_division_ready( cbl_field_t *returning, ffi_args_t *ffi_args ) { auto prog = cbl_label_of(symbols_begin(current.program_index())); + if( prog->type == LblFunction ) { + if( ! returning ) { + yyerrorv("error: FUNCTION %s requires RETURNING", prog->name); + } else { + prog->returning = field_index(returning); + } + } + // Create program initialization section. We build it on an island, // that gets executed only if the program is IS INITIAL, or when the // program is the subject of a CANCEL statement. @@ -2689,10 +2757,11 @@ file_section_parent_set( cbl_field_t *field ) { void parser_call2( cbl_refer_t name, cbl_refer_t returning, size_t narg, cbl_ffi_arg_t args[], cbl_label_t *except, - cbl_label_t *not_except ); + cbl_label_t *not_except, + bool is_function ); -#define parser_call( name, returning, narg, args, except, not_except ) \ - parser_call2(name, returning, narg, args, except, not_except ) +#define parser_call( name, returning, narg, args, except, not_except, is_function ) \ + parser_call2(name, returning, narg, args, except, not_except, is_function ) cbl_field_t * ast_file_status_between( file_status_t lower, file_status_t upper ); diff --git a/gcc/cobol/parse_util.h b/gcc/cobol/parse_util.h index b814add14156fcefcb49b6e8ebb269d5c0eb771b..59093ed807a1e4890af8a07e44d30886968b86c2 100644 --- a/gcc/cobol/parse_util.h +++ b/gcc/cobol/parse_util.h @@ -113,10 +113,14 @@ static const intrinsic_args_t intrinsic_args[] = { "__gg__formatted_datetime", "XINI", FldAlphanumeric }, { FORMATTED_TIME, "FORMATTED-TIME", "__gg__formatted_time", "INI", FldNumericBin5 }, + { FRACTION_PART, "FRACTION-PART", + "__gg__fraction_part", "N", FldNumericBin5 }, { HEX_OF, "HEX-OF", "__gg__hex_of", "X", FldAlphanumeric }, { HEX_TO_CHAR, "HEX-TO-CHAR", "__gg__hex_to_char", "X", FldAlphanumeric }, + { HIGHEST_ALGEBRAIC, "HIGHEST-ALGEBRAIC", + "__gg__highest_algebraic", "N", FldNumericBin5 }, { INTEGER, "INTEGER", "__gg__integer", "N", FldNumericBin5 }, { INTEGER_OF_DATE, "INTEGER-OF-DATE", @@ -129,12 +133,24 @@ static const intrinsic_args_t intrinsic_args[] = { "__gg__integer_part", "N", FldNumericBin5 }, { LENGTH, "LENGTH", "__gg__length", "X", FldNumericBin5 }, + { LOCALE_COMPARE, "LOCALE-COMPARE", + "__gg__locale_compare", "XXX", FldNumericBin5 }, + { LOCALE_DATE, "LOCALE-DATE", + "__gg__locale_date", "XX", FldNumericBin5 }, + { LOCALE_TIME, "LOCALE-TIME", + "__gg__locale_time", "XX", FldNumericBin5 }, + { LOCALE_TIME_FROM_SECONDS, "LOCALE-TIME-FROM-SECONDS", + "__gg__locale_time_from_seconds", "NX", FldNumericBin5 }, + { LOG, "LOG", "__gg__log", "N", FldNumericBin5 }, { LOG10, "LOG10", "__gg__log10", "N", FldNumericBin5 }, { LOWER_CASE, "LOWER-CASE", "__gg__lower_case", "X", FldAlphanumeric }, + { LOWEST_ALGEBRAIC, "LOWEST-ALGEBRAIC", + "__gg__lowest_algebraic", "N", FldNumericBin5 }, + { MAX, "MAX", "__gg__max", "n", FldAlphanumeric }, { MEAN, "MEAN", @@ -192,9 +208,9 @@ static const intrinsic_args_t intrinsic_args[] = { { TEST_DAY_YYYYDDD, "TEST-DAY-YYYYDDD", "__gg__test_day_yyyyddd", "I", FldNumericBin5 }, { TEST_FORMATTED_DATETIME, "TEST-FORMATTED-DATETIME", - "__gg__test_formatted_datetime", "XX", FldAlphanumeric }, + "__gg__test_formatted_datetime", "XX", FldNumericBin5 }, { TEST_NUMVAL, "TEST-NUMVAL", - "__gg__test_numval", "X", FldAlphanumeric }, + "__gg__test_numval", "X", FldNumericBin5 }, { TEST_NUMVAL_C, "TEST-NUMVAL-C", "__gg__test_numval_c", "XXU", FldNumericBin5 }, { TEST_NUMVAL_F, "TEST-NUMVAL-F", @@ -229,20 +245,19 @@ static const intrinsic_args_t *eoargs = intrinsic_args + COUNT_OF(intrinsic_args); static const char intrinsic_unimplemented[][40] = { - "__gg__bit_of", - "__gg__bit_to_char", - "__gg__display_of", - "__gg__hex_of", - "__gg__hex_to_char", - "__gg__national_of", - "__gg__numval_f", - "__gg__test_numval_f", - "__gg__ulength", - "__gg__upos", - "__gg__usubstr", - "__gg__usupplementary", - "__gg__uvalid", - "__gg__uwidth", + "argle-bargle", // gives ::find something to chew on + // "__gg__bit_of", + // "__gg__bit_to_char", + // "__gg__display_of", + // "__gg__hex_to_char", + // "__gg__national_of", + // "__gg__test_numval_f", + // "__gg__ulength", + // "__gg__upos", + // "__gg__usubstr", + // "__gg__usupplementary", + // "__gg__uvalid", + // "__gg__uwidth", }; diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l index 39fd3e99ea4c1d18bdd0f540db5864659b2fef64..f893f51915733b731bbe4c2946ab33805d7333cc 100644 --- a/gcc/cobol/scan.l +++ b/gcc/cobol/scan.l @@ -164,6 +164,7 @@ POP_FILE \f?[#]FILE{SPC}POP\f %x para_state picture picture_count integer_count %x basis copy_state sort_state %x cdf_state bool_state hex_state subscripts numstr_state exception +%x datetime_fmt %option debug noyywrap stack yylineno case-insensitive %% @@ -330,6 +331,8 @@ LC_MONETARY { return LC_MONETARY_kw; } LC_NUMERIC { return LC_NUMERIC_kw; } LC_TIME { return LC_TIME_kw; } LENGTH { return LENGTH; } +LENGTH{SPC}OF { return LENGTH_OF; } +LOCALE { return LOCALE; } LOWLIGHT { return LOWLIGHT; } NEAREST-AWAY-FROM-ZERO { return NEAREST_AWAY_FROM_ZERO; } NEAREST-EVEN { return NEAREST_EVEN; } @@ -799,6 +802,7 @@ AREAS { return AREAS; } AREA { return AREA; } ARE { return ARE; } APPLY { return APPLY; } +ANYCASE { return ANYCASE; } ANY { return ANY; } ALTERNATE { return ALTERNATE; } @@ -901,6 +905,7 @@ USE([[:space:]]+FOR)? { return USE; } ANY { return ANY; } LENGTH { return LENGTH; } + LENGTH{SPC}OF { return LENGTH_OF; } BASED { return BASED; } USAGE { return USAGE; } COMP(UTATIONAL)?-5 { return ucomputable(FldNumericBin5, 0); } @@ -984,9 +989,10 @@ USE([[:space:]]+FOR)? { return USE; } CONSTANT { return CONSTANT; } CONTAINS { return CONTAINS; } DATA { return DATA; } - DEPENDING { return DEPENDING; } + DEPENDING { return DEPENDING; } DESCENDING { return DESCENDING; } DISPLAY { return DISPLAY; } + EJECT { return EJECT; } EXTERNAL { return EXTERNAL; } FALSE { return FALSE_kw; } FROM { return FROM; } @@ -1178,16 +1184,6 @@ USE([[:space:]]+FOR)? { return USE; } } <quoted2>{ - {DATETIME_FMT}[""] { yylval.string = strdup(yytext); - yylval.string[yyleng-1] = '\0'; - pop_return DATETIME_FMT; } - {DATE_FMT}[""] { yylval.string = strdup(yytext); - yylval.string[yyleng-1] = '\0'; - pop_return DATE_FMT; } - {TIME_FMT}[""] { yylval.string = strdup(yytext); - yylval.string[yyleng-1] = '\0'; - pop_return TIME_FMT; } - {STRING}$ { tmpstring_append(yyleng); } ^-[ ]{4,}[""]/.+ /* ignore continuation mark */ {STRING}?[""]{2} { tmpstring_append(yyleng - 1); } @@ -1204,15 +1200,6 @@ USE([[:space:]]+FOR)? { return USE; } } <quoted1>{ - {DATETIME_FMT}[''] { yylval.string = strdup(yytext); - yylval.string[yyleng-1] = '\0'; - pop_return DATETIME_FMT; } - {DATE_FMT}[''] { yylval.string = strdup(yytext); - yylval.string[yyleng-1] = '\0'; - pop_return DATE_FMT; } - {TIME_FMT}[''] { yylval.string = strdup(yytext); - yylval.string[yyleng-1] = '\0'; - pop_return TIME_FMT; } {STRING1}$ { tmpstring_append(yyleng); } ^-[ ]{4,}['']/.+ /* ignore continuation mark */ {STRING1}?['']{2} { tmpstring_append(yyleng - 1); } @@ -1502,106 +1489,157 @@ USE([[:space:]]+FOR)? { return USE; } } } +<datetime_fmt>{ + [(] { return *yytext; } + + ['']{DATETIME_FMT}[''] { yylval.string = strdup(yytext + 1); + yylval.string[yyleng-2] = '\0'; + pop_return DATETIME_FMT; } + [""]{DATETIME_FMT}[""] { yylval.string = strdup(yytext + 1); + yylval.string[yyleng-2] = '\0'; + pop_return DATETIME_FMT; } + + ['']{DATE_FMT}[''] { yylval.string = strdup(yytext + 1); + yylval.string[yyleng-2] = '\0'; + pop_return DATE_FMT; } + [""]{DATE_FMT}[""] { yylval.string = strdup(yytext + 1); + yylval.string[yyleng-2] = '\0'; + pop_return DATE_FMT; } + + ['']{TIME_FMT}[''] { yylval.string = strdup(yytext + 1); + yylval.string[yyleng-2] = '\0'; + pop_return TIME_FMT; } + [""]{TIME_FMT}[""] { yylval.string = strdup(yytext + 1); + yylval.string[yyleng-2] = '\0'; + pop_return TIME_FMT; } + + {SPC} // ignore + . { return NO_CONDITION; } +} + <function>{ - ABS { pop_return ABS; } - ACOS { pop_return ACOS; } - ANNUITY { pop_return ANNUITY; } - ASIN { pop_return ASIN; } - ATAN { pop_return ATAN; } - BIT-OF { pop_return BIT_OF; } - BIT-TO-CHAR { pop_return BIT_TO_CHAR; } - BYTE-LENGTH { pop_return BYTE_LENGTH; } - CHAR { pop_return CHAR; } - COMBINED-DATETIME { pop_return COMBINED_DATETIME; } - CONCAT { pop_return CONCAT; } - CONTENT-LENGTH { pop_return NO_CONDITION; /* GNU only*/ } - CONTENT-OF { pop_return NO_CONDITION; /* GNU only*/ } - COS { pop_return COS; } - CURRENCY-SYBOL { pop_return NO_CONDITION; /* GNU only*/ } - CURRENT-DATE { pop_return CURRENT_DATE; } - DATE-OF-INTEGER { pop_return DATE_OF_INTEGER; } - DATE-TO-YYYYMMDD { pop_return DATE_TO_YYYYMMDD; } - DAY-OF-INTEGER { pop_return DAY_OF_INTEGER; } - DAY-TO-YYYYDDD { pop_return DAY_TO_YYYYDDD; } - DISPLAY-OF { pop_return DISPLAY_OF; } - E { pop_return E; } - - EXCEPTION-FILE-N { pop_return EXCEPTION_FILE_N; } - EXCEPTION-FILE { pop_return EXCEPTION_FILE; } - EXCEPTION-LOCATION-N { pop_return EXCEPTION_LOCATION_N; } - EXCEPTION-LOCATION { pop_return EXCEPTION_LOCATION; } - EXCEPTION-STATEMENT { pop_return EXCEPTION_STATEMENT; } - EXCEPTION-STATUS { pop_return EXCEPTION_STATUS; } - - EXP { pop_return EXP; } - EXP10 { pop_return EXP10; } - FACTORIAL { pop_return FACTORIAL; } - - FORMATTED-CURRENT-DATE { pop_return FORMATTED_CURRENT_DATE; } - FORMATTED-DATE { pop_return FORMATTED_DATE; } - FORMATTED-DATETIME { pop_return FORMATTED_DATETIME; } - FORMATTED-TIME { pop_return FORMATTED_TIME; } - - HEX-OF { pop_return HEX_OF; } - HEX-TO-CHAR { pop_return HEX_TO_CHAR; } - INTEGER { pop_return INTEGER; } - INTEGER-OF-DATE { pop_return INTEGER_OF_DATE; } - INTEGER-OF-DAY { pop_return INTEGER_OF_DAY; } - INTEGER-OF-FORMATTED-DATE { pop_return INTEGER_OF_FORMATTED_DATE; } - INTEGER-PART { pop_return INTEGER_PART; } - LENGTH { pop_return LENGTH; } - LOG { pop_return LOG; } - LOG10 { pop_return LOG10; } - LOWER-CASE { pop_return LOWER_CASE; } - MAX { pop_return MAX; } - MEAN { pop_return MEAN; } - MEDIAN { pop_return MEDIAN; } - MIDRANGE { pop_return MIDRANGE; } - MIN { pop_return MIN; } - MOD { pop_return MOD; } - NATIONAL-OF { pop_return NATIONAL_OF; } - NUMVAL { pop_return NUMVAL; } - NUMVAL-C { pop_return NUMVAL_C; } - NUMVAL-F { pop_return NUMVAL_F; } - ORD { pop_return ORD; } - ORD-MAX { pop_return ORD_MAX; } - ORD-MIN { pop_return ORD_MIN; } - PI { pop_return PI; } - PRESENT-VALUE { pop_return PRESENT_VALUE; } - - RANDOM{OSPC}{PARENS} { pop_return RANDOM; } - RANDOM{OSPC}[(] { pop_return RANDOM_SEED; } - RANDOM { pop_return RANDOM; } - - RANGE { pop_return RANGE; } - REM { pop_return REM; } - REVERSE { pop_return REVERSE; } - SECONDS-FROM-FORMATTED-TIME { pop_return SECONDS_FROM_FORMATTED_TIME; } - SECONDS-PAST-MIDNIGHT { pop_return SECONDS_PAST_MIDNIGHT; } - SIGN { pop_return SIGN; } - SIN { pop_return SIN; } - SQRT { pop_return SQRT; } - STANDARD-DEVIATION { pop_return STANDARD_DEVIATION; } - SUM { pop_return SUM; } - TAN { pop_return TAN; } - TEST-DATE-YYYYMMDD { pop_return TEST_DATE_YYYYMMDD; } - TEST-DAY-YYYYDDD { pop_return TEST_DAY_YYYYDDD; } - TEST-FORMATTED-DATETIME { pop_return TEST_FORMATTED_DATETIME; } - TEST-NUMVAL { pop_return TEST_NUMVAL; } - TEST-NUMVAL-C { pop_return TEST_NUMVAL_C; } - TEST-NUMVAL-F { pop_return TEST_NUMVAL_F; } - TRIM { pop_return TRIM; } - ULENGTH { pop_return ULENGTH; } - UPOS { pop_return UPOS; } - UPPER-CASE { pop_return UPPER_CASE; } - USUBSTR { pop_return USUBSTR; } - USUPPLEMENTARY { pop_return USUPPLEMENTARY; } - UUID4 { pop_return UUID4; } - UVALID { pop_return UVALID; } - UWIDTH { pop_return UWIDTH; } - VARIANCE { pop_return VARIANCE; } - WHEN-COMPILED { pop_return WHEN_COMPILED; } - YEAR-TO-YYYY { pop_return YEAR_TO_YYYY; } + 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; } + BIT-OF{OSPC}/[(]? { pop_return BIT_OF; } + BIT-TO-CHAR{OSPC}/[(]? { pop_return BIT_TO_CHAR; } + BYTE-LENGTH{OSPC}/[(]? { pop_return BYTE_LENGTH; } + 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*/ } + 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; } + + 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-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 MAX; } + MEAN{OSPC}/[(]? { pop_return MEAN; } + MEDIAN{OSPC}/[(]? { pop_return MEDIAN; } + MIDRANGE{OSPC}/[(]? { pop_return MIDRANGE; } + MIN{OSPC}/[(]? { pop_return MIN; } + MOD{OSPC}/[(]? { pop_return MOD; } + 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; } + + 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); + return SECONDS_FROM_FORMATTED_TIME; } + SECONDS-PAST-MIDNIGHT{OSPC}/[(]? { pop_return SECONDS_PAST_MIDNIGHT; } + SIGN{OSPC}/[(]? { pop_return SIGN; } + SIN{OSPC}/[(]? { pop_return SIN; } + SQRT{OSPC}/[(]? { pop_return SQRT; } + 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; } + + {NAME}{OSPC}/[(] { /* If /{OSPC}, "dangerous trailing context" "*/ + auto& token(yylval.number); + auto name = null_trim(strdup(yytext)); + if( 0 != (token = symbol_function_token(name)) ) { + pop_return FUNCTION_UDF; + } + pop_return NO_CONDITION; + } + + {NAME} { auto token = typed_name(yytext); + pop_return (token == FUNCTION_UDF_0? token : NO_CONDITION); + } } /* @@ -1639,6 +1677,10 @@ COPY { ydflval.string = yylval.string = strdup(yytext); int token = keyword_tok(null_trim(yylval.string)); if( token ) return token; + if( 0 != (token = symbol_function_token(yylval.string)) ) { + yylval.number = token; + return FUNCTION_UDF; + } token = typed_name(yylval.string); switch(token) { case NAME: diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h index 08c852dc254b21c2e870dfa5d3e8685a4c29a360..96d5883f4aeb11a06d9cd61fa857674688e88dc4 100644 --- a/gcc/cobol/scan_ante.h +++ b/gcc/cobol/scan_ante.h @@ -403,6 +403,13 @@ bool need_nume_set( bool tf ) { return need_nume = tf; } +static int datetime_format_of( const char input[] ); + +static int symbol_function_token( const char name[] ) { + auto e = symbol_function( 0, name ); + return e ? symbol_index(e) : 0; +} + static int typed_name( const char name[] ) { if( 0 == PROGRAM ) return NAME; @@ -414,12 +421,32 @@ typed_name( const char name[] ) { struct symbol_elem_t *e = symbol_special( PROGRAM, name ); if( e ) return cbl_special_name_of(e)->token; + e = symbol_function( 0, name ); + if( e ) { + auto L = cbl_label_of(e); + assert( L->type == LblFunction ); + yylval.number = symbol_index(e); + return FUNCTION_UDF_0; + } + e = symbol_field( PROGRAM, 0, name ); + auto type = e && e->type == SymField? cbl_field_of(e)->type : FldInvalid; switch(type) { case FldLiteral: case FldLiteralA: + { + auto f = cbl_field_of(e); + if( is_constant(f) ) { + int token = datetime_format_of(f->data.initial); + if( token ) { + yylval.string = strdup(f->data.initial); + return token; + } + } + } + __attribute__((fallthrough)); case FldLiteralN: { auto f = cbl_field_of(e); diff --git a/gcc/cobol/scan_post.h b/gcc/cobol/scan_post.h index 44b39195e124d693523f792994854f922bcd08f7..9c15eff76a7224036c679eb50020fa54e8dbc8d7 100644 --- a/gcc/cobol/scan_post.h +++ b/gcc/cobol/scan_post.h @@ -28,6 +28,82 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* + * Match datetime constants. + * + * A 78 or CONSTANT could have a special literal for formatted + * date/time functions. + */ + +static int +datetime_format_of( const char input[] ) { + + static const char date_fmt_b[] = "YYYYMMDD|YYYYDDD|YYYYWwwD"; + static const char date_fmt_e[] = "YYYY-MM-DD|YYYY-DDD|YYYY-Www-D"; + + static const char time_fmt_b[] = + "hhmmss([.,]s+)?|hhmmss([.,]s+)?Z|hhmmss([.,]s+)?[+]hhmm|"; + static const char time_fmt_e[] = + "hh:mm:ss([.,]s+)?|hh:mm:ss([.,]s+)?Z|hh:mm:ss([.,]s+)?[+]hh:mm"; + + static char date_pattern[ 3 * sizeof(date_fmt_e) ]; + static char time_pattern[ 3 * sizeof(time_fmt_e) ]; + static char datetime_pattern[ 6 * sizeof(time_fmt_e) ]; + + static struct pattern_t { + regex_t re; + const char *regex; + int token; + } patterns[] = { + { {}, datetime_pattern, DATETIME_FMT }, + { {}, date_pattern, DATE_FMT }, + { {}, time_pattern, TIME_FMT }, + }, * eopatterns = patterns + COUNT_OF(patterns);; + + // compile patterns + if( ! date_pattern[0] ) { + sprintf(date_pattern, "%s|%s", date_fmt_b, date_fmt_e); + sprintf(time_pattern, "%s|%s", time_fmt_b, time_fmt_e); + + sprintf(datetime_pattern, "(%sT%s)|(%sT%s)", + date_fmt_b, time_fmt_b, + date_fmt_e, time_fmt_e); + + 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)) ) { + regerror(erc, &p->re, msg, sizeof(msg)); + warnx("%s:%d: %s: %s", __func__, __LINE__, keyword_str(p->token), msg); + } + } + } + + // applies only in the datetime_fmt start condition + if( datetime_fmt == YY_START ) { + yy_pop_state(); + if( input == NULL ) return 0; + + // See if the input is a date, time, or datetime pattern string. + static const int nmatch = 3; + regmatch_t matches[nmatch]; + + auto p = std::find_if( patterns, eopatterns, + [input, &matches]( auto& pattern ) { + auto erc = regexec( &pattern.re, input, + COUNT_OF(matches), matches, 0 ); + return erc == 0; + } ); + + return p != eopatterns? p->token : 0; + } + return 0; +} + + /* * >>DEFINE, >>IF, and >>EVALUATE */ @@ -201,10 +277,10 @@ yylex(void) { token = prelex(); if( yy_flex_debug ) { if( lexing.parser == ydfparse ) { - warnx( "%s%d: routing %s to CDF parser", __func__, __LINE__, + warnx( "%s:%d: routing %s to CDF parser", __func__, __LINE__, keyword_str(token) ); } else if( !lexing.on() ) { - warnx( "%s%d: eating %s because conditional compilatiion is FALSE", + warnx( "%s:%d: eating %s because conditional compilatiion is FALSE", __func__, __LINE__, keyword_str(token) ); } } diff --git a/gcc/cobol/show_parse.h b/gcc/cobol/show_parse.h index 911890cffb13372cee3810da2e66830a1f21afe9..12e030307b8b215a66c04a113fa71c21361d50a8 100644 --- a/gcc/cobol/show_parse.h +++ b/gcc/cobol/show_parse.h @@ -207,6 +207,11 @@ extern bool cursor_at_sol; parser_display_internal_field(trace_handle, field, false); \ gg_fprintf(trace_handle, 1, "\" %s", gg_string_literal(b)); \ } \ + else if( field->type == FldLiteral ) \ + { \ + gg_fprintf(trace_handle, 1, "%s [", gg_string_literal(a)); \ + gg_fprintf(trace_handle, 1, "] %s", gg_string_literal(b)); \ + } \ else \ { \ gg_fprintf(trace_handle, 1, "%s [", gg_string_literal(a)); \ diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc index ea008106c2d0c86ad52a4d1f501504ffc6a0e77f..ff5c0b507ed100a9324f4fe4e75bb83991a65a40 100644 --- a/gcc/cobol/symbols.cc +++ b/gcc/cobol/symbols.cc @@ -181,6 +181,12 @@ size_t file_status_register() { return symbols.registers.file_status; } size_t return_code_register() { return symbols.registers.return_code; } size_t ec_register() { return symbols.registers.exception_condition; } +cbl_refer_t * +cbl_refer_t::empty() { + static cbl_refer_t empty; + return ∅ +} + cbl_ffi_arg_t:: cbl_ffi_arg_t( cbl_refer_t* refer, cbl_ffi_arg_attr_t attr ) : optional(false) @@ -188,7 +194,7 @@ cbl_ffi_arg_t( cbl_refer_t* refer, cbl_ffi_arg_attr_t attr ) , attr(attr) , refer(refer? *refer : cbl_refer_t()) { - if( refer ) delete refer; + if( refer && refer != refer->empty() ) delete refer; } cbl_ffi_arg_t:: @@ -199,7 +205,40 @@ cbl_ffi_arg_t( cbl_ffi_crv_t crv, , attr(attr) , refer(refer? *refer : cbl_refer_t()) { - if( refer ) delete refer; + if( refer && refer != refer->empty() ) delete refer; +} + +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 ) { + yyerrorv("logic error: %s does not define RETURNING", L->name); + return NULL; + } + size_t isym = function + 1; + for( auto arg : args ) { + auto e = symbol_at(++isym); // skip over linkage_sect_e, which appears after the function + if( e->type != SymField ) { + yyerrorv( "error: FUNCTION %s has no defined parameter matching arg %zu, '%s'", + L->name, isym - function, arg.field->name ); + return NULL; + } + + auto tgt = cbl_field_of(e); + + if( ! has_field_attr(tgt->attr, linkage_e) ) if( e->type != SymField ) { + yyerrorv( "error: FUNCTION %s has no LINKAGE parameter matching arg %zu, '%s'", + L->name, isym - function, arg.field->name ); + return NULL; + } + if( ! valid_move(tgt, arg.field) ) { + yyerrorv( "error: FUNCTION %s arg %zu, '%s' cannot be passed to %s, type %s", + L->name, isym - function, arg.field->pretty_name(), + tgt->pretty_name(), 3 + cbl_field_type_str(tgt->type) ); + return NULL; + } + } + return cbl_field_of(symbol_at(L->returning)); } /* @@ -673,6 +712,44 @@ symbol_program( size_t parent, const char name[] ) return e; } +extern int yydebug; + +static size_t +symbols_dump( size_t first, bool header ); + +struct symbol_elem_t * +symbol_function( size_t parent, const char name[] ) +{ + // TODO: not sure of rules about contained functions + auto p = std::find_if( symbols_begin(), symbols_end(), + [parent, name]( const auto& elem ) { + if( elem.type == SymLabel ) { + auto L = cbl_label_of(&elem); + if( L->type == LblFunction ) { + return 0 == strcasecmp(L->name, name); + } + } + return false; + } ); + if( yydebug && p == symbols_end() ) symbols_dump( symbols.first_program, true); + + return p == symbols_end()? NULL : p; + + cbl_label_t label = {}; + label.type = LblFunction; + label.parent = parent; + assert(strlen(name) < sizeof label.name); + strcpy(label.name, name); + + struct symbol_elem_t key = { SymLabel, 0, { NULL } }, *e; + key.elem.label = label; + + 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_special( size_t program, const char name[] ) { @@ -999,8 +1076,6 @@ name88_find( cbl_name_t name, cbl_field_t *field ) { static inline bool is_index( const cbl_field_type_t type ) { return type == FldIndex; } -extern int yydebug; - static size_t symbols_dump( size_t first, bool header ) { size_t ninvalid = 0; @@ -1087,6 +1162,42 @@ symbols_dump( size_t first, bool header ) { return ninvalid; } +static bool +grow_redefined_group( cbl_field_t *redefined, const cbl_field_t *field ) { + assert(redefined); + assert(field); + assert(redefined == symbol_redefines(field)); + + /* + * When this function is called, redefined elementary items are + * already resized, if eligible. + */ + if( redefined->type != FldGroup ) return false; + + /* + * 8) The storage area required for the subject of the entry + * shall not be larger than the storage area required for the + * data item referenced by data-name-2, unless the data item + * referenced by data- name-2 has been specified with level + * number 1 and without the EXTERNAL clause. + */ + if( 1 < redefined->level ) { + if( field_memsize(redefined) < field_memsize(field) ) { + yyerrorv("error: line %d: %s (size %u) larger than REDEFINES %s (size %u)", + field->line, + field->name, field_memsize(field), + redefined->name, field_memsize(redefined)); + return false; + } + } + + redefined->data.memsize = std::max(field_memsize(redefined), + field_memsize(field)); + + return true; +} + + /* * Input is a symbol-table element, always a field. * For elementary fields, return the input. @@ -1182,6 +1293,7 @@ static struct symbol_elem_t * e--; // set e to last symbol processed (not next one, because ++e) } +#if 0 cbl_field_t *redefined = symbol_redefines(field); /* @@ -1191,8 +1303,8 @@ static struct symbol_elem_t * * referenced by data- name-2 has been specified with level * number 1 and without the EXTERNAL clause. */ - if( redefined && redefined->type == FldGroup && redefined->level > 1 ) { - if( field->type != FldGroup && redefined->type != FldGroup ) { + if( redefined ) { + if( redefined->type == FldGroup && redefined->level > 1 ) { if( field_memsize(redefined) < field_memsize(field) ) { yyerrorv("error: line %d: %s (size %u) larger than REDEFINES %s (size %u)", field->line, @@ -1200,24 +1312,24 @@ static struct symbol_elem_t * redefined->name, field_memsize(redefined)); } } - } + + if( redefined != group) { + if( group->our_index != redefined->parent ) { + if( yydebug ) yyerrorv("%s:%d: our index %zu != redefined parent %zu", + __func__, __LINE__, group->our_index, redefined->parent); + continue; + } - if( redefined && redefined != group) { - if( group->our_index != redefined->parent ) { - if( yydebug ) yyerrorv("%s:%d: our index %zu != redefined parent %zu", - __func__, __LINE__, group->our_index, redefined->parent); + redefined->data.memsize = std::max(field_memsize(redefined), + field_memsize(field)); + field->data.memsize = 0; + if( redefined->data.memsize == redefined->data.capacity ) { + redefined->data.memsize = 0; + } continue; } - - redefined->data.memsize = std::max(field_memsize(redefined), - field_memsize(field)); - field->data.memsize = 0; - if( redefined->data.memsize == redefined->data.capacity ) { - redefined->data.memsize = 0; - } - continue; } - +#endif members.push_back(field); } @@ -1235,9 +1347,16 @@ static struct symbol_elem_t * for( auto field : members ) { cbl_field_t *redefined = symbol_redefines(field); if( redefined ) { - assert( group == redefined ); + if( group != redefined ) { + grow_redefined_group(redefined, field); + } max_memsize = std::max(max_memsize, field_memsize(field)); + field->data.memsize = 0; + + if( redefined->data.memsize == redefined->data.capacity ) { + redefined->data.memsize = 0; + } continue; } group->data.capacity += field_size(field); @@ -1253,7 +1372,9 @@ static struct symbol_elem_t * if( group->data.memsize == group->data.capacity ) group->data.memsize = 0; if( 0 < group->data.memsize && group->data.memsize < group->data.capacity ) { - warnx( "%s:%d: small capacity?\n\t%s", __func__, __LINE__, field_str(group) ); + if( yydebug ) { + warnx( "%s:%d: small capacity?\n\t%s", __func__, __LINE__, field_str(group) ); + } group->data.memsize = group->data.capacity; } @@ -2125,7 +2246,7 @@ symbol_add( struct symbol_elem_t *elem ) symbol_elem_cmp ) ); assert(symbols.nelem > 1); - if( p->type == SymLabel && p->elem.label.type == LblProgram ) { + if( is_program(*p) ) { assert(p->program == 0 || p->elem.label.os_name != NULL); p->program = p - symbols.elems; } @@ -2779,11 +2900,11 @@ new_temporary_impl( enum cbl_field_type_t type ) struct cbl_field_t *f = new cbl_field_t; switch(type) { + case FldGroup: case FldAlphanumeric: *f = empty_alpha; break; case FldInvalid: - case FldGroup: case FldClass: case FldForward: case FldIndex: @@ -2945,7 +3066,7 @@ symbol_program_programs() { for( const auto& elem : common_callables ) { if( elem.first == 0 ) continue; assert(symbol_at(elem.first)->type == SymLabel); - assert(cbl_label_of(symbol_at(elem.first))->type == LblProgram); + assert(is_program(*symbol_at(elem.first))); // might be a function programs.insert(elem.first); } return programs; diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h index 75e29d696acf00dad9f1193f07771536dd9a1fbf..bddd5f3154f573d5a048052b099d37ddcfae9d6e 100644 --- a/gcc/cobol/symbols.h +++ b/gcc/cobol/symbols.h @@ -189,18 +189,22 @@ is_numeric( cbl_field_type_t type ) { case FldClass: case FldConditional: case FldForward: - case FldIndex: case FldSwitch: case FldDisplay: - case FldPointer: case FldBlob: return false; + // Dubner's definition of is_numeric 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 FldNumericDisplay: case FldNumericBinary: case FldFloat: case FldPacked: case FldNumericBin5: case FldLiteralN: + case FldPointer: + case FldIndex: return true; } warnx( "%s:%d: invalid symbol_type_t %d", __func__, __LINE__, type ); @@ -553,8 +557,15 @@ struct cbl_field_t { data.capacity; } uint32_t size() const; // table capacity or capacity + + const char * pretty_name() const { + if( name[0] == '_' && data.initial ) return data.initial; + return name; + } }; +bool valid_move( const struct cbl_field_t *tgt, const struct cbl_field_t *src ); + #define record_area_name_stem "_ra_" static inline bool @@ -798,6 +809,8 @@ struct cbl_refer_t { std::copy(subscripts, subscripts + nsubscript, this->subscripts); } + static cbl_refer_t *empty(); + cbl_refer_t * name( const char name[] ) { assert(name); assert(strlen(name) < sizeof(field->name)); @@ -811,7 +824,6 @@ 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_refmod_reference() const { return refmod.is_active(); } @@ -832,6 +844,21 @@ struct cbl_refer_t { } }; +struct cbl_substitute_t { + enum subst_fl_t { subst_all_e, subst_first_e = 'F', subst_last_e = 'L'}; + bool anycase; + 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 ) + : anycase(anycase) + , first_last(subst_fl_t(first_last)) + , orig( orig? *orig : cbl_refer_t() ) + , replacement( replacement? *replacement : cbl_refer_t() ) + {} +}; + static inline const char * field_name( const cbl_field_t *f ) { return f? f->name : "(void)"; } @@ -864,7 +891,19 @@ struct cbl_num_result_t { * CALL */ enum cbl_ffi_arg_attr_t { none_of_e, address_of_e, length_of_e }; -enum cbl_ffi_crv_t { by_reference_e, by_content_e, by_value_e }; +enum cbl_ffi_crv_t { by_reference_e = 'R', by_content_e = 'C', by_value_e = 'E' }; + +static inline const char * +cbl_ffi_crv_str( cbl_ffi_crv_t crv ) { + switch (crv) { + case by_reference_e: return "REFERENCE"; + case by_content_e: return "CONTENT"; + case by_value_e: return "VALUE"; + } + if( crv == cbl_ffi_crv_t(0) ) return "<none>"; + return "???"; +} + void parser_symbol_add( struct cbl_field_t *new_var ); void parser_local_add( struct cbl_field_t *new_var ); @@ -975,7 +1014,7 @@ struct cbl_label_t { size_t parent; int line; bool common, initial, recursive; - size_t initial_section; + size_t initial_section, returning; cbl_name_t name; const char *os_name, *mangled_name; union @@ -1074,6 +1113,8 @@ struct label_cmp_lessthan { size_t field_index( const cbl_field_t *f ); +cbl_field_t * new_temporary_imply( enum cbl_field_type_t type ); // for parser + cbl_field_t * new_temporary( enum cbl_field_type_t type ); cbl_field_t * new_literal( const char initial[], enum cbl_field_attr_t attr = none_e ); @@ -1919,6 +1960,7 @@ struct symbol_elem_t * symbol_field( size_t program, struct cbl_label_t * symbol_label( size_t program, cbl_label_type_t type, size_t section, const char name[], const char os_name[] = NULL ); +struct symbol_elem_t * symbol_function( size_t parent, const char name[] ); struct symbol_elem_t * symbol_literal( size_t program, const char name[] ); struct symbol_elem_t * symbol_literalA( size_t program, const char name[] ); @@ -2046,8 +2088,11 @@ symbol_field_alias2( struct symbol_elem_t *e, struct symbol_elem_t * 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 ); +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 ); diff --git a/gcc/cobol/symfind.cc b/gcc/cobol/symfind.cc index 178b92ff9da6c50849a0d499ed1bad436898e906..6c884ff2718c917ec5e27cdf020424f87575be09 100644 --- a/gcc/cobol/symfind.cc +++ b/gcc/cobol/symfind.cc @@ -381,7 +381,10 @@ symbol_match2( size_t program, { std::vector<size_t> fields; - for( auto e = symbols_begin(program); e != symbols_end(); e++ ) { + auto e = symbols_begin(program); + if( is_program(*e) ) e++; + + for( ; e != symbols_end(); e++ ) { if( e->program != program ) break; if( e->type != SymField ) continue; diff --git a/gcc/cobol/tests/c-to-cobol/c_sub.c b/gcc/cobol/tests/c-to-cobol/c_sub.c index 637c0dab90f3bf5dbaa250384a56b10bb0446b6b..5b2bf906bc50b9fa196f7b9c7ff143a151aa6f01 100644 --- a/gcc/cobol/tests/c-to-cobol/c_sub.c +++ b/gcc/cobol/tests/c-to-cobol/c_sub.c @@ -36,3 +36,15 @@ Routine_B() extern void routine_c(); routine_c(); } + +void +routine_128(__int128 x) + { + while(x) + { + int digit = x % 10; + x /= 10; + printf("%c", digit + '0'); + } + printf("\n"); + } \ No newline at end of file diff --git a/gcc/cobol/tests/c-to-cobol/call_stuff.cbl b/gcc/cobol/tests/c-to-cobol/call_stuff.cbl index 86dd88cd10c6f1822aca764a27368860a99ce76c..a76c2919a856fbd4e184206b239dc8932e974aef 100644 --- a/gcc/cobol/tests/c-to-cobol/call_stuff.cbl +++ b/gcc/cobol/tests/c-to-cobol/call_stuff.cbl @@ -6,14 +6,18 @@ WORKING-STORAGE SECTION. 01 CWD PIC X(100). 01 RETURNED-CWD PIC X(100). 01 LEN_OF_CWD PIC 999 VALUE 100. -01 USR-LOCAL-BIN PIC X(14) VALUE "/usr/local/bin". +01 USR-LOCAL-BIN PIC X(15) VALUE "/usr/local/bin". 01 CHDIR_RETURN PIC S999 BINARY. 01 var1 pic x(24) VALUE "I shouldn't change". 01 var2 pic x(24) VALUE "I should change". +01 var128 pic 9(30) VALUE 987654321098765432109876543210. +01 var128-2 pic 9(30) . + PROCEDURE DIVISION. + MOVE X'00' TO USR-LOCAL-BIN(15:1) CALL "chdir" USING BY CONTENT USR-LOCAL-BIN RETURNING CHDIR_RETURN @@ -78,6 +82,14 @@ PROCEDURE DIVISION. DISPLAY var1 display var2 + DISPLAY "pass 128-bit value to a C routine" + call "routine_128" USING BY VALUE var128 + + *> DISPLAY "pass 128-bit value to a COBOL routine and get it back" + *> MOVE ZERO to var128-2 + *> call "routine_128_cobol" USING BY VALUE var128 RETURNING var128-2 + *> DISPLAY "var128-2" + MOVE ZERO TO RETURN-CODE GOBACK. END PROGRAM A. @@ -142,3 +154,15 @@ move "Good: I changed!" TO par2 goback. END PROGRAM callee. + +*> IDENTIFICATION DIVISION. +*> PROGRAM-ID. routine_128_cobol. +*> DATA DIVISION. +*> LINKAGE SECTION. +*> 01 var1 pic 9(30) . +*> 01 var2 pic 9(30) . +*> PROCEDURE DIVISION USING var1 RETURNING var2. + *> DISPLAY " I am COBOL routine_128_cobol". + *> DISPLAY var1 + *> MOVE var1 TO var2 + *> END PROGRAM routine_128_cobol. diff --git a/gcc/cobol/tests/c-to-cobol/known-good.txt b/gcc/cobol/tests/c-to-cobol/known-good.txt index 5860ecfdf0c5dd2681325de6fb26767cb1617643..02a050cba432553758f02a01b6d620a85429d483 100644 --- a/gcc/cobol/tests/c-to-cobol/known-good.txt +++ b/gcc/cobol/tests/c-to-cobol/known-good.txt @@ -34,8 +34,10 @@ I should change after I shouldn't change Good: I changed! +pass 128-bit value to a C routine The C routine string_to_long got the string "123456" from the caller, and is returning the 64-bit value 123456 The C code got the long integer 654321 from the caller, and is returning the string "654321" I am C routine_b(); I will call COBOL routine_c() +012345678901234567890123456789 diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc index bbc3e7824c9d56824e10f4e149eed1800de043c0..7f94c2e8d9ccc425e8f3afc7b20b89443cf51917 100644 --- a/gcc/cobol/util.cc +++ b/gcc/cobol/util.cc @@ -752,7 +752,7 @@ redefine_field( cbl_field_t *field ) { } if( field->data.capacity == 0 ) field->data = primary->data; - + if( is_numeric(field->type) && field->usage == FldDisplay ) { fOK = symbol_field_type_update(field, FldNumericDisplay, false); } diff --git a/libgcobol/intrinsic.cc b/libgcobol/intrinsic.cc index f216927c654ae41754249dfd649caa563bb6debf..744fce55517d6e301544ca8b28582bb9b28c435e 100644 --- a/libgcobol/intrinsic.cc +++ b/libgcobol/intrinsic.cc @@ -42,6 +42,8 @@ #include <math.h> #include <algorithm> #include <cctype> +#include <langinfo.h> + #include "libgcobol.h" #include "intrinsic.h" @@ -75,93 +77,76 @@ struct cobol_tm static int is_leap_year(int); +typedef char * PCHAR; -#if 0 -static -double -iYMD_to_JD(int Y, int M, int D) +static void +trim_trailing_spaces(PCHAR left, PCHAR &right) + { + while( right > left ) { - // Calculates the Julian Day in an integer-ish way. The classic formula - // in fYMD_to_JD is inefficient because of the floating point operations: - - int JD = 2305813; // This is Jan 0, 1601 - - int extra_day = is_leap_year(Y) - 365; - - int modified_year = Y-1601; // Cycle starts are years N*400 plus 1. That - // puts 0x00 non-leap years at the end of - // centuries, and the years divisible by 400 at - // the end of cycles. - int cycle = modified_year / 400; - int year_of_cycle = modified_year % 400; - int century_of_cycle = year_of_cycle / 100; - int year_of_century = year_of_cycle % 100; - int leap_cycle = year_of_century / 4; - int year_in_leap_cycle = year_of_century % 4; - - JD += cycle * 146097; // This is Jan 0 of the first year of the cycle - JD += century_of_cycle * 36524; // This is Jan 0 of the first year of the - // century - JD += leap_cycle * 1461; // Jan 0 of the year xx01 for our year - JD += year_in_leap_cycle * 365; - - // This is the table of day numbers for day zero of each month: - static const int month_start_days[13] = {-1,0,31,59,90,120,151,181,212,243,273,304,334}; - JD += month_start_days[M]; - if(M > 2) + if( *(right-1) != internal_space ) { - JD += extra_day; + break; } - JD += D; + right -= 1; + } + } - return (double)JD - 0.5; +static bool +is_zulu_format(PCHAR left, PCHAR &right) + { + bool retval = false; + if( right > left ) + { + retval = toupper(*(right-1)) == internal_Z; } -#endif + return retval; + } static double YMD_to_JD(int Y, int M, int D) - { - // Calculates the Julian Day + { + // Calculates the Julian Day - if( M <= 2 ) - { - Y -= 1 ; - M += 12; - } - double A = floor(Y/100.); - double B = 2. - A + floor(A/4.); + if( M <= 2 ) + { + Y -= 1 ; + M += 12; + } + double A = floor(Y/100.); + double B = 2. - A + floor(A/4.); - double JD; - JD = floor(365.25 * double(Y + 4716) + floor((30.6001 * double(M+1)))) + D + B -1524.5 ; + double JD; + JD = floor(365.25 * double(Y + 4716) + floor((30.6001 * double(M+1)))) + D + B -1524.5 ; - return JD; - } + return JD; + } static void JD_to_YMD(int &YY, int &MM, int &DD, double JD) + { + JD += 0.5; + double Z = floor(JD); + double F = JD - Z; + double A; + if( Z < 2299161.0 ) { - JD += 0.5; - double Z = floor(JD); - double F = JD - Z; - double A; - if( Z < 2299161.0 ) - { - A = Z; - } - else - { - double alpha = floor( (Z-1867216.25) / 36524.25 ) ; - A = Z + 1.0 + alpha - floor(alpha/4.0); - } - double B = A + 1524; - double C = floor( (B - 122.1)/365.25 ); - double D = floor( 365.25 * C ); - double E = floor( (B-D)/30.6001 ); - - DD = (int)( B - D - floor(30.6001 * E) + F ); - MM = (int)( E < 14 ? E - 1 : E - 13 ); - YY = (int)( MM > 2 ? C - 4716 : C - 4715 ); + A = Z; } + else + { + double alpha = floor( (Z-1867216.25) / 36524.25 ) ; + A = Z + 1.0 + alpha - floor(alpha/4.0); + } + double B = A + 1524; + double C = floor( (B - 122.1)/365.25 ); + double D = floor( 365.25 * C ); + double E = floor( (B-D)/30.6001 ); + + DD = (int)( B - D - floor(30.6001 * E) + F ); + MM = (int)( E < 14 ? E - 1 : E - 13 ); + YY = (int)( MM > 2 ? C - 4716 : C - 4715 ); + } static int JD_to_DOW(double JD) @@ -177,52 +162,52 @@ JD_to_DOW(double JD) static char * timespec_to_string(char *retval, struct timespec &tp) - { - /* - Returns a 21-character string: - - 1 - 4 Four numeric digits of the year in the Gregorian calendar - 5 - 6 Two numeric digits of the month of the year, in the range 01 through 12 - 7 - 8 Two numeric digits of the day of the month, in the range 01 through 31 - 9 - 10 Two numeric digits of the hours past midnight, in the range 00 through 23 - 11 - 12 Two numeric digits of the minutes past the hour, in the range 00 through 59 - 13 - 14 Two numeric digits of the seconds past the minute, in the range 00 through 59 - 15 - 16 Two numeric digits of the hundredths of a second past the second, in the range - 17 Either the character '-' or the character '+'. - 18 - 19 If character position 17 is '-', two numeric digits are returned in the range 00 - through 12 indicating the number of hours that the reported time is behind - Greenwich mean time. - - If character position 17 is '+', two numeric digits are - returned in the range 00 through 13 indicating the number of hours that the - reported time is ahead of Greenwich mean time. If character position 17 is '0', the - value 00 is returned. - 20 - 21 Two numeric digits are returned in the range 00 through 59 indicating the number - of additional minutes that the reported time is ahead of or behind Greenwich - mean time, depending on whether character position 17 - */ - - const int size_of_buffer = DATE_STRING_BUFFER_SIZE; - const int offset_to_hundredths = 14; - const long nanoseconds_to_hundredths = 10000000; - - // Convert the nanosecond fraction to hundredths of a second: - char achCentiseconds[3]; - snprintf(achCentiseconds, 3, "%2.2ld", (tp.tv_nsec/nanoseconds_to_hundredths) ); - - // Convert the epoch seconds to broken-down time: - struct tm tm = {}; - localtime_r(&tp.tv_sec, &tm); + { + /* + Returns a 21-character string: + + 1 - 4 Four numeric digits of the year in the Gregorian calendar + 5 - 6 Two numeric digits of the month of the year, in the range 01 through 12 + 7 - 8 Two numeric digits of the day of the month, in the range 01 through 31 + 9 - 10 Two numeric digits of the hours past midnight, in the range 00 through 23 + 11 - 12 Two numeric digits of the minutes past the hour, in the range 00 through 59 + 13 - 14 Two numeric digits of the seconds past the minute, in the range 00 through 59 + 15 - 16 Two numeric digits of the hundredths of a second past the second, in the range + 17 Either the character '-' or the character '+'. + 18 - 19 If character position 17 is '-', two numeric digits are returned in the range 00 + through 12 indicating the number of hours that the reported time is behind + Greenwich mean time. + + If character position 17 is '+', two numeric digits are + returned in the range 00 through 13 indicating the number of hours that the + reported time is ahead of Greenwich mean time. If character position 17 is '0', the + value 00 is returned. + 20 - 21 Two numeric digits are returned in the range 00 through 59 indicating the number + of additional minutes that the reported time is ahead of or behind Greenwich + mean time, depending on whether character position 17 + */ + + const int size_of_buffer = DATE_STRING_BUFFER_SIZE; + const int offset_to_hundredths = 14; + const long nanoseconds_to_hundredths = 10000000; + + // Convert the nanosecond fraction to hundredths of a second: + char achCentiseconds[3]; + snprintf(achCentiseconds, 3, "%2.2ld", (tp.tv_nsec/nanoseconds_to_hundredths) ); + + // Convert the epoch seconds to broken-down time: + struct tm tm = {}; + localtime_r(&tp.tv_sec, &tm); - // Format the time as per COBOL specifications, leaving two spaces for the - // hundredths of seconds: - strftime(retval, size_of_buffer, "%Y%m%d%H%M%S %z", &tm); + // Format the time as per COBOL specifications, leaving two spaces for the + // hundredths of seconds: + strftime(retval, size_of_buffer, "%Y%m%d%H%M%S %z", &tm); - // Copy the 100ths into place: - memcpy(retval+offset_to_hundredths, achCentiseconds, 2); + // Copy the 100ths into place: + memcpy(retval+offset_to_hundredths, achCentiseconds, 2); - return retval; - } + return retval; + } static void @@ -236,133 +221,133 @@ string_to_dest(cblc_field_t *dest, const char *psz) } struct input_state - { - size_t nsubscript; - bool *subscript_alls; - size_t *subscripts; - size_t *subscript_limits; - bool done; + { + size_t nsubscript; + bool *subscript_alls; + size_t *subscripts; + size_t *subscript_limits; + bool done; - void allocate(size_t N) - { - nsubscript = N; - if(N) - { - subscript_alls = (bool *) MALLOC(nsubscript); - subscripts = (size_t *)MALLOC(nsubscript); - subscript_limits = (size_t *)MALLOC(nsubscript); - } - done = false; - } - void deallocate() - { - if(nsubscript) - { - FREE(subscript_alls); - FREE(subscripts); - FREE(subscript_limits); - } - } - }; + void allocate(size_t N) + { + nsubscript = N; + if(N) + { + subscript_alls = (bool *) MALLOC(nsubscript); + subscripts = (size_t *)MALLOC(nsubscript); + subscript_limits = (size_t *)MALLOC(nsubscript); + } + done = false; + } + void deallocate() + { + if(nsubscript) + { + FREE(subscript_alls); + FREE(subscripts); + FREE(subscript_limits); + } + } + }; struct refer_state_for_all - { - size_t nflags; - size_t coefficients [MAXIMUM_TABLE_DIMENSIONS]; - size_t capacities [MAXIMUM_TABLE_DIMENSIONS]; - size_t limits [MAXIMUM_TABLE_DIMENSIONS]; - }; + { + size_t nflags; + size_t coefficients [MAXIMUM_TABLE_DIMENSIONS]; + size_t capacities [MAXIMUM_TABLE_DIMENSIONS]; + size_t limits [MAXIMUM_TABLE_DIMENSIONS]; + }; static void build_refer_state_for_all( refer_state_for_all &state, cblc_refer_t *refer) + { + memset(&state, 0, sizeof(refer_state_for_all) ); + if( refer->all_flags ) { - memset(&state, 0, sizeof(refer_state_for_all) ); - if( refer->all_flags ) + // At this point, refer points to the very first element of + // an array specification that includes at least one ALL subscript. At + // this time, those ALLs were calculated as if they had been replaced + // with one. + + // We are going to walk the reference up to its ultimate parent, picking + // up what we need along the way. + + size_t current_bit = 1; + size_t current_index = 0; + cblc_field_t *current_sizer = refer->field; + while( current_sizer ) + { + while( current_sizer && !current_sizer->occurs_upper ) { - // At this point, refer points to the very first element of - // an array specification that includes at least one ALL subscript. At - // this time, those ALLs were calculated as if they had been replaced - // with one. + // current_sizer isn't a table, which isn't unusual. + current_sizer = current_sizer->parent; + } - // We are going to walk the reference up to its ultimate parent, picking - // up what we need along the way. + if( !current_sizer ) + { + // We have found all of the elements in this data description + // that have OCCURS clauses + break; + } - size_t current_bit = 1; - size_t current_index = 0; - cblc_field_t *current_sizer = refer->field; - while( current_sizer ) - { - while( current_sizer && !current_sizer->occurs_upper ) - { - // current_sizer isn't a table, which isn't unusual. - current_sizer = current_sizer->parent; - } - - if( !current_sizer ) - { - // We have found all of the elements in this data description - // that have OCCURS clauses - break; - } - - // We are sitting on an occurs clause: - - if( current_bit & refer->all_flags ) - { - // It is an ALL subscript: - state.nflags += 1; - state.coefficients[current_index] = 1; - state.capacities[current_index] = current_sizer->capacity; - state.limits[current_index] = current_sizer->occurs_upper; - if( current_sizer->depending_on ) - { - int rdigits; - state.limits[current_index] - = (size_t)__gg__binary_value_from_field(&rdigits, - current_sizer->depending_on); - } - current_index += 1 ; - } - - current_bit <<= 1; - current_sizer = current_sizer->parent; - } + // We are sitting on an occurs clause: + + if( current_bit & refer->all_flags ) + { + // It is an ALL subscript: + state.nflags += 1; + state.coefficients[current_index] = 1; + state.capacities[current_index] = current_sizer->capacity; + state.limits[current_index] = current_sizer->occurs_upper; + if( current_sizer->depending_on ) + { + int rdigits; + state.limits[current_index] + = (size_t)__gg__binary_value_from_field(&rdigits, + current_sizer->depending_on); + } + current_index += 1 ; } + + current_bit <<= 1; + current_sizer = current_sizer->parent; + } } + } static bool update_refer_state_for_all( refer_state_for_all &state, cblc_refer_t *refer) - { - bool retval = false; // Means there is nothing left - - for(size_t i=0; i<state.nflags; i++) - { - state.coefficients[i] += 1; - refer->qual_data += state.capacities[i]; - if( state.coefficients[i] <= state.limits[i] ) - { - // This coefficient is within range: - retval = true; - break; - } + { + bool retval = false; // Means there is nothing left - // We have used up this coefficient. + for(size_t i=0; i<state.nflags; i++) + { + state.coefficients[i] += 1; + refer->qual_data += state.capacities[i]; + if( state.coefficients[i] <= state.limits[i] ) + { + // This coefficient is within range: + retval = true; + break; + } - // Remove the effects of incrementing this coefficient: - refer->qual_data -= state.limits[i] * state.capacities[i]; - // Reset the coefficient back to one: - state.coefficients[i] = 1; + // We have used up this coefficient. - // And continue on to the next coefficient. - } + // Remove the effects of incrementing this coefficient: + refer->qual_data -= state.limits[i] * state.capacities[i]; + // Reset the coefficient back to one: + state.coefficients[i] = 1; - return retval; + // And continue on to the next coefficient. } + return retval; + } + static int year_to_yyyy(int arg1, int arg2, int arg3) @@ -399,9 +384,9 @@ get_value_as_double_from_refer(cblc_refer_t *input) default: retval = __gg__binary_value_from_refer(&rdigits, input); for(int i=0; i<rdigits; i++) - { - retval /= 10.0; - } + { + retval /= 10.0; + } break; } @@ -479,7 +464,7 @@ variance(size_t ncount, cblc_refer_t *source) refer_state_for_all state; cblc_refer_t *next_input = &source[i]; build_refer_state_for_all(state, next_input); - + for(;;) { newValue = __gg__float128_from_refer(next_input); @@ -515,7 +500,7 @@ get_all_time( char *stime, // 1111111111222222222233333333334 // 01234567890123456789012345678901234567890 // Returns YYYYMMDDThhmmss.sssssssss+hhmmWwwdDDDZZZZ - // + // // YYYY is the year // MM is the month // DD is the day of the month @@ -538,13 +523,17 @@ get_all_time( char *stime, "W%2.2u" // Www "%1u" // DOW [1-7], 1 for Monday "%3.3u" // DDD day of year, 001 - 365,366 - "%4.4u", // ZZZZ Year for YYYY-Www-D - ctm.YYYY, ctm.MM, ctm.DD, - ctm.hh, ctm.mm, ctm.ss, + "%4.4u", // ZZZZ Year for YYYY-Www-D + ctm.YYYY, + ctm.MM, + ctm.DD, + ctm.hh, + ctm.mm, + ctm.ss, ctm.nanoseconds, - ctm.tz_offset < 0 ? '-' : '+' , - abs(ctm.tz_offset) / 60 , - abs(ctm.tz_offset) % 60 , + ctm.tz_offset < 0 ? '-' : '+', + abs(ctm.tz_offset) / 60, + abs(ctm.tz_offset) % 60, ctm.week_of_year, ctm.day_of_week+1, ctm.day_of_year, @@ -553,53 +542,6 @@ get_all_time( char *stime, ascii_to_internal_str(stime, strlen(stime)); } -#if 0 -static -int -is_leap_yearx(int yyyy) - { - int days_in_year; - if( !(yyyy%4) && ((yyyy%100) || !(yyyy%400)) ) - { - days_in_year = 366; - } - else - { - days_in_year = 365; - } - -#if 0 - static bool once = true; - if( once ) - { - once = false; - - int ncount = 0; - unsigned char i; - for(int y = 1600; y<2000; y++) - { - i >>= 1; - if( is_leap_yearx(y) == 366 ) - { - i |= 0x80; - } - ncount += 1; - if((ncount & 7) == 0) - { - fprintf(stderr, "0x%2.2x, ", i); - if( ((ncount>>3) % 10 ) == 0 ) - { - fprintf(stderr, "\n"); - } - } - } - exit(1); - } -#endif - return days_in_year; - } -#endif - static int is_leap_year(int yyyy) @@ -612,14 +554,14 @@ is_leap_year(int yyyy) 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, }; - - static const unsigned char mask[8] = + + static const unsigned char mask[8] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80 }; int days_in_year; int year_in_cycle = yyyy % 400; - + if( leap_year_bits[year_in_cycle/8] & mask[year_in_cycle & 0x07] ) { days_in_year = 366; @@ -667,7 +609,7 @@ populate_ctm_from_tm(struct cobol_tm &ctm, const struct tm &tm) double JD_Jan4 = YMD_to_JD(ctm.YYYY, 1, 4); ctm.day_of_year = (int)(JD - (JD_Jan4-4)); - + int dow_Jan4 = JD_to_DOW(JD_Jan4); double adjusted_starting_date = JD_Jan4 - dow_Jan4; @@ -695,12 +637,8 @@ populate_ctm_from_tm(struct cobol_tm &ctm, const struct tm &tm) static void -populate_ctm_from_date( struct cobol_tm &ctm, cblc_refer_t *pdate ) +populate_ctm_from_JD(struct cobol_tm &ctm, double JD ) { - // Get the date as an integer - int rdigits; - double JD = (double)__gg__binary_value_from_refer(&rdigits, pdate); - // Extract the year, month, and day int Y; int M; @@ -717,12 +655,18 @@ populate_ctm_from_date( struct cobol_tm &ctm, cblc_refer_t *pdate ) static void -populate_ctm_from_time( struct cobol_tm &ctm, - cblc_refer_t *ptime, - cblc_refer_t *poffset ) +populate_ctm_from_date( struct cobol_tm &ctm, cblc_refer_t *pdate ) { - double time = get_value_as_double_from_refer(ptime); + // Get the date as an integer + int rdigits; + double JD = (double)__gg__binary_value_from_refer(&rdigits, pdate); + populate_ctm_from_JD(ctm, JD); + } +static +void +populate_ctm_from_double_time(struct cobol_tm &ctm, double time) + { // Get hours, minutes, and seconds double intpart; double fracpart = modf(time, &intpart); @@ -735,7 +679,17 @@ populate_ctm_from_time( struct cobol_tm &ctm, ctm.mm = minute; ctm.hh = hour; ctm.nanoseconds = (int)(fracpart * 1000000000 + 0.5); - + } + +static +void +populate_ctm_from_time( struct cobol_tm &ctm, + cblc_refer_t *ptime, + cblc_refer_t *poffset ) + { + double time = get_value_as_double_from_refer(ptime); + populate_ctm_from_double_time(ctm, time); + if( poffset ) { int rdigits; @@ -746,6 +700,46 @@ populate_ctm_from_time( struct cobol_tm &ctm, rdigits = 0; } ctm.tz_offset = value; + if( abs(value) >= 1440 ) + { + exception_raise(ec_argument_function_e); + } + } + else + { + ctm.tz_offset = 0; + } + } + +static void +convert_to_zulu(cobol_tm &ctm) + { + // Get the Julian Day + double JD = YMD_to_JD(ctm.YYYY, + ctm.MM, + ctm.DD); + // Get the time in seconds past midnight + double seconds_past_midnight = ctm.hh * 3600 + + ctm.mm * 60 + + ctm.ss; + // Subtract the UTC offset, which is given in minutes + seconds_past_midnight -= ctm.tz_offset * 60; + if( seconds_past_midnight < 0 ) + { + JD -= 1; + seconds_past_midnight += 86400; + } + else if( seconds_past_midnight >= 86400 ) + { + JD += 1; + seconds_past_midnight -= 86400; + } + JD -= JD_OF_1601_01_02; + populate_ctm_from_JD(ctm, JD); + populate_ctm_from_double_time(ctm, seconds_past_midnight); + if( ctm.YYYY < 1601 ) + { + ctm.YYYY = ctm.MM = ctm.DD = 0; } } @@ -949,9 +943,9 @@ __gg__acos(cblc_field_t *dest, cblc_refer_t *source) } __gg__float128_to_field( dest, - value, - truncation_e, - NULL); + value, + truncation_e, + NULL); } extern "C" @@ -976,7 +970,7 @@ __gg__annuity(cblc_field_t *dest, cblc_refer_t *arg1, cblc_refer_t *arg2) } else { - retval = val1 / (1- powf128( (1+val1) , -val2 )); + retval = val1 / (1- powf128( (1+val1), -val2 )); } } else @@ -1009,9 +1003,9 @@ __gg__asin(cblc_field_t *dest, cblc_refer_t *source) } __gg__float128_to_field( dest, - value, - truncation_e, - NULL); + value, + truncation_e, + NULL); } extern "C" @@ -1025,9 +1019,9 @@ __gg__atan(cblc_field_t *dest, cblc_refer_t *source) value = atanf128(value); __gg__float128_to_field( dest, - value, - truncation_e, - NULL); + value, + truncation_e, + NULL); } extern "C" @@ -1046,50 +1040,62 @@ __gg__byte_length(cblc_field_t *dest, cblc_refer_t *source) extern "C" void __gg__char(cblc_field_t *dest, cblc_refer_t *source ) - { - int rdigits; + { + int rdigits; - // The CHAR function takes an integer, the ordinal position. It - // returns a single-character string, which is the character at that - // ordinal position. + // The CHAR function takes an integer, the ordinal position. It + // returns a single-character string, which is the character at that + // ordinal position. - // 'A', with the ascii value of 65, is at the ordinal position 66. + // 'A', with the ascii value of 65, is at the ordinal position 66. - int ordinal = (int)(__gg__binary_value_from_refer( &rdigits, - source)); - ordinal /= __gg__power_of_ten(rdigits); - int ch = ordinal-1; - memset(dest->data, internal_space, dest->capacity); - dest->data[0] = ch; - } + int ordinal = (int)(__gg__binary_value_from_refer( &rdigits, + source)); + ordinal /= __gg__power_of_ten(rdigits); + int ch = ordinal-1; + memset(dest->data, internal_space, dest->capacity); + dest->data[0] = ch; + } extern "C" void __gg__combined_datetime(cblc_field_t *dest, cblc_refer_t *arg1, cblc_refer_t *arg2) - { - int rdigits; + { + int rdigits; - __int128 val1 = (int)(__gg__binary_value_from_refer(&rdigits, - arg1)); - __int128 val2 = (int)(__gg__binary_value_from_refer(&rdigits, - arg2)); - __int128 value = val1 * 1000000 + val2; + __int128 val1 = (int)(__gg__binary_value_from_refer(&rdigits, + arg1)); + __int128 val2 = (int)(__gg__binary_value_from_refer(&rdigits, + arg2)); + __int128 value = val1 * 1000000 + val2; __gg__int128_to_field(dest, value, 6, truncation_e, NULL); - } + } extern "C" void -__gg__concat(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[] ) -{ - warnx("%s: not implemented", __func__); -} +__gg__concat(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) + { + size_t bytes = 0; + size_t offset = 0; + for(size_t i=0; i<ncount; i++) + { + bytes += inputs[i].qual_size; + } + __gg__adjust_dest_size(dest, bytes); + for(size_t i=0; i<ncount; i++) + { + memcpy( dest->data + offset, + inputs[i].qual_data, + inputs[i].qual_size); + offset += inputs[i].qual_size; + } + } - extern "C" void __gg__cos(cblc_field_t *dest, cblc_refer_t *source) @@ -1109,36 +1115,36 @@ __gg__cos(cblc_field_t *dest, cblc_refer_t *source) extern "C" void __gg__current_date(cblc_field_t *dest) - { - // FUNCTION CURRENT-DATE - struct timespec tp = {}; - __gg__clock_gettime(CLOCK_REALTIME, &tp); // time_t tv_sec; long tv_nsec - - char retval[DATE_STRING_BUFFER_SIZE]; - timespec_to_string(retval, tp); - ascii_to_internal_str(retval, strlen(retval)); - string_to_dest(dest, retval); - } + { + // FUNCTION CURRENT-DATE + struct timespec tp = {}; + __gg__clock_gettime(CLOCK_REALTIME, &tp); // time_t tv_sec; long tv_nsec + + char retval[DATE_STRING_BUFFER_SIZE]; + timespec_to_string(retval, tp); + ascii_to_internal_str(retval, strlen(retval)); + string_to_dest(dest, retval); + } extern "C" void __gg__date_of_integer(cblc_field_t *dest, cblc_refer_t *source) - { - // FUNCTION DATE-OF-INTEGER - int rdigits; - double JD = (double)__gg__binary_value_from_refer(&rdigits, source); - JD += JD_OF_1601_01_02; - int Y; - int M; - int D; - JD_to_YMD(Y, M, D, JD); - int retval = Y*10000 + M*100 + D; - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); - } + { + // FUNCTION DATE-OF-INTEGER + int rdigits; + double JD = (double)__gg__binary_value_from_refer(&rdigits, source); + JD += JD_OF_1601_01_02; + int Y; + int M; + int D; + JD_to_YMD(Y, M, D, JD); + int retval = Y*10000 + M*100 + D; + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } extern "C" void @@ -1159,44 +1165,44 @@ __gg__date_to_yyyymmdd( cblc_field_t *dest, int retval = year_to_yyyy(yy, arg2, arg3) * 10000 + mmdd; __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + retval, + NO_RDIGITS, + truncation_e, + NULL); } extern "C" void __gg__day_of_integer(cblc_field_t *dest, cblc_refer_t *source) - { - // FUNCTION DAY-OF_INTEGER - int rdigits; - double JD = (double)__gg__binary_value_from_refer(&rdigits, source); - JD += JD_OF_1601_01_02; - int Y; - int M; - int D; - JD_to_YMD(Y, M, D, JD); - - double start_of_year = YMD_to_JD(Y, 1, 1); - - __int128 retval = Y * 1000 + int(JD - start_of_year) + 1; - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); - } - -extern "C" -void -__gg__day_to_yyyyddd( cblc_field_t *dest, - cblc_refer_t *par1, - cblc_refer_t *par2, - cblc_refer_t *par3) { - // FUNCTION DAY-TO-YYYYDDD - // See the discussion in ISO/IEC 2014-1989 Section 15.20 + // FUNCTION DAY-OF_INTEGER + int rdigits; + double JD = (double)__gg__binary_value_from_refer(&rdigits, source); + JD += JD_OF_1601_01_02; + int Y; + int M; + int D; + JD_to_YMD(Y, M, D, JD); + + double start_of_year = YMD_to_JD(Y, 1, 1); + + __int128 retval = Y * 1000 + int(JD - start_of_year) + 1; + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } + +extern "C" +void +__gg__day_to_yyyyddd( cblc_field_t *dest, + cblc_refer_t *par1, + cblc_refer_t *par2, + cblc_refer_t *par3) + { + // FUNCTION DAY-TO-YYYYDDD + // See the discussion in ISO/IEC 2014-1989 Section 15.20 int rdigits; int arg1 = (int)__gg__binary_value_from_refer(&rdigits, par1); int arg2 = (int)__gg__binary_value_from_refer(&rdigits, par2); @@ -1261,34 +1267,34 @@ __gg__exp10(cblc_field_t *dest, cblc_refer_t *source) extern "C" void __gg__factorial(cblc_field_t *dest, cblc_refer_t *source) + { + // FUNCTION FACTORIAL + int rdigits; + int N = (int)__gg__binary_value_from_refer(&rdigits, source); + while(rdigits--) { - // FUNCTION FACTORIAL - int rdigits; - int N = (int)__gg__binary_value_from_refer(&rdigits, source); - while(rdigits--) - { - N /= 10; - } + N /= 10; + } - __int128 retval = 1; + __int128 retval = 1; - while( N > 1 ) - { - retval *= N--; - } - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + while( N > 1 ) + { + retval *= N--; } + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } extern "C" void __gg__formatted_current_date( cblc_field_t *dest, // Destination string cblc_refer_t *input) // datetime format { - // FUNCTION FORMATTED-DATETIME + // FUNCTION CURRENT-DATE // Establish the destination, and set it to spaces char *d = (char *)dest->data; @@ -1362,7 +1368,14 @@ __gg__formatted_date( cblc_field_t *dest, // Destination string char achftime[64]; get_all_time(achftime, ctm); - ftime_replace(d, dend, format, format_end, achftime); + if( __gg__exception_code ) + { + memset(d, internal_space, dend-d); + } + else + { + ftime_replace(d, dend, format, format_end, achftime); + } } extern "C" @@ -1374,7 +1387,7 @@ __gg__formatted_datetime( cblc_field_t *dest, // Destination string cblc_refer_t *par4) // optional offset in seconds { // FUNCTION FORMATTED-DATETIME - + // Establish the destination, and set it to spaces char *d = (char *)dest->data; char *dend = d + dest->capacity; @@ -1383,15 +1396,29 @@ __gg__formatted_datetime( cblc_field_t *dest, // Destination string // Establish the formatting string: char *format = (char *)par1->qual_data; char *format_end = format + par1->qual_size; + trim_trailing_spaces(format, format_end); + bool is_zulu = is_zulu_format(format, format_end); struct cobol_tm ctm = {}; populate_ctm_from_date(ctm, par2); populate_ctm_from_time(ctm, par3, par4); + if( is_zulu ) + { + convert_to_zulu(ctm); + } + char achftime[64]; get_all_time(achftime, ctm); - ftime_replace(d, dend, format, format_end, achftime); + if( __gg__exception_code ) + { + memset(d, internal_space, dend-d); + } + else + { + ftime_replace(d, dend, format, format_end, achftime); + } } extern "C" @@ -1411,13 +1438,27 @@ __gg__formatted_time( cblc_field_t *dest, // Destination string // Establish the formatting string: char *format = (char *)par1->qual_data; char *format_end = format + par1->qual_size; + trim_trailing_spaces(format, format_end); + bool is_zulu = is_zulu_format(format, format_end); struct cobol_tm ctm = {}; populate_ctm_from_time(ctm, par3, par4); + if( is_zulu ) + { + convert_to_zulu(ctm); + } + char achftime[64]; get_all_time(achftime, ctm); - ftime_replace(d, dend, format, format_end, achftime); + if( __gg__exception_code ) + { + memset(d, internal_space, dend-d); + } + else + { + ftime_replace(d, dend, format, format_end, achftime); + } } extern "C" @@ -1437,50 +1478,50 @@ __gg__integer(cblc_field_t *dest, cblc_refer_t *source) extern "C" void __gg__integer_of_date(cblc_field_t *dest, cblc_refer_t *source) - { - // FUNCTION INTEGER-OF-DATE - int rdigits; - long argument_1 = (long)(__gg__binary_value_from_refer(&rdigits, source)); + { + // FUNCTION INTEGER-OF-DATE + int rdigits; + long argument_1 = (long)(__gg__binary_value_from_refer(&rdigits, source)); - int retval = 0; - static const int max_days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + int retval = 0; + static const int max_days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - int year = (long)argument_1/10000; - int month = (long)argument_1/100 % 100; - int day = (long)argument_1 % 100; + int year = (long)argument_1/10000; + int month = (long)argument_1/100 % 100; + int day = (long)argument_1 % 100; - // We need to check for validity in the proleptic Gregorian calendar. + // We need to check for validity in the proleptic Gregorian calendar. - int max_day = 0; - if( month >= 1 && month <= 12 ) - { - max_day = max_days[month]; - } - if( max_day == 28 && (((year%4) == 0 && ((year)%100) != 0) || ((year%400) == 0) )) - { - // Year is divisible by four, but is not divisible by 100, so this - // is a leap year. - max_day += 1; - } - if( day < 1 || day > max_day ) - { - max_day = 0; - } - if( max_day && year >= 1601 && year <= 9999 ) - { - // It's a valid Y/M/D: - double JD = YMD_to_JD(year, month, day); + int max_day = 0; + if( month >= 1 && month <= 12 ) + { + max_day = max_days[month]; + } + if( max_day == 28 && (((year%4) == 0 && ((year)%100) != 0) || ((year%400) == 0) )) + { + // Year is divisible by four, but is not divisible by 100, so this + // is a leap year. + max_day += 1; + } + if( day < 1 || day > max_day ) + { + max_day = 0; + } + if( max_day && year >= 1601 && year <= 9999 ) + { + // It's a valid Y/M/D: + double JD = YMD_to_JD(year, month, day); - // Offset result so that 1601-01-01 comes back as the first day of - // the Gregorian Calendar - retval = (int)(JD - JD_OF_1601_01_02); - } - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + // Offset result so that 1601-01-01 comes back as the first day of + // the Gregorian Calendar + retval = (int)(JD - JD_OF_1601_01_02); } + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } extern "C" void @@ -1511,10 +1552,32 @@ __gg__integer_part(cblc_field_t *dest, cblc_refer_t *source) _Float128 value = __gg__float128_from_refer(source); _Float128 retval = floorf128(fabsf128(value)); - char ach[64]; - strfromf128(ach, sizeof(ach), "%f", value); + if( value < 0 ) + { + retval = -retval; + } + __gg__float128_to_field(dest, + retval, + truncation_e, + NULL); + } +extern "C" +void +__gg__fraction_part(cblc_field_t *dest, cblc_refer_t *source) + { + // FUNCTION INTEGER-PART + _Float128 value = __gg__float128_from_refer(source); + bool is_negative = false; if( value < 0 ) + { + is_negative = true; + value = -value; + } + + _Float128 retval = value - floorf128(value); + + if( is_negative ) { retval = -retval; } @@ -1571,7 +1634,7 @@ __gg__max(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) // FUNCTION MAX if( ( inputs[0].field->type == FldAlphanumeric - || inputs[0].field->type == FldLiteralA) ) + || inputs[0].field->type == FldLiteralA) ) { cblc_field_t *best_field ; unsigned char *best_location ; @@ -1611,19 +1674,19 @@ __gg__max(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) bool candidate_address_of = inputs[i].address_of; int compare_result = __gg__compare_2( - candidate_field, - candidate_location, - candidate_length, - candidate_attr, - candidate_move_all, - candidate_address_of, - best_field, - best_location, - best_length, - best_attr, - best_move_all, - best_address_of, - 0); + candidate_field, + candidate_location, + candidate_length, + candidate_attr, + candidate_move_all, + candidate_address_of, + best_field, + best_location, + best_length, + best_attr, + best_move_all, + best_address_of, + 0); if( compare_result >= 0 ) { best_field = candidate_field ; @@ -1679,25 +1742,25 @@ __gg__max(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) } } } - __gg__float128_to_field(dest, - retval, - truncation_e, - NULL); + __gg__float128_to_field(dest, + retval, + truncation_e, + NULL); } } extern "C" void __gg__lower_case(cblc_field_t *dest, cblc_refer_t *input ) - { - size_t dest_length = dest->capacity; - size_t source_length = input->qual_size; - memset(dest->data, internal_space, dest_length); - memcpy(dest->data, input->qual_data, std::min(dest_length, source_length)); - internal_to_ascii((char *)dest->data, dest_length); - std::transform(dest->data, dest->data + dest_length, dest->data, tolower); - ascii_to_internal_str((char *)dest->data, dest_length); - } + { + size_t dest_length = dest->capacity; + size_t source_length = input->qual_size; + memset(dest->data, internal_space, dest_length); + memcpy(dest->data, input->qual_data, std::min(dest_length, source_length)); + internal_to_ascii((char *)dest->data, dest_length); + std::transform(dest->data, dest->data + dest_length, dest->data, tolower); + ascii_to_internal_str((char *)dest->data, dest_length); + } extern "C" void @@ -1720,8 +1783,8 @@ __gg__median(cblc_field_t *dest, size_t ncount, cblc_refer_t *source) // FUNCTION MEDIAN // This is wasteful, because it allocates N values in order to sort them. It - // is also an O(NlogN) solution, when there are O(N) solutions available. - + // is also an O(NlogN) solution, when there are O(N) solutions available. + // It has the merit of being very simple. // The future beckons, but not today. @@ -1777,7 +1840,7 @@ extern "C" void __gg__midrange(cblc_field_t *dest, size_t ncount, cblc_refer_t *source) { - // FUNCTION MIDRANGE + // FUNCTION MIDRANGE _Float128 val; _Float128 min=0; _Float128 max=0; @@ -1818,9 +1881,9 @@ void __gg__min(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) { // FUNCTION MIN - + if( ( inputs[0].field->type == FldAlphanumeric - || inputs[0].field->type == FldLiteralA) ) + || inputs[0].field->type == FldLiteralA) ) { cblc_field_t *best_field ; unsigned char *best_location ; @@ -1859,19 +1922,19 @@ __gg__min(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) bool candidate_address_of = inputs[i].address_of; int compare_result = __gg__compare_2( - candidate_field, - candidate_location, - candidate_length, - candidate_attr, - candidate_move_all, - candidate_address_of, - best_field, - best_location, - best_length, - best_attr, - best_move_all, - best_address_of, - 0); + candidate_field, + candidate_location, + candidate_length, + candidate_attr, + candidate_move_all, + candidate_address_of, + best_field, + best_location, + best_length, + best_attr, + best_move_all, + best_address_of, + 0); if( compare_result < 0 ) { best_field = candidate_field ; @@ -1889,7 +1952,7 @@ __gg__min(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) } } } - + __gg__adjust_dest_size(dest, best_length); dest->type = FldAlphanumeric; memcpy(dest->data, best_location, best_length); @@ -1927,10 +1990,10 @@ __gg__min(cblc_field_t *dest, size_t ncount, cblc_refer_t inputs[]) } } } - __gg__float128_to_field(dest, - retval, - truncation_e, - NULL); + __gg__float128_to_field(dest, + retval, + truncation_e, + NULL); } } @@ -1969,192 +2032,291 @@ __gg__mod(cblc_field_t *dest, cblc_refer_t *source1, cblc_refer_t *source2) NULL); } -static -int +static int numval(cblc_field_t *dest, cblc_refer_t *input) { - size_t errcode = 0; + // Returns the one-based character position of a bad character + // returns zero if it is okay + + char *p = (char *)input->qual_data; + char *pend = p + input->qual_size; - char *p = (char *)input->qual_data; - char *pstart = p; - char *pend = p + input->qual_size; + int errpos = 0; + __int128 retval = 0; + int retval_rdigits = 0; - _Float128 retval = 0; - int sign = 0; - int rdigits = 0; - int rdigit_bump = 0; + bool saw_digit= false; char decimal_point = __gg__get_decimal_point(); - - // We will do this as a state machine: - - enum + bool in_fraction = false; + bool leading_sign = false; + bool is_negative = false; + enum { - first_space , - first_sign , - before_digits , - in_digits , - after_digits , - second_sign , - final_space , - } state = first_space; + SPACE1, + SPACE2, + DIGITS, + SPACE3, + SPACE4, + } state = SPACE1; + if( input->qual_size == 0 ) + { + errpos = 1; + goto done; + } while( p < pend ) { char ch = *p++; + errpos += 1; switch( state ) { - case first_space : - if( ch != internal_space ) + case SPACE1: + // We tolerate spaces, and expect to end with a sign, digit, + // or decimal point: + if( ch == internal_space ) { - state = first_sign; - p -= 1; + continue; } - break; - - case first_sign : if( ch == internal_plus ) { - sign = 1; - state = before_digits; + leading_sign = true; + state = SPACE2; + break; } - else if( ch == internal_minus ) + if( ch == internal_minus ) { - sign = -1; - state = before_digits; + leading_sign = true; + is_negative = true; + state = SPACE2; + break; } - else if( (ch >= internal_0 && ch <= internal_9) - || ch == decimal_point ) + if( ch >= internal_0 && ch <= internal_9 ) { - state = in_digits; - p -= 1; + saw_digit = true; + retval = ch & 0xF; + state = DIGITS; + break; } - else + if( ch == decimal_point ) { - // We have a bad character: - errcode = p - pstart; - state = final_space; - p = pend; + in_fraction = true; + state = DIGITS; + break; } + // This is a bad character; errpos is correct + goto done; break; - case before_digits : - if( ch != internal_space ) + case SPACE2: + // We tolerate spaces, and expect to end with a digit or decimal point: + if( ch == internal_space ) { - state = in_digits; - p -= 1; + break; } + if( ch >= internal_0 && ch <= internal_9 ) + { + saw_digit = true; + retval = ch & 0xF; + state = DIGITS; + break; + } + if( ch == decimal_point ) + { + in_fraction = true; + state = DIGITS; + break; + } + // This is a bad character; errpos is correct + goto done; break; - case in_digits : - // The only thing allowed here are digits and the decimal separator: + case DIGITS: + // We tolerate digits. We tolerate one decimal point. We expect to + // end with a space, a sign, "DB" or "CR", or the the end of the string + // It's a bit complicated + if( ch >= internal_0 && ch <= internal_9 ) { - // We have a digit. - rdigits += rdigit_bump; + saw_digit = true; retval *= 10; - retval += ch & 0x0F; - } - else if( ch == decimal_point && rdigit_bump) - { - // We have a decimal_point, which is against the rules: - errcode = p - pstart; - state = final_space; - p = pend; + retval += ch & 0xF; + if( in_fraction ) + { + retval_rdigits += 1; + } + break; } - else if( ch == decimal_point ) + if( ch == decimal_point && in_fraction ) { - rdigit_bump = 1; + // Only one decimal is allowed + goto done; } - else + if( ch == decimal_point ) { - // We something that isn't a digit or decimal separator: - state = after_digits; - p -= 1; + in_fraction = true; + break; } - break; - - case after_digits : if( ch == internal_space ) { - continue; + state = SPACE3; + break; } - if( sign ) + if( ch == internal_plus && leading_sign) { - // We already saw a sign character - state = final_space; + // We are allowed leading or trailing signs, but not both + goto done; } - else + if( ch == internal_minus && leading_sign) { - state = second_sign; + // We are allowed leading or trailing signs, but not both + goto done; } - p -= 1; - break; - - case second_sign : if( ch == internal_plus ) { - sign = 1; + state = SPACE4; + break; } - else if( ch == internal_minus ) + if( ch == internal_minus ) { - sign = -1; + is_negative = true; + state = SPACE4; + break; } - else if( (ch == internal_D || ch == internal_d) - && p < pend - && (*p == internal_B || *p == internal_b) ) + if( tolower(ch) == 'd' ) { - sign = -1; - p += 1; + if( leading_sign ) + { + goto done; + } + ch = *p++; + errpos += 1; + if( p > pend || tolower(ch) != 'b' ) + { + goto done; + } + is_negative = true; + state = SPACE4; + break; } - else if( (ch == internal_C || ch == internal_c) - && p < pend - && (*p == internal_R || *p == internal_r) ) + if( tolower(ch) == 'c' ) { - sign = -1; - p += 1; + if( leading_sign ) + { + goto done; + } + ch = *p++; + errpos += 1; + if( p > pend || tolower(ch) != 'r' ) + { + goto done; + } + is_negative = true; + state = SPACE4; + break; } - else + // This is a bad character; errpos is correct + goto done; + break; + + case SPACE3: + // We tolerate spaces, or we end with a sign: + if( ch == internal_space ) { - // We have an invalid character - errcode = p - pstart; - state = final_space; - p = pend; + break; } - state = final_space; + if( ch == internal_plus && leading_sign) + { + // We are allowed leading or trailing signs, but not both + goto done; + } + if( ch == internal_minus && leading_sign) + { + // We are allowed leading or trailing signs, but not both + goto done; + } + if( ch == internal_plus ) + { + state = SPACE4; + break; + } + if( ch == internal_minus ) + { + is_negative = true; + state = SPACE4; + break; + } + if( tolower(ch) == 'd' ) + { + if( leading_sign ) + { + goto done; + } + ch = *p++; + errpos += 1; + if( p > pend || tolower(ch) != 'b' ) + { + goto done; + } + is_negative = true; + state = SPACE4; + break; + } + if( tolower(ch) == 'c' ) + { + if( leading_sign ) + { + goto done; + } + ch = *p++; + errpos += 1; + if( p > pend || tolower(ch) != 'r' ) + { + goto done; + } + is_negative = true; + state = SPACE4; + break; + } + goto done; break; - - case final_space : + case SPACE4: if( ch == internal_space ) { - continue; + break; } - // We have a non-space where there should be only space - errcode = p - pstart; - p = pend; + goto done; break; } } - if( sign == 0 ) + if( saw_digit ) { - sign = 1; + errpos = 0; } - retval *= sign; - - if( state != after_digits && state != final_space && state != in_digits ) + else if( p == pend ) { - errcode = pend - pstart + 1; + // If we got to the end without seeing adigit, we need to bump the + // error pointer: + errpos += 1; } - if( dest ) + done: + if(errpos) { - retval /= __gg__power_of_ten(rdigits); - - __gg__float128_to_field(dest, - retval, - truncation_e, - NULL); + retval = 0; } - return (int)errcode; + if( is_negative ) + { + retval = -retval; + } + if(dest) + { + __gg__int128_to_field(dest, + retval, + retval_rdigits, + truncation_e, + NULL); + } + return errpos; } static @@ -2204,15 +2366,15 @@ numval_c( cblc_field_t *dest, enum { - first_space , - first_sign , - second_space , - currency , - before_digits , - digits , - after_digits , - second_sign , - final_space , + first_space, + first_sign, + second_space, + currency, + before_digits, + digits, + after_digits, + second_sign, + final_space, } state = first_space; while( p < pend ) @@ -2238,7 +2400,7 @@ numval_c( cblc_field_t *dest, p -= 1; } else if( (ch >= internal_0 && ch <= internal_9) - || ch == decimal_point ) + || ch == decimal_point ) { state = digits; p -= 1; @@ -2262,7 +2424,7 @@ numval_c( cblc_field_t *dest, sign = 1; state = second_space; } - else + else { sign = -1; state = second_space; @@ -2280,7 +2442,7 @@ numval_c( cblc_field_t *dest, p -= 1; } else if( (ch >= internal_0 && ch <= internal_9) - || ch == decimal_point ) + || ch == decimal_point ) { state = digits; p -= 1; @@ -2323,7 +2485,7 @@ numval_c( cblc_field_t *dest, if( ch != internal_space ) { if( (ch >= internal_0 && ch <= internal_9) - || ch == decimal_point ) + || ch == decimal_point ) { state = digits; p -= 1; @@ -2378,11 +2540,11 @@ numval_c( cblc_field_t *dest, if( ch != internal_space ) { if( ch == internal_plus - || ch == internal_minus - || ch == internal_D - || ch == internal_d - || ch == internal_C - || ch == internal_c ) + || ch == internal_minus + || ch == internal_D + || ch == internal_d + || ch == internal_C + || ch == internal_c ) { state = second_sign; p -= 1; @@ -2407,15 +2569,15 @@ numval_c( cblc_field_t *dest, sign = -1; } else if( (ch == internal_D || ch == internal_d) - && p < pend - && (*p == internal_B || *p == internal_b) ) + && p < pend + && (*p == internal_B || *p == internal_b) ) { sign = -1; p += 1; } else if( (ch == internal_C || ch == internal_c) - && p < pend - && (*p == internal_R || *p == internal_r) ) + && p < pend + && (*p == internal_R || *p == internal_r) ) { sign = -1; p += 1; @@ -2463,7 +2625,23 @@ extern "C" void __gg__numval(cblc_field_t *dest, cblc_refer_t *source) { - numval(dest, source); + int errpos = numval(dest, source); + if( errpos ) + { + exception_raise(ec_argument_function_e); + } + } + +extern "C" +void +__gg__test_numval(cblc_field_t *dest, cblc_refer_t *source) + { + int retval = numval(NULL, source); + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); } extern "C" @@ -2473,217 +2651,229 @@ __gg__numval_c(cblc_field_t *dest, cblc_refer_t *source, cblc_refer_t *currency) numval_c(dest, source, currency); } +extern "C" +void +__gg__test_numval_c(cblc_field_t *dest, cblc_refer_t *source, cblc_refer_t *currency) + { + int retval = numval_c(NULL, source, currency); + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } + extern "C" void __gg__ord(cblc_field_t *dest, cblc_refer_t *input ) - { - // We get our input in internal_character form. - char *arg = (char *)input->qual_data; + { + // We get our input in internal_character form. + char *arg = (char *)input->qual_data; - // The ORD function takes a single-character string and returns the - // ordinal position of that character. + // The ORD function takes a single-character string and returns the + // ordinal position of that character. - // In ASCII mode, an A is 0x41, so we return 0x42 - // In EBCDIC mode, an A is 0xC1, so we return 0xC2 + // In ASCII mode, an A is 0x41, so we return 0x42 + // In EBCDIC mode, an A is 0xC1, so we return 0xC2 - size_t retval = (arg[0]&0xFF) + 1; - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); - } + size_t retval = (arg[0]&0xFF) + 1; + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } extern "C" void __gg__ord_min(cblc_field_t *dest, size_t ninputs, cblc_refer_t inputs[]) - { - // Sets dest to the one-based ordinal position of the first occurrence - // of the biggest element in the list of refs[] + { + // Sets dest to the one-based ordinal position of the first occurrence + // of the biggest element in the list of refs[] - int retval = -1; - int running_position = -1; + int retval = -1; + int running_position = -1; - cblc_field_t *best; - unsigned char *best_location; - size_t best_length; - int best_attr; - bool best_move_all; - bool best_address_of ; + cblc_field_t *best; + unsigned char *best_location; + size_t best_length; + int best_attr; + bool best_move_all; + bool best_address_of ; - unsigned char *candidate_location; - size_t candidate_length; - int candidate_attr; - bool candidate_move_all; - bool candidate_address_of; + unsigned char *candidate_location; + size_t candidate_length; + int candidate_attr; + bool candidate_move_all; + bool candidate_address_of; - for( size_t i=0; i<ninputs; i++ ) - { - refer_state_for_all state; + for( size_t i=0; i<ninputs; i++ ) + { + refer_state_for_all state; - cblc_refer_t *input = &inputs[i]; - build_refer_state_for_all(state, input); - for(;;) - { - running_position += 1; - if( retval == -1) - { - // We have to initialize the comparisons: - retval = running_position; - best = input->field; - best_location = input->qual_data; - best_length = input->qual_size; - best_attr = input->field->attr; - best_move_all = inputs[i].move_all; - best_address_of = inputs[i].address_of; - } - else - { - // We need to save the current adjustments, because __gg__compare - // is free to modify .location - candidate_location = input->qual_data; - candidate_length = input->qual_size; - candidate_attr = input->field->attr; - candidate_move_all = inputs[i].move_all; - candidate_address_of = inputs[i].address_of; - - int compare_result = - __gg__compare_2( - input->field, - candidate_location, - candidate_length, - candidate_attr, - candidate_move_all, - candidate_address_of, - best, - best_location, - best_length, - best_attr, - best_move_all, - best_address_of, - 0); - if( compare_result < 0 ) - { - retval = running_position; - best = input->field; - best_location = candidate_location; - best_length = candidate_length; - best_attr = candidate_attr; - best_move_all = candidate_move_all; - best_address_of = candidate_address_of; - } - } - if( !update_refer_state_for_all(state, input) ) - { - // There is nothing left to do for that input. - break; - } - } + cblc_refer_t *input = &inputs[i]; + build_refer_state_for_all(state, input); + for(;;) + { + running_position += 1; + if( retval == -1) + { + // We have to initialize the comparisons: + retval = running_position; + best = input->field; + best_location = input->qual_data; + best_length = input->qual_size; + best_attr = input->field->attr; + best_move_all = inputs[i].move_all; + best_address_of = inputs[i].address_of; } - - retval += 1; - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + else + { + // We need to save the current adjustments, because __gg__compare + // is free to modify .location + candidate_location = input->qual_data; + candidate_length = input->qual_size; + candidate_attr = input->field->attr; + candidate_move_all = inputs[i].move_all; + candidate_address_of = inputs[i].address_of; + + int compare_result = + __gg__compare_2( + input->field, + candidate_location, + candidate_length, + candidate_attr, + candidate_move_all, + candidate_address_of, + best, + best_location, + best_length, + best_attr, + best_move_all, + best_address_of, + 0); + if( compare_result < 0 ) + { + retval = running_position; + best = input->field; + best_location = candidate_location; + best_length = candidate_length; + best_attr = candidate_attr; + best_move_all = candidate_move_all; + best_address_of = candidate_address_of; + } + } + if( !update_refer_state_for_all(state, input) ) + { + // There is nothing left to do for that input. + break; + } + } } + retval += 1; + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } + extern "C" void __gg__ord_max(cblc_field_t *dest, size_t ninputs, cblc_refer_t inputs[]) - { - // Sets dest to the one-based ordinal position of the first occurrence - // of the biggest element in the list of refs[] + { + // Sets dest to the one-based ordinal position of the first occurrence + // of the biggest element in the list of refs[] - int retval = -1; - int running_position = -1; + int retval = -1; + int running_position = -1; - cblc_field_t *best; - unsigned char *best_location; - size_t best_length; - int best_attr; - bool best_move_all; - bool best_address_of ; + cblc_field_t *best; + unsigned char *best_location; + size_t best_length; + int best_attr; + bool best_move_all; + bool best_address_of ; - unsigned char *candidate_location; - size_t candidate_length; - int candidate_attr; - bool candidate_move_all; - bool candidate_address_of; + unsigned char *candidate_location; + size_t candidate_length; + int candidate_attr; + bool candidate_move_all; + bool candidate_address_of; - for( size_t i=0; i<ninputs; i++ ) - { - refer_state_for_all state; + for( size_t i=0; i<ninputs; i++ ) + { + refer_state_for_all state; - cblc_refer_t *input = &inputs[i]; - build_refer_state_for_all(state, input); - for(;;) - { - running_position += 1; - if( retval == -1) - { - // We have to initialize the comparisons: - retval = running_position; - best = input->field; - best_location = input->qual_data; - best_length = input->qual_size; - best_attr = input->field->attr; - best_move_all = inputs[i].move_all; - best_address_of = inputs[i].address_of; - } - else - { - // We need to save the current adjustments, because __gg__compare - // is free to modify .location - candidate_location = input->qual_data; - candidate_length = input->qual_size; - candidate_attr = input->field->attr; - candidate_move_all = inputs[i].move_all; - candidate_address_of = inputs[i].address_of; - - int compare_result = - __gg__compare_2( - input->field, - candidate_location, - candidate_length, - candidate_attr, - candidate_move_all, - candidate_address_of, - best, - best_location, - best_length, - best_attr, - best_move_all, - best_address_of, - 0); - if( compare_result > 0 ) - { - retval = running_position; - best = input->field; - best_location = candidate_location; - best_length = candidate_length; - best_attr = candidate_attr; - best_move_all = candidate_move_all; - best_address_of = candidate_address_of; - } - } - if( !update_refer_state_for_all(state, input) ) - { - // There is nothing left to do for that input. - break; - } - } + cblc_refer_t *input = &inputs[i]; + build_refer_state_for_all(state, input); + for(;;) + { + running_position += 1; + if( retval == -1) + { + // We have to initialize the comparisons: + retval = running_position; + best = input->field; + best_location = input->qual_data; + best_length = input->qual_size; + best_attr = input->field->attr; + best_move_all = inputs[i].move_all; + best_address_of = inputs[i].address_of; } - - retval += 1; - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + else + { + // We need to save the current adjustments, because __gg__compare + // is free to modify .location + candidate_location = input->qual_data; + candidate_length = input->qual_size; + candidate_attr = input->field->attr; + candidate_move_all = inputs[i].move_all; + candidate_address_of = inputs[i].address_of; + + int compare_result = + __gg__compare_2( + input->field, + candidate_location, + candidate_length, + candidate_attr, + candidate_move_all, + candidate_address_of, + best, + best_location, + best_length, + best_attr, + best_move_all, + best_address_of, + 0); + if( compare_result > 0 ) + { + retval = running_position; + best = input->field; + best_location = candidate_location; + best_length = candidate_length; + best_attr = candidate_attr; + best_move_all = candidate_move_all; + best_address_of = candidate_address_of; + } + } + if( !update_refer_state_for_all(state, input) ) + { + // There is nothing left to do for that input. + break; + } + } } + retval += 1; + __gg__int128_to_field(dest, + retval, + NO_RDIGITS, + truncation_e, + NULL); + } + extern "C" void __gg__pi(cblc_field_t *dest) @@ -2822,90 +3012,90 @@ __gg__rem( cblc_field_t *dest, cblc_refer_t *input1, cblc_refer_t *input2 ) extern "C" void __gg__trim(cblc_field_t *dest, cblc_refer_t *arg1, cblc_refer_t *arg2) - { - int rdigits; - __int128 type = __gg__binary_value_from_refer( &rdigits, - arg2); - //static const int BOTH = 0; - static const int LEADING = 1; // Remove leading spaces - static const int TRAILING = 2; // Remove trailing spaces + { + int rdigits; + __int128 type = __gg__binary_value_from_refer( &rdigits, + arg2); + //static const int BOTH = 0; + static const int LEADING = 1; // Remove leading spaces + static const int TRAILING = 2; // Remove trailing spaces - if( dest->type != FldAlphanumeric || + if( dest->type != FldAlphanumeric || !(dest->attr & temporary_e) || !(dest->attr & intermediate_e) ) - { - fprintf(stderr, - "We expect the target of a FUNCTION TIME to " - "be an intermediate alphanumeric\n"); - abort(); - } - dest->capacity = dest->offset; + { + fprintf(stderr, + "We expect the target of a FUNCTION TIME to " + "be an intermediate alphanumeric\n"); + abort(); + } + dest->capacity = dest->offset; - // No matter what, we want to find the leftmost non-space and the - // rightmost non-space: + // No matter what, we want to find the leftmost non-space and the + // rightmost non-space: - char *left = (char *)arg1->qual_data; - char *right = left + arg1->qual_size-1; + char *left = (char *)arg1->qual_data; + char *right = left + arg1->qual_size-1; - // Find left and right: the first and last non-spaces - while( left <= right ) - { - if( *left != internal_space && *right != internal_space ) - { - break; - } - if( *left == internal_space ) - { - left += 1; - } - if( *right == internal_space ) - { - right -= 1; - } - } - if( type == LEADING ) + // Find left and right: the first and last non-spaces + while( left <= right ) + { + if( *left != internal_space && *right != internal_space ) { - // We want to leave any trailing spaces, so we return 'right' to its - // original value: - right = (char *)arg1->qual_data + arg1->qual_size-1; + break; } - else if( type == TRAILING ) + if( *left == internal_space ) { - // We want to leave any leading spaces, so we return 'left' to its - // original value: - left = (char *)arg1->qual_data; + left += 1; } - - if( left > right ) + if( *right == internal_space ) { - // When the arg1 input string was empty, we want left to be right+1. - // The left/right loop can sometimes end up with left equal to right+2. - // That needs to be fixed: - left = right+1; + right -= 1; } + } + if( type == LEADING ) + { + // We want to leave any trailing spaces, so we return 'right' to its + // original value: + right = (char *)arg1->qual_data + arg1->qual_size-1; + } + else if( type == TRAILING ) + { + // We want to leave any leading spaces, so we return 'left' to its + // original value: + left = (char *)arg1->qual_data; + } - size_t ncount = right+1 - left; - __gg__adjust_dest_size(dest, ncount); + if( left > right ) + { + // When the arg1 input string was empty, we want left to be right+1. + // The left/right loop can sometimes end up with left equal to right+2. + // That needs to be fixed: + left = right+1; + } - // Because it's a temporary, we are weakly confident that we can change - // the capacity to match what we want. At this writing, we aren't 100% - // sure of the implications of the run-time capacity not matching what the - // compiler believes the capacity to be at compile-time. But we obviously - // think it'll be okay. + size_t ncount = right+1 - left; + __gg__adjust_dest_size(dest, ncount); - char *dest_left = (char *)dest->data; - char *dest_right = dest_left + dest->capacity - 1; - char *dest_end = dest_left + dest->capacity; + // Because it's a temporary, we are weakly confident that we can change + // the capacity to match what we want. At this writing, we aren't 100% + // sure of the implications of the run-time capacity not matching what the + // compiler believes the capacity to be at compile-time. But we obviously + // think it'll be okay. - while( dest_left <= dest_right && left <= right ) - { - *dest_left++ = *left++; - } - while(dest_left < dest_end) - { - *dest_left++ = internal_space; - } + char *dest_left = (char *)dest->data; + char *dest_right = dest_left + dest->capacity - 1; + char *dest_end = dest_left + dest->capacity; + + while( dest_left <= dest_right && left <= right ) + { + *dest_left++ = *left++; } + while(dest_left < dest_end) + { + *dest_left++ = internal_space; + } + } static struct random_data *buf = NULL; static char *state = NULL; @@ -2914,76 +3104,76 @@ static const size_t state_len = 256; extern "C" void __gg__random(cblc_field_t *dest, cblc_refer_t *input ) - { - // This creates a thread-safe pseudo-random number generator - // using input as the seed + { + // This creates a thread-safe pseudo-random number generator + // using input as the seed - // The return value is between zero and not quite one + // The return value is between zero and not quite one - if( !buf ) - { - // This is the very first time through - buf = (random_data *)MALLOC(sizeof(struct random_data)); - buf->state = NULL; - state = (char *)MALLOC(state_len); - - struct timespec ts; - __gg__clock_gettime(CLOCK_REALTIME, &ts); - initstate_r( ts.tv_nsec, state, state_len, buf); - } + if( !buf ) + { + // This is the very first time through + buf = (random_data *)MALLOC(sizeof(struct random_data)); + buf->state = NULL; + state = (char *)MALLOC(state_len); - int rdigits; - int seed = (int)__gg__binary_value_from_refer(&rdigits, input); - srandom_r(seed, buf); - - int32_t retval_31; - random_r(buf, &retval_31); - // We are going to convert this to a value between zero and not quite one: - double retval = double(retval_31) / double(0x80000000UL); - __gg__double_to_target( dest, - retval, - truncation_e); + struct timespec ts; + __gg__clock_gettime(CLOCK_REALTIME, &ts); + initstate_r( ts.tv_nsec, state, state_len, buf); } + int rdigits; + int seed = (int)__gg__binary_value_from_refer(&rdigits, input); + srandom_r(seed, buf); + + int32_t retval_31; + random_r(buf, &retval_31); + // We are going to convert this to a value between zero and not quite one: + double retval = double(retval_31) / double(0x80000000UL); + __gg__double_to_target( dest, + retval, + truncation_e); + } + extern "C" void __gg__random_next(cblc_field_t *dest) - { - // The return value is between zero and not quite one - - if( !buf ) - { - // This is the very first time through - buf = (random_data *)MALLOC(sizeof(struct random_data)); - buf->state = NULL; - state = (char *)MALLOC(state_len); - struct timespec ts; - __gg__clock_gettime(CLOCK_REALTIME, &ts); - initstate_r( ts.tv_nsec, state, state_len, buf); - } - int32_t retval_31; - random_r(buf, &retval_31); + { + // The return value is between zero and not quite one - // We are going to convert this to a value between zero and not quite one: - double retval = double(retval_31) / double(0x80000000UL); - __gg__double_to_target( dest, - retval, - truncation_e); + if( !buf ) + { + // This is the very first time through + buf = (random_data *)MALLOC(sizeof(struct random_data)); + buf->state = NULL; + state = (char *)MALLOC(state_len); + struct timespec ts; + __gg__clock_gettime(CLOCK_REALTIME, &ts); + initstate_r( ts.tv_nsec, state, state_len, buf); } + int32_t retval_31; + random_r(buf, &retval_31); + + // We are going to convert this to a value between zero and not quite one: + double retval = double(retval_31) / double(0x80000000UL); + __gg__double_to_target( dest, + retval, + truncation_e); + } extern "C" void __gg__reverse( cblc_field_t *dest, cblc_refer_t *input ) + { + size_t dest_length = dest->capacity; + size_t source_length = input->qual_size; + size_t length = std::min(dest_length, source_length); + memset(dest->data, internal_space, dest_length); + for(size_t i=0; i<length; i++) { - size_t dest_length = dest->capacity; - size_t source_length = input->qual_size; - size_t length = std::min(dest_length, source_length); - memset(dest->data, internal_space, dest_length); - for(size_t i=0; i<length; i++) - { - dest->data[i] = input->qual_data[source_length-1-i]; - } + dest->data[i] = input->qual_data[source_length-1-i]; } + } extern "C" void @@ -3101,7 +3291,7 @@ __gg__test_date_yyyymmdd( cblc_field_t *dest, cblc_refer_t *source) { int rdigits; int yyyymmdd = (int)__gg__binary_value_from_refer(&rdigits, - source); + source); int retval; int dd = yyyymmdd % 100; int mmdd = yyyymmdd % 10000; @@ -3136,10 +3326,10 @@ __gg__test_date_yyyymmdd( cblc_field_t *dest, cblc_refer_t *source) } } __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + retval, + NO_RDIGITS, + truncation_e, + NULL); } extern "C" @@ -3148,7 +3338,7 @@ __gg__test_day_yyyyddd( cblc_field_t *dest, cblc_refer_t *source) { int rdigits; int yyyyddd = (int)__gg__binary_value_from_refer( &rdigits, - source); + source); int retval; int ddd = yyyyddd % 1000; int yyyy = yyyyddd / 1000; @@ -3169,55 +3359,31 @@ __gg__test_day_yyyyddd( cblc_field_t *dest, cblc_refer_t *source) retval = 0; } __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + retval, + NO_RDIGITS, + truncation_e, + NULL); } extern "C" void -__gg__test_numval(cblc_field_t *dest, cblc_refer_t *source) +__gg__upper_case(cblc_field_t *dest, cblc_refer_t *input ) { - int retval = numval(NULL, source); - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + size_t dest_length = dest->capacity; + size_t source_length = input->qual_size; + memset(dest->data, internal_space, dest_length); + memcpy(dest->data, input->qual_data, std::min(dest_length, source_length)); + internal_to_ascii((char *)dest->data, dest_length); + std::transform(dest->data, dest->data + dest_length, dest->data, toupper); + ascii_to_internal_str((char *)dest->data, dest_length); } extern "C" void -__gg__test_numval_c(cblc_field_t *dest, cblc_refer_t *source, cblc_refer_t *currency) +__gg__variance(cblc_field_t *dest, size_t ncount, cblc_refer_t *source) { - int retval = numval_c(NULL, source, currency); - __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); - } - -extern "C" -void -__gg__upper_case(cblc_field_t *dest, cblc_refer_t *input ) - { - size_t dest_length = dest->capacity; - size_t source_length = input->qual_size; - memset(dest->data, internal_space, dest_length); - memcpy(dest->data, input->qual_data, std::min(dest_length, source_length)); - internal_to_ascii((char *)dest->data, dest_length); - std::transform(dest->data, dest->data + dest_length, dest->data, toupper); - ascii_to_internal_str((char *)dest->data, dest_length); - } - -extern "C" -void -__gg__variance(cblc_field_t *dest, size_t ncount, cblc_refer_t *source) - { - // FUNCTION VARIANCE - _Float128 retval = variance(ncount, source); + // FUNCTION VARIANCE + _Float128 retval = variance(ncount, source); __gg__float128_to_field(dest, retval, @@ -3228,15 +3394,15 @@ __gg__variance(cblc_field_t *dest, size_t ncount, cblc_refer_t *source) extern "C" void __gg__when_compiled(cblc_field_t *dest, size_t tv_sec, long tv_nsec) - { - struct timespec tp = {}; - tp.tv_sec = tv_sec; - tp.tv_nsec = tv_nsec; - char retval[DATE_STRING_BUFFER_SIZE]; - timespec_to_string(retval, tp); - ascii_to_internal_str(retval, strlen(retval)); - string_to_dest(dest, retval); - } + { + struct timespec tp = {}; + tp.tv_sec = tv_sec; + tp.tv_nsec = tv_nsec; + char retval[DATE_STRING_BUFFER_SIZE]; + timespec_to_string(retval, tp); + ascii_to_internal_str(retval, strlen(retval)); + string_to_dest(dest, retval); + } extern "C" void @@ -3254,21 +3420,22 @@ __gg__year_to_yyyy( cblc_field_t *dest, int retval = year_to_yyyy(yy, arg2, arg3); __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + retval, + NO_RDIGITS, + truncation_e, + NULL); } static int -gets_int(int digits, char *p, char *pend) +gets_int(int ndigits, char *p, char *pend, int *digits) { // This routine returns the value of the integer at p. If there is something // wrong with the integer, it returns a negative number, the value being the // position (starting at 1) where the problem is. int retval = 0; - for(int i=1; i<=digits; i++) + memset(digits, 0xFF, ndigits * sizeof(int)); + for(int i=1; i<=ndigits; i++) { if(p >= pend) { @@ -3284,7 +3451,8 @@ gets_int(int digits, char *p, char *pend) break; } retval *= 10; - retval += ch - internal_0; + retval += ch & 0xF; + digits[i-1] = ch & 0xF; } return retval; } @@ -3300,7 +3468,29 @@ gets_year(char *p, char *pend, struct cobol_tm &ctm) // where a four-character range with a year value of 1601 became impossible. int retval = 0; - int YYYY = gets_int(4, p, pend); + int digits[4]; + int YYYY = gets_int(4, p, pend, digits); + + if( digits[0] == -1 || digits[0] == 0 ) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } + if( digits[0] == 0 && digits[1] < 5) + { + return 2; + } + if( digits[2] == -1 ) + { + return 4; + } + if( digits[3] == -1 ) + { + return 4; + } if( YYYY >= 0 ) { @@ -3344,8 +3534,18 @@ gets_month(char *p, char *pend, struct cobol_tm &ctm) // Returns either zero, or else the ordinal position of where the input // processing failed. + int digits[2]; int retval = 0; - int MM = gets_int(2, p, pend); + int MM = gets_int(2, p, pend, digits); + + if( digits[0] == -1 || digits[0] > 1) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } if( MM >= 0 ) { if( MM == 0 ) @@ -3380,8 +3580,18 @@ gets_day(char *p, char *pend, struct cobol_tm &ctm) // The assumption is that YYYY and MM were populated before arriving here + int digits[2]; int retval = 0; - int DD = gets_int(2, p, pend); + int DD = gets_int(2, p, pend, digits); + + if( digits[0] == -1 || digits[0] > 3) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } if(DD >= 0) { if( DD >= 0 ) @@ -3440,7 +3650,8 @@ gets_day_of_week(char *p, char *pend, struct cobol_tm &ctm) { // This is just a simple D, for day-of-week. The COBOL spec is that // it be 1 to 7, 1 being Monday - int day_of_week = gets_int(1, p, pend); + int digits[1]; + int day_of_week = gets_int(1, p, pend, digits); if( day_of_week<0 || day_of_week >7) { // The single character at source is no good: @@ -3460,7 +3671,7 @@ gets_day_of_week(char *p, char *pend, struct cobol_tm &ctm) int day_of_year = (int)(JD - JD_Jan0); - // It's possible for the year/week/day_of_week to be + // It's possible for the year/week/day_of_week to be // before Jan 1. This is the case for 1900-12-31, as one example; that // date gets converted to 1901-W01-01 if( day_of_year <= 0 ) @@ -3487,7 +3698,20 @@ int gets_day_of_year(char *p, char *pend, struct cobol_tm &ctm) { // This is a three-digit day-of-year, 001 through 365,366 - int DDD = gets_int(3, p, pend); + int digits[3]; + int DDD = gets_int(3, p, pend, digits); + if( digits[0] == -1 || digits[0] > 3) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } + if( digits[2] == -1 ) + { + return 3; + } if( DDD < 0 ) { return -DDD; @@ -3531,7 +3755,16 @@ int gets_week(char *p, char *pend, struct cobol_tm &ctm) { // This is a two-digit value, 01 through 52,53 - int ww = gets_int(2, p, pend); + int digits[2]; + int ww = gets_int(2, p, pend, digits); + if( digits[0] == -1 || digits[0] > 5 ) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } if( ww < 0 ) { return -ww; @@ -3562,7 +3795,18 @@ int gets_hours(char *p, char *pend, struct cobol_tm &ctm, bool in_offset) { // This is a two-digit value, 01 through 23 - int hh = gets_int(2, p, pend); + int digits[2]; + int hh = gets_int(2, p, pend, digits); + + if( digits[0] == -1 || digits[0] > 2 ) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } + if( hh < 0 ) { return -hh; @@ -3596,7 +3840,17 @@ int gets_minutes(char *p, char *pend, struct cobol_tm &ctm, bool in_offset) { // This is a two-digit value, 01 through 59 - int mm = gets_int(2, p, pend); + int digits[2]; + int mm = gets_int(2, p, pend, digits); + if( digits[0] == -1 || digits[0] > 5 ) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } + if( mm < 0 ) { return -mm; @@ -3624,7 +3878,16 @@ int gets_seconds(char *p, char *pend, struct cobol_tm &ctm) { // This is a two-digit value, 01 through 59 - int ss = gets_int(2, p, pend); + int digits[2]; + int ss = gets_int(2, p, pend, digits); + if( digits[0] == -1 || digits[0] > 5 ) + { + return 1; + } + if( digits[1] == -1 ) + { + return 2; + } if( ss < 0 ) { return -ss; @@ -3642,7 +3905,7 @@ gets_seconds(char *p, char *pend, struct cobol_tm &ctm) static int -gets_nanoseconds(char *p, char *pend, struct cobol_tm &ctm) +gets_nanoseconds(char *f, char *f_end, char *p, char *pend, struct cobol_tm &ctm) { // Because nanoseconds digits to the right of the decimal point can vary from // one digit to our implementation-specific limit of nine characters, this @@ -3655,8 +3918,9 @@ gets_nanoseconds(char *p, char *pend, struct cobol_tm &ctm) int nanoseconds = 0; char *pinit = p; - while( *p == internal_s && p < pend ) + while( f < f_end && *f == internal_s && p < pend ) { + f += 1; int ch = *p++; errpos += 1; @@ -3694,7 +3958,11 @@ fill_cobol_tm(cobol_tm &ctm, // Establish the string to be checked: char *source = (char *)par2->qual_data; - char *source_end = format + par2->qual_size; + char *source_end = source + par2->qual_size; + + // Let's eliminate trailing spaces... + trim_trailing_spaces(format, format_end); + trim_trailing_spaces(source, source_end); bool in_offset = false; bool in_nanoseconds = false; @@ -3714,9 +3982,9 @@ fill_cobol_tm(cobol_tm &ctm, char ch = *format; if( ch == internal_T - || ch == internal_colon - || ch == internal_minus - || ch == internal_W) + || ch == internal_colon + || ch == internal_minus + || ch == internal_W) { // These are just formatting characters. They need to be duplicated, // but are otherwise ignored. @@ -3731,10 +3999,37 @@ fill_cobol_tm(cobol_tm &ctm, if( ch == internal_plus ) { // This flags a following hhmm offset. It needs to match a '+' or '-' - if( *source != internal_plus && *source != internal_minus ) + if( *source != internal_plus + && *source != internal_minus + && *source != internal_zero) { break; } + if( *source == internal_zero ) + { + // The next four characters have to be zeroes + if( source[1] != internal_zero ) + { + retval += 1; + break; + } + if( source[2] != internal_zero ) + { + retval += 2; + break; + } + if( source[3] != internal_zero ) + { + retval += 3; + break; + } + if( source[4] != internal_zero ) + { + retval += 4; + break; + } + } + in_offset = true; bump = 1; goto proceed; @@ -3866,28 +4161,45 @@ fill_cobol_tm(cobol_tm &ctm, if( ch == internal_s && in_nanoseconds ) { - errpos = gets_nanoseconds(source, source_end, ctm); + // Peel off digits to the right of the decimal point one at a time + errpos = gets_nanoseconds(format, format_end, source, source_end, ctm); if( errpos > 0 ) { retval += errpos - 1; break; } - bump = errpos; + bump = -errpos; + goto proceed; + } + + if( ch == internal_Z || ch == internal_z ) + { + // This has to be the end of the road + if( toupper(source[0]) != 'Z' ) + { + retval += 0; + break; + } + + convert_to_zulu(ctm); + bump = 1; goto proceed; } assert(false); - proceed: +proceed: retval += bump; format += bump; source += bump; } - if( format >= format_end ) + if( format >= format_end && source >= source_end) { // This means we processed the entire format string without seeing an error retval = 0; + + // Otherwise, either the format or source was too short } return retval; } @@ -3905,10 +4217,10 @@ __gg__test_formatted_datetime(cblc_field_t *dest, par2); __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + retval, + NO_RDIGITS, + truncation_e, + NULL); } extern "C" @@ -3937,10 +4249,10 @@ __gg__integer_of_formatted_date(cblc_field_t *dest, } __gg__int128_to_field(dest, - retval, - NO_RDIGITS, - truncation_e, - NULL); + retval, + NO_RDIGITS, + truncation_e, + NULL); } extern "C" @@ -3952,8 +4264,8 @@ __gg__seconds_from_formatted_time(cblc_field_t *dest, struct cobol_tm ctm = {}; double retval = fill_cobol_tm( ctm, - par1, - par2); + par1, + par2); if(retval > 0) { retval = 0; // Indicates there was a problem with the input data @@ -3967,3 +4279,747 @@ __gg__seconds_from_formatted_time(cblc_field_t *dest, truncation_e); } +extern "C" +void +__gg__hex_of(cblc_field_t *dest, + cblc_refer_t *par1) + { + static const char hex[17] = "0123456789ABCDEF"; + size_t bytes = par1->qual_size; + __gg__adjust_dest_size(dest, 2*bytes); + for(size_t i=0; i<bytes; i++) + { + unsigned char byte = par1->qual_data[i]; + dest->data[2*i] = hex[byte>>4]; + dest->data[2*i+1] = hex[byte&0xF]; + } + } + +extern "C" +void +__gg__highest_algebraic(cblc_field_t *dest, + cblc_refer_t *var_) + { + cblc_field_t *var = var_->field; + __int128 result = 0; + __int128 result_rdigits = 0; + + if( var->attr & scaled_e ) + { + result = __gg__power_of_ten(var->digits) - 1; + if( var->rdigits<0 ) + { + result *= __gg__power_of_ten(-var->rdigits); + } + else + { + result_rdigits = var->digits + var->rdigits; + } + } + else if( var->digits == 0 ) + { + result = (1<<(var->capacity*8)) -1 ; + if( var->attr & signable_e ) + { + result >>=1 ; + } + } + else + { + result_rdigits = var->rdigits; + result = __gg__power_of_ten(var->digits) - 1; + } + __gg__int128_to_field(dest, + result, + result_rdigits, + truncation_e, + NULL); + } + +extern "C" +void +__gg__lowest_algebraic(cblc_field_t *dest, + cblc_refer_t *var_) + { + cblc_field_t *var = var_->field; + __int128 result = 0; + __int128 result_rdigits = 0; + + if( var->attr & scaled_e ) + { + result = __gg__power_of_ten(var->digits) - 1; + if( var->rdigits<0 ) + { + result *= __gg__power_of_ten(-var->rdigits); + } + else + { + result_rdigits = var->digits + var->rdigits; + } + if( var->attr & signable_e ) + { + result = -result; + } + else + { + result = 0; + } + } + else if( var->digits == 0 ) + { + result = (1<<(var->capacity*8)) -1 ; + if( var->attr & signable_e ) + { + result >>=1 ; + result += 1; + result = -result; + } + else + { + result = 0; + } + } + else + { + result_rdigits = var->rdigits; + result = __gg__power_of_ten(var->digits) - 1; + if( var->attr & signable_e ) + { + result = -result; + } + else + { + result = 0; + } + } + __gg__int128_to_field(dest, + result, + result_rdigits, + truncation_e, + NULL); + } + +static int +floating_format_tester(char const * const f, char * const f_end) + { + int retval = -1; + char decimal_point = __gg__get_decimal_point(); + + enum + { + SPACE1, + SPACE2, + DIGITS1, + DIGITS2, + SPACE3, + SPACE4, + SPACE5, + DIGITS3, + SPACE6, + } state = SPACE1; + ssize_t index = 0; + while(index < f_end - f) + { + char ch = f[index]; + switch(state) + { + case SPACE1: + if( ch == internal_space ) + { + // Just keep looking + break; + } + if( ch == internal_minus + || ch == internal_plus) + { + state = SPACE2; + break; + } + if( ch >= internal_0 && ch <= internal_9 ) + { + state = DIGITS1; + break; + } + if( decimal_point ) + { + state = DIGITS2; + break; + } + // Disallowed character + retval = index; + break; + + case SPACE2: + if( ch == internal_space ) + { + break; + } + if( ch >= internal_0 && ch <= internal_9 ) + { + state = DIGITS1; + break; + } + if( ch == decimal_point ) + { + state = DIGITS2; + break; + } + retval = index; + break; + + case DIGITS1: + if( ch >= internal_0 && ch <= internal_9 ) + { + break; + } + if( ch == decimal_point ) + { + state = DIGITS2; + break; + } + if( ch == internal_space ) + { + state = SPACE3; + break; + } + retval = index; + break; + + case DIGITS2: + if( ch >= internal_0 && ch <= internal_9 ) + { + break; + } + if( ch == internal_space ) + { + state = SPACE3; + break; + } + if( ch == internal_E || ch == internal_e ) + { + state = SPACE4; + break; + } + retval = index; + break; + + case SPACE3: + if( ch == internal_space ) + { + break; + } + if( ch >= internal_0 && ch <= internal_9 ) + { + retval = index; + break; + } + if( ch == internal_E || ch == internal_e ) + { + state = SPACE4; + break; + } + retval = index; + break; + + case SPACE4: + if( ch == internal_space ) + { + break; + } + if( ch == internal_minus || ch == internal_plus ) + { + state = SPACE5; + break; + } + if( ch >= internal_0 && ch <= internal_9 ) + { + state = DIGITS3; + break; + } + retval = index; + break; + + case SPACE5: + if( ch == internal_space ) + { + break; + } + if( ch >= internal_0 && ch <= internal_9 ) + { + state = DIGITS3; + break; + } + retval = index; + break; + + case DIGITS3: + if( ch >= internal_0 && ch <= internal_9 ) + { + break; + } + if( ch == internal_space ) + { + state = SPACE6; + break; + } + retval = index; + break; + + case SPACE6: + if( ch == internal_space ) + { + break; + } + retval = index; + break; + } + + if( retval > -1 ) + { + break; + } + index += 1; + } + + retval += 1; + return retval; + } + +extern "C" +void +__gg__numval_f( cblc_field_t *dest, + cblc_refer_t *var) + { + _Float128 value = 0; + char *data = (char * )var->qual_data; + char *data_end = data + var->qual_size; + + int error = floating_format_tester(data, data_end); + + if( error || var->qual_size >= 256 ) + { + exception_raise(ec_argument_function_e); + } + else + { + // Get rid of any spaces in the string + char ach[256]; + char *p = ach; + while( data < data_end ) + { + char ch = *data++; + if( ch != internal_space ) + { + *p++ = ch; + } + } + *p++ = '\0'; + value = strtof128(ach, NULL); + } + __gg__float128_to_field(dest, + value, + truncation_e, + NULL); + } + +extern "C" +void +__gg__test_numval_f(cblc_field_t *dest, + cblc_refer_t *var) + { + char *data = (char * )var->qual_data; + char *data_end = data + var->qual_size; + + int error = floating_format_tester(data, data_end); + + __gg__int128_to_field(dest, + error, + NO_RDIGITS, + truncation_e, + NULL); + } + +static bool +ismatch(char *a1, char *a2, char *b1, char *b2) + { + bool retval = true; + while( a1 < a2 && b1 < b2 ) + { + if( *a1++ != *b1++ ) + { + retval = false; + } + } + return retval; + } + +static bool +iscasematch(char *a1, char *a2, char *b1, char *b2) + { + bool retval = true; + while( a1 < a2 && b1 < b2 ) + { + if( tolower(*a1++) != tolower(*b1++) ) + { + retval = false; + } + } + return retval; + } + +static char * +strstr(char *haystack, char *haystack_e, char *needle, char *needle_e) + { + char *retval = NULL; + char *pend = haystack_e - (needle_e - needle); + while( haystack <= pend ) + { + if(ismatch(haystack, haystack_e, needle, needle_e)) + { + retval = haystack; + break; + } + haystack += 1; + } + return retval; + } + +static char * +strcasestr(char *haystack, char *haystack_e, char *needle, char *needle_e) + { + char *retval = NULL; + char *pend = haystack_e - (needle_e - needle); + while( haystack <= pend ) + { + if(iscasematch(haystack, haystack_e, needle, needle_e)) + { + retval = haystack; + break; + } + haystack += 1; + } + return retval; + } + +static char * +strlaststr(char *haystack, char *haystack_e, char *needle, char *needle_e) + { + char *retval = NULL; + char *pend = haystack_e - (needle_e - needle); + while( haystack <= pend ) + { + if(ismatch(haystack, haystack_e, needle, needle_e)) + { + retval = haystack; + } + haystack += 1; + } + return retval; + } + +static char * +strcaselaststr(char *haystack, char *haystack_e, char *needle, char *needle_e) + { + char *retval = NULL; + char *pend = haystack_e - (needle_e - needle); + while( haystack <= pend ) + { + if(iscasematch(haystack, haystack_e, needle, needle_e)) + { + retval = haystack; + } + haystack += 1; + } + return retval; + } + + +extern "C" +void __gg__substitute(cblc_field_t *dest, + cblc_refer_t *arg1, + size_t N, + uint8_t *control, + cblc_refer_t *arg2, + cblc_refer_t *arg3) + { + // When EBCDIC becomes an issue, this will have to be modified heavily + ssize_t retval_size = 256; + char *retval = (char *)malloc(retval_size); + *retval = '\0'; + + char *haystack = (char *)arg1->qual_data; + char *haystack_e = (char *)arg1->qual_data + arg1->qual_size; + + ssize_t outdex = 0; + + char **pflasts = (char **)malloc(N * sizeof(char *)); + + if( arg1->qual_size == 0 ) + { + exception_raise(ec_argument_function_e); + goto bugout; + } + + for( size_t i=0; i<N; i++ ) + { + if( arg2[i].qual_size == 0 ) + { + exception_raise(ec_argument_function_e); + goto bugout; + } + if( control[i] & substitute_anycase_e ) + { + if( control[i] & substitute_first_e ) + { + pflasts[i] = strcasestr(haystack, + haystack_e, + (char *)arg2[i].qual_data, + (char *)arg2[i].qual_data + arg2[i].qual_size); + } + else if( control[i] & substitute_last_e) + { + pflasts[i] = strcaselaststr(haystack, + haystack_e, + (char *)arg2[i].qual_data, + (char *)arg2[i].qual_data + arg2[i].qual_size); + } + else + { + pflasts[i] = NULL; + } + } + else + { + if( control[i] & substitute_first_e ) + { + pflasts[i] = strstr(haystack, + haystack_e, + (char *)arg2[i].qual_data, + (char *)arg2[i].qual_data + arg2[i].qual_size); + } + else if( control[i] & substitute_last_e) + { + pflasts[i] = strlaststr(haystack, + haystack_e, + (char *)arg2[i].qual_data, + (char *)arg2[i].qual_data + arg2[i].qual_size); + } + else + { + pflasts[i] = NULL; + } + } + } + + while( haystack < haystack_e ) + { + bool did_something = false; + for( size_t i=0; i<N; i++ ) + { + // Let's make sure that there is enough room in the case that we add this + // arg + while( outdex - (ssize_t)arg2[i].qual_size + (ssize_t)arg3[i].qual_size + > retval_size ) + { + retval_size *= 2; + retval = (char *)realloc(retval, retval_size); + } + + // We checked earlier for FIRST/LAST matches + bool matched = pflasts[i] == haystack; + if( !matched ) + { + // It didn't match. But if it was flagged as FIRST or LAST, we need + // to skip it + + if( control[i] & (substitute_first_e|substitute_last_e) ) + { + continue; + } + + char *needle = (char *)arg2[i].qual_data; + char *needle_e = (char *)arg2[i].qual_data + arg2[i].qual_size; + matched = (control[i] & substitute_anycase_e) && iscasematch( + haystack, + haystack_e, + needle, + needle_e); + if( !matched ) + { + matched = !(control[i] & substitute_anycase_e) && ismatch(haystack, + haystack_e, + needle, + needle_e) ; + } + } + if( matched ) + { + haystack += arg2[i].qual_size; + memcpy(retval + outdex, arg3[i].qual_data, arg3[i].qual_size); + outdex += arg3[i].qual_size; + did_something = true; + break; + } + } + if( !did_something ) + { + while( outdex + 1 > retval_size ) + { + retval_size *= 2; + retval = (char *)realloc(retval, retval_size); + } + retval[outdex++] = *haystack++; + } + } + + bugout: + __gg__adjust_dest_size(dest, outdex); + memcpy(dest->data, retval, outdex); + + free(pflasts); + free(retval); + } + +extern "C" +void +__gg__locale_compare( cblc_field_t *dest, + cblc_refer_t *arg1, + cblc_refer_t *arg2, + cblc_refer_t *arg_locale) + { + char achretval[2] = "?"; + + if( arg_locale && arg_locale->field ) + { + // We don't yet know what to do with a locale + exception_raise(ec_locale_missing_e); + } + else + { + // Default locale + achretval[0] = '='; + size_t length = std::min(arg1->qual_size, arg2->qual_size); + for(size_t i=0; i<length; i++ ) + { + if( arg1->qual_data[i] < arg2->qual_data[i] ) + { + achretval[0] = '<'; + break; + } + if( arg1->qual_data[i] > arg2->qual_data[i] ) + { + achretval[0] = '>'; + break; + } + } + if( achretval[0] == '=' ) + { + if( arg1->qual_size < arg2->qual_size ) + { + achretval[0] = '<'; + } + else if( arg1->qual_size > arg2->qual_size ) + { + achretval[0] = '>'; + } + } + } + + __gg__adjust_dest_size(dest, 1); + ascii_to_internal_str(achretval, 1); + dest->data[0] = *achretval; + } + +extern "C" +void +__gg__locale_date(cblc_field_t *dest, + cblc_refer_t *arg1, + cblc_refer_t *arg_locale) + { + char ach[256] = " "; + + if( arg_locale && arg_locale->field ) + { + // We don't yet know what to do with a locale + exception_raise(ec_locale_missing_e); + } + else + { + // Default locale + tm tm; + memcpy(ach, arg1->qual_data, 8); + ach[8] = '\0'; + long ymd = atoi(ach); + tm.tm_year = ymd/10000 - 1900; + tm.tm_mon = ymd/100 % 100; + tm.tm_mday = ymd % 100; + strcpy(ach, nl_langinfo(D_FMT)); + strftime(ach, sizeof(ach), nl_langinfo(D_FMT), &tm); + } + + __gg__adjust_dest_size(dest, strlen(ach)); + ascii_to_internal_str(ach, strlen(ach)); + memcpy(dest->data, ach, strlen(ach)); + } + +extern "C" +void +__gg__locale_time(cblc_field_t *dest, + cblc_refer_t *arg1, + cblc_refer_t *arg_locale) + { + char ach[256] = " "; + + if( arg_locale && arg_locale->field ) + { + // We don't yet know what to do with a locale + exception_raise(ec_locale_missing_e); + } + else + { + // Default locale + tm tm = {}; + memcpy(ach, arg1->qual_data, 8); + ach[8] = '\0'; + long hms = atoi(ach); + tm.tm_hour = hms/10000; + tm.tm_min = hms/100 % 100; + tm.tm_sec = hms % 100; + strftime(ach, sizeof(ach), nl_langinfo(T_FMT), &tm); + } + + __gg__adjust_dest_size(dest, strlen(ach)); + ascii_to_internal_str(ach, strlen(ach)); + memcpy(dest->data, ach, strlen(ach)); + } + +extern "C" +void +__gg__locale_time_from_seconds( cblc_field_t *dest, + cblc_refer_t *arg1, + cblc_refer_t *arg_locale) + { + char ach[256] = " "; + + if( arg_locale && arg_locale->field ) + { + // We don't yet know what to do with a locale + exception_raise(ec_locale_missing_e); + } + else + { + // Default locale + tm tm = {}; + + int rdigits; + long seconds = (long)__gg__binary_value_from_refer(&rdigits, arg1); + tm.tm_hour = seconds/3600; + tm.tm_min = ((seconds%3600) / 60) % 100; + tm.tm_sec = seconds % 100; + strftime(ach, sizeof(ach), nl_langinfo(T_FMT), &tm); + } + + __gg__adjust_dest_size(dest, strlen(ach)); + ascii_to_internal_str(ach, strlen(ach)); + memcpy(dest->data, ach, strlen(ach)); + } diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc index 6111989fa2180de83f6e6859dea977cddb4d1237..860e460c72158bbf7c6863168d6db58cd3ca5cd3 100644 --- a/libgcobol/libgcobol.cc +++ b/libgcobol/libgcobol.cc @@ -99,6 +99,7 @@ static const char *stashed_exception_source_file; static int stashed_exception_line_number; static const char *stashed_exception_statement; + int __gg__call_parameter_count = A_ZILLION; static int sv_from_raise_statement = 0; @@ -3945,8 +3946,6 @@ __gg__initialize_variable(cblc_refer_t *var_ref, // Make a copy of the field pointer we're working with as a convenience: cblc_field_t *var = var_ref->field; - // fprintf(stderr, "__gg__initialize_variable %s\n", var->name); - // Set the "initialized" bit, which is tested in parser_symbol_add to make // sure this code gets executed only once. var->attr |= initialized_e; @@ -6602,11 +6601,21 @@ __gg__display( cblc_refer_t *var, static size_t display_string_size = MINIMUM_ALLOCATION_SIZE; static char *display_string = (char *)MALLOC(display_string_size); - format_for_display_internal(&display_string, - &display_string_size, - var->field, - var->qual_data, - var->qual_size ); + // if( var->qual_data ) + // { + format_for_display_internal(&display_string, + &display_string_size, + var->field, + var->qual_data, + var->qual_size ); + // } + // else + // { + // // This can happen during TRACE1=2 with LOCAL=STORAGE + // char achmsg[] = "<NULL>"; + // strcpy(display_string, achmsg); + // display_string_size = strlen(display_string); + // } // Let's honor the locale of the system, as best we can: static size_t converted_size = MINIMUM_ALLOCATION_SIZE; @@ -6617,6 +6626,18 @@ __gg__display( cblc_refer_t *var, ssize_t ss = write( file_descriptor, converted, strlen(converted)); + if(ss == -1) + { + fprintf(stderr, "__gg__display() %s %p\n", var->field->name, var->qual_data); + fprintf(stderr, "__gg__display() %zd\n", converted_size); + fprintf(stderr, "__gg__display() "); + for(size_t i=0; i<converted_size; i++) + { + fprintf(stderr, "%c(%2.2x) ", converted[i]<32 ? '?' : converted[i], converted[i]); + } + fprintf(stderr, "\n"); + } + assert(ss != -1); if( advance ) @@ -8115,12 +8136,12 @@ __gg__routine_to_call(char *name, } extern "C" -ssize_t +__int128 __gg__fetch_call_by_value_value(cblc_refer_t *var) { int rdigits; - ssize_t retval = 0; + __int128 retval = 0; switch(var->field->type) { case FldGroup: @@ -8143,7 +8164,7 @@ __gg__fetch_call_by_value_value(cblc_refer_t *var) break; case 16: - *(double *)(&retval) = double(*(_Float128 *)var->qual_data); + *(_Float128 *)(&retval) = double(*(_Float128 *)var->qual_data); break; } break; @@ -8157,8 +8178,7 @@ __gg__fetch_call_by_value_value(cblc_refer_t *var) case FldLiteralN: case FldIndex: case FldPointer: - retval = (ssize_t) __gg__binary_value_from_refer(&rdigits, - var); + retval = __gg__binary_value_from_refer(&rdigits, var); default: break; } @@ -8167,19 +8187,18 @@ __gg__fetch_call_by_value_value(cblc_refer_t *var) extern "C" void -__gg__assign_value_from_stack(cblc_field_t *dest, size_t parameter) +__gg__assign_value_from_stack(cblc_field_t *dest, __int128 parameter) { switch(dest->type) { case FldGroup: case FldAlphanumeric: case FldAlphaEdited: + case FldNumericEdited: if( dest->capacity >= 1) { - memset(dest->data, internal_space, dest->capacity); - // A single 8-bit character was placed in the 64-bit entry on the - // stack. - *(char *)dest->data = *(char *)parameter; + warnx("%s is not valid for BY VALUE", dest->name); + exit(1); } break; @@ -8188,15 +8207,15 @@ __gg__assign_value_from_stack(cblc_field_t *dest, size_t parameter) switch(dest->capacity) { case 4: - *(float *)(dest->data) = *(float *)parameter; + *(float *)(dest->data) = *(float *)¶meter; break; case 8: - *(double *)(dest->data) = *(double *)parameter; + *(double *)(dest->data) = *(double *)¶meter; break; case 16: - *(_Float128 *)(dest->data) = *(_Float128 *)parameter; + *(_Float128 *)(dest->data) = *(_Float128 *)¶meter; break; } break; @@ -8206,7 +8225,6 @@ __gg__assign_value_from_stack(cblc_field_t *dest, size_t parameter) case FldPacked: case FldNumericBin5: case FldNumericDisplay: - case FldNumericEdited: case FldLiteralN: case FldIndex: case FldPointer: @@ -9060,11 +9078,10 @@ __gg__func_exception_location(cblc_field_t *dest) } else { - // In actual production, this should be a single space - strcpy(ach, "<did you mean to turn LOCATION on?>"); + strcpy(ach, " "); } - __gg__adjust_dest_size(dest, strlen(ach)); } + __gg__adjust_dest_size(dest, strlen(ach)); memcpy(dest->data, ach, strlen(ach)); } @@ -9072,14 +9089,14 @@ extern "C" void __gg__func_exception_statement(cblc_field_t *dest) { - char ach[128]; + char ach[128] = " "; if(stashed_exception_statement) { snprintf(ach, sizeof(ach), "%s", stashed_exception_statement); ach[sizeof(ach)-1] = '\0'; - __gg__adjust_dest_size(dest, strlen(ach)); - memcpy(dest->data, ach, strlen(ach)); } + __gg__adjust_dest_size(dest, strlen(ach)); + memcpy(dest->data, ach, strlen(ach)); } extern "C" @@ -9099,15 +9116,13 @@ __gg__func_exception_status(cblc_field_t *dest) } p += 1; } - __gg__adjust_dest_size(dest, strlen(ach)); - memcpy(dest->data, ach, strlen(ach)); } else { strcpy(ach, " "); - __gg__adjust_dest_size(dest, strlen(ach)); - memcpy(dest->data, ach, strlen(ach)); } + __gg__adjust_dest_size(dest, strlen(ach)); + memcpy(dest->data, ach, strlen(ach)); } static cblc_file_t *recent_file = NULL;