Kaydet (Commit) 585b16b0 authored tarafından Colomban Wendling's avatar Colomban Wendling

COBOL: Import new upstream candidate parser

This fixes support for COBOL symbols after the recent breakage of regex
parsers, as well as introducing additional features and bug fixes.

Also import some of the tests.

https://github.com/universal-ctags/ctags/pull/2076

Part of #2119.
üst acebc2d6
...@@ -8,42 +8,516 @@ ...@@ -8,42 +8,516 @@
* files. * files.
*/ */
/* Some references:
* - https://www.cs.vu.nl/grammarware/browsable/cobol/
* - https://www.cs.vu.nl/grammarware/browsable/vs-cobol-ii/
* - https://open-cobol.sourceforge.io/guides/grammar.pdf
* - http://mapage.noos.fr/~bpinon/a_cobol_parser.htm
* - https://en.wikipedia.org/wiki/COBOL
*/
/* /*
* INCLUDE FILES * INCLUDE FILES
*/ */
#include "general.h" /* must always come first */ #include "general.h" /* must always come first */
#include "debug.h"
#include "entry.h"
#include "keyword.h"
#include "nestlevel.h"
#include "parse.h" #include "parse.h"
#include "read.h"
#include "routines.h" #include "routines.h"
static tagRegexTable cobolTagRegexTable[] = { typedef enum {
{"^[ \t]*[0-9]+[ \t]+([A-Z0-9][A-Z0-9-]*)[ \t]+(" K_FILE,
"BLANK|OCCURS|IS|JUST|PIC|REDEFINES|RENAMES|SIGN|SYNC|USAGE|VALUE" K_GROUP,
")", "\\1", K_PROGRAM,
"d,data,data items", "i", NULL}, K_SECTION,
{"^[ \t]*[FSR]D[ \t]+([A-Z0-9][A-Z0-9-]*)\\.", "\\1", K_DIVISION,
"f,file,file descriptions (FD, SD, RD)", "i", NULL}, K_PARAGRAPH,
{"^[ \t]*[0-9]+[ \t]+([A-Z0-9][A-Z0-9-]*)\\.", "\\1", K_DATA,
"g,group,group items", "i", NULL}, K_SOURCEFILE,
{"^[ \t]*([A-Z0-9][A-Z0-9-]*)\\.", "\\1", } cobolKind;
"p,paragraph,paragraphs", "i", NULL},
{"^[ \t]*PROGRAM-ID\\.[ \t]+([A-Z0-9][A-Z0-9-]*)\\.", "\\1", typedef enum {
"P,program,program ids", "i", NULL}, COBOL_SOURCEFILE_COPIED,
{"^[ \t]*([A-Z0-9][A-Z0-9-]*)[ \t]+SECTION\\.", "\\1", } cobolSourcefileRole;
"s,section,sections", "i", NULL},
static roleDefinition CobolSourcefileRoles [] = {
{ true, "copied", "copied in source file" },
}; };
/* static kindDefinition CobolKinds[] = {
* FUNCTION DEFINITIONS { true, 'f', "fd", "file descriptions (FD, SD, RD)" },
*/ { true, 'g', "group", "group items" },
{ true, 'P', "program", "program ids" },
{ true, 's', "section", "sections" },
{ true, 'D', "division", "divisions" },
{ true, 'p', "paragraph", "paragraphs" },
{ true, 'd', "data", "data items" },
{ true, 'S', "sourcefile", "source code file",
.referenceOnly = true, ATTACH_ROLES(CobolSourcefileRoles)},
};
static langType Lang_cobol;
enum {
KEYWORD_FD,
KEYWORD_SD,
KEYWORD_RD,
KEYWORD_SECTION,
KEYWORD_DIVISION,
KEYWORD_CONTINUE,
KEYWORD_END_EXEC,
KEYWORD_FILLER,
KEYWORD_BLANK,
KEYWORD_OCCURS,
KEYWORD_IS,
KEYWORD_JUST,
KEYWORD_PIC,
KEYWORD_REDEFINES,
KEYWORD_RENAMES,
KEYWORD_SIGN,
KEYWORD_SYNC,
KEYWORD_USAGE,
KEYWORD_VALUE,
KEYWORD_PROGRAM_ID,
KEYWORD_EXIT,
KEYWORD_COPY,
};
static const keywordTable cobolKeywordTable[] = {
#define DEFINE_KEYWORD(n) { #n, KEYWORD_##n }
DEFINE_KEYWORD (FD),
DEFINE_KEYWORD (SD),
DEFINE_KEYWORD (RD),
DEFINE_KEYWORD (SECTION),
DEFINE_KEYWORD (DIVISION),
DEFINE_KEYWORD (CONTINUE),
{ "END-EXEC", KEYWORD_END_EXEC },
DEFINE_KEYWORD (EXIT),
DEFINE_KEYWORD (FILLER),
DEFINE_KEYWORD (BLANK),
DEFINE_KEYWORD (OCCURS),
DEFINE_KEYWORD (IS),
DEFINE_KEYWORD (JUST),
DEFINE_KEYWORD (PIC),
{ "PICTURE", KEYWORD_PIC },
DEFINE_KEYWORD (REDEFINES),
DEFINE_KEYWORD (RENAMES),
DEFINE_KEYWORD (SIGN),
DEFINE_KEYWORD (SYNC),
DEFINE_KEYWORD (USAGE),
DEFINE_KEYWORD (VALUE),
{ "VALUES", KEYWORD_VALUE },
{ "PROGRAM-ID", KEYWORD_PROGRAM_ID },
DEFINE_KEYWORD (COPY),
};
#define INDICATOR_COLUMN 7
#define PROGRAM_NAME_AREA_COLUMN 73
#define isIdentifierChar(c) (isalnum(c) || (c) == '-')
#define isQuote(c) ((c) == '\'' || (c) == '"')
typedef enum {
/* Fixed: program starts at column 8, ends at column 72 */
FORMAT_FIXED = 0x1,
/* Free: program starts at column 1, no specific end */
FORMAT_FREE = 0x2,
/* Variable: program starts at column 8, no specific end */
FORMAT_VARIABLE = FORMAT_FIXED | FORMAT_FREE
} CobolFormat;
static struct {
vString *line;
unsigned long int lineNumber;
MIOPos filePosition;
const char *nextLine;
CobolFormat format;
} CblInputState;
static void cblppInit (const CobolFormat format)
{
CblInputState.line = vStringNew ();
CblInputState.lineNumber = 0;
CblInputState.nextLine = NULL;
CblInputState.format = format;
}
static void cblppDeinit (void)
{
vStringDelete (CblInputState.line);
}
static const char *cblppGetColumn (const char *line,
const unsigned int column)
{
unsigned int col = 0;
for (; *line; line++)
{
col += (*line == '\t') ? 8 : 1;
if (col >= column)
return line;
}
return NULL;
}
static void cblppAppendLine (vString *buffer,
const char *line)
{
if (CblInputState.format & FORMAT_FIXED)
{
const char *indicator = cblppGetColumn (line, INDICATOR_COLUMN);
if (indicator && *indicator && *indicator != '*' && *indicator != '/')
{
const char *lineStart = indicator + 1;
const char *lineEnd = cblppGetColumn (line, PROGRAM_NAME_AREA_COLUMN);
if (*indicator == '-')
{
vStringStripTrailing (buffer);
while (isspace (*lineStart))
lineStart++;
}
if (CblInputState.format == FORMAT_FIXED)
vStringNCatS (buffer, lineStart, lineEnd - lineStart);
else
vStringCatS (buffer, lineStart);
}
}
else if (line[0] != '*' && line[0] != '/')
vStringCatS (buffer, line);
}
/* TODO: skip *> comments */
static const char *cblppGetLine (void)
{
const char *line;
if (CblInputState.nextLine)
{
line = CblInputState.nextLine;
CblInputState.nextLine = NULL;
}
else
line = (const char *) readLineFromInputFile ();
CblInputState.lineNumber = getInputLineNumber ();
CblInputState.filePosition = getInputFilePosition ();
if (!line)
return NULL;
vStringClear (CblInputState.line);
cblppAppendLine (CblInputState.line, line);
/* check for continuation lines */
if (CblInputState.format & FORMAT_FIXED)
{
while (true)
{
const char *indicator;
line = (const char *) readLineFromInputFile ();
if (! line)
break;
indicator = cblppGetColumn (line, INDICATOR_COLUMN);
if (indicator && *indicator == '-')
cblppAppendLine (CblInputState.line, line);
else
break;
}
CblInputState.nextLine = line;
}
return vStringValue (CblInputState.line);
}
static void initCOBOLRefTagEntry (tagEntryInfo *e, const char *name,
const cobolKind kind, const int role)
{
initRefTagEntry (e, name, kind, role);
e->lineNumber = CblInputState.lineNumber;
e->filePosition = CblInputState.filePosition;
}
static void initCOBOLTagEntry (tagEntryInfo *e, const char *name, const cobolKind kind)
{
initCOBOLRefTagEntry (e, name, kind, ROLE_INDEX_DEFINITION);
}
static int makeCOBOLRefTag (const char *name, const cobolKind kind, const int role)
{
if (CobolKinds[kind].enabled)
{
tagEntryInfo e;
initCOBOLRefTagEntry (&e, name, kind, role);
return makeTagEntry (&e);
}
return CORK_NIL;
}
static int makeCOBOLTag (const char *name, const cobolKind kind)
{
return makeCOBOLRefTag (name, kind, ROLE_INDEX_DEFINITION);
}
#define CBL_NL(nl) (*((unsigned int *) (nestingLevelGetUserData (nl))))
static NestingLevel *popNestingLevelsToLevelNumber (NestingLevels *levels, const unsigned int levelNumber)
{
NestingLevel *nl;
while (true)
{
nl = nestingLevelsGetCurrent (levels);
if (! nl || CBL_NL (nl) < levelNumber)
break;
nestingLevelsPop (levels);
}
return nl;
}
static bool isNumeric (const char *nptr, unsigned long int *num)
{
char *endptr;
unsigned long int v;
v = strtoul (nptr, &endptr, 10);
if (nptr != endptr && *endptr == 0)
{
if (num)
*num = v;
return true;
}
return false;
}
static void findCOBOLTags (const CobolFormat format)
{
NestingLevels *levels;
const char *line;
cblppInit (format);
levels = nestingLevelsNew (sizeof (unsigned int));
while ((line = cblppGetLine ()) != NULL)
{
char word[64];
int keyword;
unsigned long int levelNumber;
#define READ_WHILE(word, cond) \
do { \
unsigned int i; \
for (i = 0; i < (ARRAY_SIZE (word) - 1) && *line && (cond); line++) \
word[i++] = *line; \
word[i] = 0; \
} while (0)
#define READ_LITERAL(word) \
do { \
const char READ_LITERAL__q = isQuote (*line) ? *line++ : 0; \
READ_WHILE (word, (READ_LITERAL__q && READ_LITERAL__q != *line) || \
isIdentifierChar (*line)); \
if (READ_LITERAL__q && READ_LITERAL__q == *line) \
line++; \
keyword = lookupCaseKeyword (word, Lang_cobol); \
} while (0)
#define READ_WORD(word, keyword) \
do { \
READ_WHILE (word, isIdentifierChar (*line)); \
keyword = lookupCaseKeyword (word, Lang_cobol); \
} while (0)
#define READ_KEYWORD(keyword) \
do { \
char READ_KEYWORD__word[64]; \
READ_WORD (READ_KEYWORD__word, keyword); \
} while (0)
#define SKIP_SPACES() do { while (isspace (*line)) line++; } while (0)
SKIP_SPACES ();
READ_WORD (word, keyword);
SKIP_SPACES ();
switch (keyword)
{
case KEYWORD_FD:
case KEYWORD_SD:
case KEYWORD_RD:
READ_WORD (word, keyword);
SKIP_SPACES ();
if (*word && *line == '.')
makeCOBOLTag (word, K_FILE);
break;
case KEYWORD_PROGRAM_ID:
if (*line == '.')
{
line++;
SKIP_SPACES ();
}
READ_LITERAL (word);
if (*word)
makeCOBOLTag (word, K_PROGRAM);
break;
case KEYWORD_COPY:
READ_WORD (word, keyword); // FIXME: also allow LITERAL
if (*word)
makeCOBOLRefTag (word, K_SOURCEFILE, COBOL_SOURCEFILE_COPIED);
break;
case KEYWORD_CONTINUE:
case KEYWORD_END_EXEC:
case KEYWORD_EXIT:
case KEYWORD_FILLER:
/* nothing, just ignore those in following cases */;
break;
default:
if (isNumeric (word, &levelNumber))
{
READ_WORD (word, keyword);
SKIP_SPACES ();
if (*word && keyword != KEYWORD_FILLER)
{
int kind = KIND_GHOST_INDEX;
if (*line == '.')
kind = K_GROUP;
else
{
int keyword2;
READ_KEYWORD (keyword2);
switch (keyword2)
{
case KEYWORD_BLANK:
case KEYWORD_OCCURS:
case KEYWORD_IS:
case KEYWORD_JUST:
case KEYWORD_PIC:
case KEYWORD_REDEFINES:
case KEYWORD_RENAMES:
case KEYWORD_SIGN:
case KEYWORD_SYNC:
case KEYWORD_USAGE:
case KEYWORD_VALUE:
kind = K_DATA;
}
}
if (kind != KIND_GHOST_INDEX)
{
NestingLevel *nl;
tagEntryInfo entry;
int r;
unsigned int nestingLevelNumber;
/* for nesting purposes, level 77 is identical to 1,
* and 66 to 2 */
switch (levelNumber)
{
default: nestingLevelNumber = levelNumber; break;
case 77: nestingLevelNumber = 1; break;
case 66: nestingLevelNumber = 2; break;
}
nl = popNestingLevelsToLevelNumber (levels, nestingLevelNumber);
initCOBOLTagEntry (&entry, word, kind);
if (nl && CBL_NL (nl) < nestingLevelNumber)
entry.extensionFields.scopeIndex = nl->corkIndex;
r = makeTagEntry (&entry);
if (levelNumber < 50 /* exclude special levels */)
{
nl = nestingLevelsPush (levels, r);
CBL_NL (nl) = levelNumber;
}
}
}
}
else if (*word && *line == '.')
makeCOBOLTag (word, K_PARAGRAPH);
else
{
int keyword2;
READ_KEYWORD (keyword2);
SKIP_SPACES ();
if (keyword2 == KEYWORD_DIVISION && *line == '.')
makeCOBOLTag (word, K_DIVISION);
else if (keyword2 == KEYWORD_SECTION && *line == '.')
makeCOBOLTag (word, K_SECTION);
}
}
}
nestingLevelsFree (levels);
cblppDeinit ();
}
static void findCOBOLFixedTags (void)
{
findCOBOLTags (FORMAT_FIXED);
}
static void findCOBOLFreeTags (void)
{
findCOBOLTags (FORMAT_FREE);
}
static void findCOBOLVariableTags (void)
{
findCOBOLTags (FORMAT_VARIABLE);
}
static void initializeCobolParser (langType language)
{
Lang_cobol = language;
}
static parserDefinition* commonCobolParserDefinition (const char *name,
simpleParser parser)
{
parserDefinition* def = parserNew (name);
def->initialize = initializeCobolParser;
def->parser = parser;
def->kindTable = CobolKinds;
def->kindCount = ARRAY_SIZE(CobolKinds);
def->keywordTable = cobolKeywordTable;
def->keywordCount = ARRAY_SIZE(cobolKeywordTable);
def->useCork = true;
return def;
}
extern parserDefinition* CobolParser (void) extern parserDefinition* CobolParser (void)
{ {
static const char *const extensions [] = { static const char *const extensions [] = {
"cbl", "cob", "cpy", "CBL", "COB", NULL }; "cbl", "cob", "CBL", "COB", NULL };
parserDefinition* def = parserNew ("Cobol"); parserDefinition* def = commonCobolParserDefinition ("Cobol",
findCOBOLFixedTags);
def->extensions = extensions; def->extensions = extensions;
def->tagRegexTable = cobolTagRegexTable;
def->tagRegexCount = ARRAY_SIZE (cobolTagRegexTable);
def->method = METHOD_NOT_CRAFTED|METHOD_REGEX;
return def; return def;
} }
extern parserDefinition* CobolFreeParser (void)
{
return commonCobolParserDefinition ("CobolFree", findCOBOLFreeTags);
}
extern parserDefinition* CobolVariableParser (void)
{
return commonCobolParserDefinition ("CobolVariable", findCOBOLVariableTags);
}
...@@ -490,10 +490,12 @@ static void add_top_level_items(GeanyDocument *doc) ...@@ -490,10 +490,12 @@ static void add_top_level_items(GeanyDocument *doc)
tag_list_add_groups(tag_store, tag_list_add_groups(tag_store,
&tv_iters.tag_class, _("Program"), ICON_CLASS, &tv_iters.tag_class, _("Program"), ICON_CLASS,
&tv_iters.tag_function, _("File"), ICON_METHOD, &tv_iters.tag_function, _("File"), ICON_METHOD,
&tv_iters.tag_interface, _("Divisions"), ICON_NAMESPACE,
&tv_iters.tag_namespace, _("Sections"), ICON_NAMESPACE, &tv_iters.tag_namespace, _("Sections"), ICON_NAMESPACE,
&tv_iters.tag_macro, _("Paragraph"), ICON_OTHER, &tv_iters.tag_macro, _("Paragraph"), ICON_OTHER,
&tv_iters.tag_struct, _("Group"), ICON_STRUCT, &tv_iters.tag_struct, _("Group"), ICON_STRUCT,
&tv_iters.tag_variable, _("Data"), ICON_VAR, &tv_iters.tag_variable, _("Data"), ICON_VAR,
&tv_iters.tag_externvar, _("Copies"), ICON_NAMESPACE,
NULL); NULL);
break; break;
case GEANY_FILETYPES_CONF: case GEANY_FILETYPES_CONF:
......
...@@ -423,11 +423,13 @@ static TMParserMapEntry map_R[] = { ...@@ -423,11 +423,13 @@ static TMParserMapEntry map_R[] = {
static TMParserMapEntry map_COBOL[] = { static TMParserMapEntry map_COBOL[] = {
{'d', tm_tag_variable_t}, {'d', tm_tag_variable_t},
{'D', tm_tag_interface_t},
{'f', tm_tag_function_t}, {'f', tm_tag_function_t},
{'g', tm_tag_struct_t}, {'g', tm_tag_struct_t},
{'p', tm_tag_macro_t}, {'p', tm_tag_macro_t},
{'P', tm_tag_class_t}, {'P', tm_tag_class_t},
{'s', tm_tag_namespace_t}, {'s', tm_tag_namespace_t},
{'S', tm_tag_externvar_t},
}; };
static TMParserMapEntry map_OBJC[] = { static TMParserMapEntry map_OBJC[] = {
...@@ -768,6 +770,7 @@ gboolean tm_parser_has_full_context(TMParserType lang) ...@@ -768,6 +770,7 @@ gboolean tm_parser_has_full_context(TMParserType lang)
case TM_PARSER_C: case TM_PARSER_C:
case TM_PARSER_CPP: case TM_PARSER_CPP:
case TM_PARSER_CSHARP: case TM_PARSER_CSHARP:
case TM_PARSER_COBOL:
case TM_PARSER_D: case TM_PARSER_D:
case TM_PARSER_FERITE: case TM_PARSER_FERITE:
case TM_PARSER_GLSL: case TM_PARSER_GLSL:
......
dist_check_SCRIPTS = runner.sh dist_check_SCRIPTS = runner.sh
# Disabled tests:
# simple.cbl
NULL = NULL =
test_sources = \ test_sources = \
1795612.js \ 1795612.js \
...@@ -117,6 +114,11 @@ test_sources = \ ...@@ -117,6 +114,11 @@ test_sources = \
case_sensitivity.php \ case_sensitivity.php \
char-selector.f90 \ char-selector.f90 \
classes.php \ classes.php \
cobol/helloworld.cbl \
cobol/helloworld2.cbl \
cobol/levels.cbl \
cobol/quoted-program-id.cbl \
cobol/simple.cbl \
common.f \ common.f \
complex-return.js \ complex-return.js \
continuation.f90 \ continuation.f90 \
......
00000* VALIDATION OF BASE COBOL INSTALL 00050000
01000 IDENTIFICATION DIVISION. 00060000
01100 PROGRAM-ID. 'HELLO'. 00070000
02000 ENVIRONMENT DIVISION. 00080000
02100 CONFIGURATION SECTION. 00090000
02110 SOURCE-COMPUTER. GNULINUX. 00100000
02120 OBJECT-COMPUTER. HERCULES. 00110000
02200 SPECIAL-NAMES. 00120000
02210 CONSOLE IS CONSL. 00130000
03000 DATA DIVISION. 00140000
04000 PROCEDURE DIVISION. 00150000
04100 00-MAIN. 00160000
04110 DISPLAY 'HELLO, WORLD' UPON CONSL. 00170000
04900 STOP RUN. 00180000
# format=tagmanager
00-MAIN655360
CONFIGURATION2560
DATA320
ENVIRONMENT320
HELLO10
IDENTIFICATION320
OBJECT-COMPUTER655360
PROCEDURE320
SOURCE-COMPUTER655360
SPECIAL-NAMES655360
00000* VALIDATION OF BASE COBOL INSTALL
IDENTIFICATION DIVISION.
01100 PROGRAM-ID. 'HELLO WORLD'.
02000 ENVIRONMENT DIV
-ISION.
02100 CONFIGURATION SECTION.
02110 SOURCE-COMPUTER. GNULINUX.
02120 OBJECT-COMPUTER. HERCULES.
02200 SPECIAL
--NAMES.
02210 CONSOLE IS CONSL.
03000 DATA DIVISION.
04000 PROCEDUR
- E DIVISION.
04100 00-MAIN.
04110 DISPLAY
'HELLO, WORLD'
UPON CONSL.
04900 STOP RUN.
# format=tagmanager
00-MAIN655360
CONFIGURATION2560
DATA320
ENVIRONMENT320
HELLO WORLD10
IDENTIFICATION320
OBJECT-COMPUTER655360
PROCEDURE320
SOURCE-COMPUTER655360
SPECIAL-NAMES655360
IDENTIFICATION DIVISION.
PROGRAM-ID. Test-Items.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 RECORD1.
05 ITEM1 PIC X(1).
05 ITEM2 PIC A(1).
88 ODD VALUES 1, 3, 5, 7, 9.
88 EVEN VALUES 2, 4, 6, 8.
05 ITEM3 PIC X(6).
66 RDITEM4 RENAMES ITEM1 THRU ITEM2.
66 RDITEM5 RENAMES ITEM1 THROUGH ITEM3.
77 STDLN1 PIC A(4).
01 REC2.
02 G1.
05 ITEM1 PICTURE X(10).
05 ITEM2 PIC X(10).
66 OTHERNAME1 RENAMES ITEM1 IN REC2.
66 OTHERNAME2 RENAMES G1.
PROCEDURE DIVISION.
MAIN-PROCEDURE.
DISPLAY "Hello world".
DISPLAY OTHERNAME2.
STOP RUN.
END PROGRAM Test-Items.
# format=tagmanager
DATA320
EVEN16384RECORD1.ITEM20
G12048REC20
IDENTIFICATION320
ITEM116384REC2.G10
ITEM116384RECORD10
ITEM216384REC2.G10
ITEM216384RECORD10
ITEM316384RECORD10
MAIN-PROCEDURE655360
ODD16384RECORD1.ITEM20
OTHERNAME116384REC20
OTHERNAME216384REC20
PROCEDURE320
RDITEM416384RECORD10
RDITEM516384RECORD10
REC220480
RECORD120480
STDLN1163840
Test-Items10
WORKING-STORAGE2560
IDENTIFICATION DIVISION.
PROGRAM-ID. 'Test program name'.
PROCEDURE DIVISION.
MAIN.
DISPLAY 'This is a test program'.
STOP RUN.
END PROGRAM 'Test program name'.
# format=tagmanager
IDENTIFICATION320
MAIN655360
PROCEDURE320
Test program name10
# format=tagmanager # format=tagmanager
AUTHOR655360 AUTHOR655360
Begin655360 Begin655360
DW-DAYS-IN-MONTH163840 DATA320
DW-DAYS-IN-MONTHS163840 DW-DAYS-IN-MONTH16384Group-Name.Data-Item1.DW-DAYS-IN-MONTHS0
Data-Item1163840 DW-DAYS-IN-MONTHS16384Group-Name.Data-Item10
Data-Item116384Group-Name0
ENVIRONMENT320
FILE2560 FILE2560
FILE-CONTROL655360 FILE-CONTROL655360
File-Data-Item163840 File-Data-Item16384File-Group-Name0
File-Group-Name20480 File-Group-Name20480
File-Name160 File-Name160
Group-Name20480 Group-Name20480
IDENTIFICATION320
INPUT-OUPUT2560 INPUT-OUPUT2560
PROGRAM-ID655360 PROCEDURE320
Program-Name10 Program-Name10
SH-WORK-MMDDYYYY163840 SH-WORK-MMDDYYYY16384Group-Name.Data-Item10
SH-WORK-MMDDYYYY-2163840 SH-WORK-MMDDYYYY-216384Group-Name.Data-Item10
Subprogram-Name655360 Subprogram-Name655360
WORKING-STORAGE2560 WORKING-STORAGE2560
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment