2 * Copyright (c) 2000-2003, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for generating tags for COBOL language
12 * - https://www.cs.vu.nl/grammarware/browsable/cobol/
13 * - https://www.cs.vu.nl/grammarware/browsable/vs-cobol-ii/
14 * - https://open-cobol.sourceforge.io/guides/grammar.pdf
15 * - http://mapage.noos.fr/~bpinon/a_cobol_parser.htm
16 * - https://en.wikipedia.org/wiki/COBOL
22 #include "general.h" /* must always come first */
26 #include "nestlevel.h"
43 COBOL_SOURCEFILE_COPIED
,
44 } cobolSourcefileRole
;
46 static roleDefinition CobolSourcefileRoles
[] = {
47 { true, "copied", "copied in source file" },
50 static kindDefinition CobolKinds
[] = {
51 { true, 'f', "fd", "file descriptions (FD, SD, RD)" },
52 { true, 'g', "group", "group items" },
53 { true, 'P', "program", "program ids" },
54 { true, 's', "section", "sections" },
55 { true, 'D', "division", "divisions" },
56 { true, 'p', "paragraph", "paragraphs" },
57 { true, 'd', "data", "data items" },
58 { true, 'S', "sourcefile", "source code file",
59 .referenceOnly
= true, ATTACH_ROLES(CobolSourcefileRoles
)},
62 static langType Lang_cobol
;
89 static const keywordTable cobolKeywordTable
[] = {
90 #define DEFINE_KEYWORD(n) { #n, KEYWORD_##n }
94 DEFINE_KEYWORD (SECTION
),
95 DEFINE_KEYWORD (DIVISION
),
96 DEFINE_KEYWORD (CONTINUE
),
97 { "END-EXEC", KEYWORD_END_EXEC
},
98 DEFINE_KEYWORD (EXIT
),
99 DEFINE_KEYWORD (FILLER
),
100 DEFINE_KEYWORD (BLANK
),
101 DEFINE_KEYWORD (OCCURS
),
103 DEFINE_KEYWORD (JUST
),
104 DEFINE_KEYWORD (PIC
),
105 { "PICTURE", KEYWORD_PIC
},
106 DEFINE_KEYWORD (REDEFINES
),
107 DEFINE_KEYWORD (RENAMES
),
108 DEFINE_KEYWORD (SIGN
),
109 DEFINE_KEYWORD (SYNC
),
110 DEFINE_KEYWORD (USAGE
),
111 DEFINE_KEYWORD (VALUE
),
112 { "VALUES", KEYWORD_VALUE
},
113 { "PROGRAM-ID", KEYWORD_PROGRAM_ID
},
114 DEFINE_KEYWORD (COPY
),
117 #define INDICATOR_COLUMN 7
118 #define PROGRAM_NAME_AREA_COLUMN 73
120 #define isIdentifierChar(c) (isalnum(c) || (c) == '-')
121 #define isQuote(c) ((c) == '\'' || (c) == '"')
124 /* Fixed: program starts at column 8, ends at column 72 */
126 /* Free: program starts at column 1, no specific end */
128 /* Variable: program starts at column 8, no specific end */
129 FORMAT_VARIABLE
= FORMAT_FIXED
| FORMAT_FREE
134 unsigned long int lineNumber
;
136 const char *nextLine
;
140 static void cblppInit (const CobolFormat format
)
142 CblInputState
.line
= vStringNew ();
143 CblInputState
.lineNumber
= 0;
144 CblInputState
.nextLine
= NULL
;
145 CblInputState
.format
= format
;
148 static void cblppDeinit (void)
150 vStringDelete (CblInputState
.line
);
153 static const char *cblppGetColumn (const char *line
,
154 const unsigned int column
)
156 unsigned int col
= 0;
158 for (; *line
; line
++)
160 col
+= (*line
== '\t') ? 8 : 1;
168 static void cblppAppendLine (vString
*buffer
,
171 if (CblInputState
.format
& FORMAT_FIXED
)
173 const char *indicator
= cblppGetColumn (line
, INDICATOR_COLUMN
);
175 if (indicator
&& *indicator
&& *indicator
!= '*' && *indicator
!= '/')
177 const char *lineStart
= indicator
+ 1;
178 const char *lineEnd
= cblppGetColumn (line
, PROGRAM_NAME_AREA_COLUMN
);
180 if (*indicator
== '-')
182 vStringStripTrailing (buffer
);
183 while (isspace (*lineStart
))
187 if (CblInputState
.format
== FORMAT_FIXED
)
188 vStringNCatS (buffer
, lineStart
, lineEnd
- lineStart
);
190 vStringCatS (buffer
, lineStart
);
193 else if (line
[0] != '*' && line
[0] != '/')
194 vStringCatS (buffer
, line
);
197 /* TODO: skip *> comments */
198 static const char *cblppGetLine (void)
202 if (CblInputState
.nextLine
)
204 line
= CblInputState
.nextLine
;
205 CblInputState
.nextLine
= NULL
;
208 line
= (const char *) readLineFromInputFile ();
210 CblInputState
.lineNumber
= getInputLineNumber ();
211 CblInputState
.filePosition
= getInputFilePosition ();
216 vStringClear (CblInputState
.line
);
217 cblppAppendLine (CblInputState
.line
, line
);
219 /* check for continuation lines */
220 if (CblInputState
.format
& FORMAT_FIXED
)
224 const char *indicator
;
225 line
= (const char *) readLineFromInputFile ();
228 indicator
= cblppGetColumn (line
, INDICATOR_COLUMN
);
229 if (indicator
&& *indicator
== '-')
230 cblppAppendLine (CblInputState
.line
, line
);
235 CblInputState
.nextLine
= line
;
238 return vStringValue (CblInputState
.line
);
241 static void initCOBOLRefTagEntry (tagEntryInfo
*e
, const char *name
,
242 const cobolKind kind
, const int role
)
244 initRefTagEntry (e
, name
, kind
, role
);
245 e
->lineNumber
= CblInputState
.lineNumber
;
246 e
->filePosition
= CblInputState
.filePosition
;
249 static void initCOBOLTagEntry (tagEntryInfo
*e
, const char *name
, const cobolKind kind
)
251 initCOBOLRefTagEntry (e
, name
, kind
, ROLE_DEFINITION_INDEX
);
254 static int makeCOBOLRefTag (const char *name
, const cobolKind kind
, const int role
)
256 if (CobolKinds
[kind
].enabled
)
260 initCOBOLRefTagEntry (&e
, name
, kind
, role
);
262 return makeTagEntry (&e
);
268 static int makeCOBOLTag (const char *name
, const cobolKind kind
)
270 return makeCOBOLRefTag (name
, kind
, ROLE_DEFINITION_INDEX
);
273 #define CBL_NL(nl) (*((unsigned int *) (nestingLevelGetUserData (nl))))
275 static NestingLevel
*popNestingLevelsToLevelNumber (NestingLevels
*levels
, const unsigned int levelNumber
)
281 nl
= nestingLevelsGetCurrent (levels
);
282 if (! nl
|| CBL_NL (nl
) < levelNumber
)
284 nestingLevelsPop (levels
);
290 static bool isNumeric (const char *nptr
, unsigned long int *num
)
295 v
= strtoul (nptr
, &endptr
, 10);
296 if (nptr
!= endptr
&& *endptr
== 0)
305 static void findCOBOLTags (const CobolFormat format
)
307 NestingLevels
*levels
;
312 levels
= nestingLevelsNew (sizeof (unsigned int));
314 while ((line
= cblppGetLine ()) != NULL
)
318 unsigned long int levelNumber
;
320 #define READ_WHILE(word, cond) \
323 for (i = 0; i < (ARRAY_SIZE (word) - 1) && *line && (cond); line++) \
327 #define READ_LITERAL(word) \
329 const char READ_LITERAL__q = isQuote (*line) ? *line++ : 0; \
330 READ_WHILE (word, (READ_LITERAL__q && READ_LITERAL__q != *line) || \
331 isIdentifierChar (*line)); \
332 if (READ_LITERAL__q && READ_LITERAL__q == *line) \
334 keyword = lookupCaseKeyword (word, Lang_cobol); \
336 #define READ_WORD(word, keyword) \
338 READ_WHILE (word, isIdentifierChar (*line)); \
339 keyword = lookupCaseKeyword (word, Lang_cobol); \
341 #define READ_KEYWORD(keyword) \
343 char READ_KEYWORD__word[64]; \
344 READ_WORD (READ_KEYWORD__word, keyword); \
346 #define SKIP_SPACES() do { while (isspace (*line)) line++; } while (0)
349 READ_WORD (word
, keyword
);
357 READ_WORD (word
, keyword
);
359 if (*word
&& *line
== '.')
360 makeCOBOLTag (word
, K_FILE
);
363 case KEYWORD_PROGRAM_ID
:
371 makeCOBOLTag (word
, K_PROGRAM
);
375 READ_WORD (word
, keyword
); // FIXME: also allow LITERAL
377 makeCOBOLRefTag (word
, K_SOURCEFILE
, COBOL_SOURCEFILE_COPIED
);
380 case KEYWORD_CONTINUE
:
381 case KEYWORD_END_EXEC
:
384 /* nothing, just ignore those in following cases */;
388 if (isNumeric (word
, &levelNumber
))
390 READ_WORD (word
, keyword
);
393 if (*word
&& keyword
!= KEYWORD_FILLER
)
395 int kind
= KIND_GHOST_INDEX
;
403 READ_KEYWORD (keyword2
);
411 case KEYWORD_REDEFINES
:
412 case KEYWORD_RENAMES
:
421 if (kind
!= KIND_GHOST_INDEX
)
426 unsigned int nestingLevelNumber
;
428 /* for nesting purposes, level 77 is identical to 1,
432 default: nestingLevelNumber
= levelNumber
; break;
433 case 77: nestingLevelNumber
= 1; break;
434 case 66: nestingLevelNumber
= 2; break;
437 nl
= popNestingLevelsToLevelNumber (levels
, nestingLevelNumber
);
438 initCOBOLTagEntry (&entry
, word
, kind
);
439 if (nl
&& CBL_NL (nl
) < nestingLevelNumber
)
440 entry
.extensionFields
.scopeIndex
= nl
->corkIndex
;
441 r
= makeTagEntry (&entry
);
442 if (levelNumber
< 50 /* exclude special levels */)
444 nl
= nestingLevelsPush (levels
, r
);
445 CBL_NL (nl
) = levelNumber
;
450 else if (*word
&& *line
== '.')
451 makeCOBOLTag (word
, K_PARAGRAPH
);
456 READ_KEYWORD (keyword2
);
459 if (keyword2
== KEYWORD_DIVISION
&& *line
== '.')
460 makeCOBOLTag (word
, K_DIVISION
);
461 else if (keyword2
== KEYWORD_SECTION
&& *line
== '.')
462 makeCOBOLTag (word
, K_SECTION
);
467 nestingLevelsFree (levels
);
471 static void findCOBOLFixedTags (void)
473 findCOBOLTags (FORMAT_FIXED
);
476 static void findCOBOLFreeTags (void)
478 findCOBOLTags (FORMAT_FREE
);
481 static void findCOBOLVariableTags (void)
483 findCOBOLTags (FORMAT_VARIABLE
);
486 static void initializeCobolParser (langType language
)
488 Lang_cobol
= language
;
491 static parserDefinition
* commonCobolParserDefinition (const char *name
,
494 parserDefinition
* def
= parserNew (name
);
495 def
->initialize
= initializeCobolParser
;
496 def
->parser
= parser
;
497 def
->kindTable
= CobolKinds
;
498 def
->kindCount
= ARRAY_SIZE(CobolKinds
);
499 def
->keywordTable
= cobolKeywordTable
;
500 def
->keywordCount
= ARRAY_SIZE(cobolKeywordTable
);
501 def
->useCork
= CORK_QUEUE
;
505 extern parserDefinition
* CobolParser (void)
507 static const char *const extensions
[] = {
508 "cbl", "cob", "CBL", "COB", NULL
};
509 parserDefinition
* def
= commonCobolParserDefinition ("Cobol",
511 def
->extensions
= extensions
;
515 extern parserDefinition
* CobolFreeParser (void)
517 return commonCobolParserDefinition ("CobolFree", findCOBOLFreeTags
);
520 extern parserDefinition
* CobolVariableParser (void)
522 return commonCobolParserDefinition ("CobolVariable", findCOBOLVariableTags
);