geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

geany_cobol.c
Go to the documentation of this file.
1/*
2* Copyright (c) 2000-2003, Darren Hiebert
3*
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.
6*
7* This module contains functions for generating tags for COBOL language
8* files.
9*/
10
11/* Some references:
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
17 */
18
19/*
20* INCLUDE FILES
21*/
22#include "general.h" /* must always come first */
23#include "debug.h"
24#include "entry.h"
25#include "keyword.h"
26#include "nestlevel.h"
27#include "parse.h"
28#include "read.h"
29#include "routines.h"
30
31typedef enum {
40} cobolKind;
41
42typedef enum {
45
47 { true, "copied", "copied in source file" },
48};
49
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",
60};
61
63
64enum {
87};
88
90#define DEFINE_KEYWORD(n) { #n, KEYWORD_##n }
91 DEFINE_KEYWORD (FD),
92 DEFINE_KEYWORD (SD),
93 DEFINE_KEYWORD (RD),
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),
102 DEFINE_KEYWORD (IS),
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),
115};
116
117#define INDICATOR_COLUMN 7
118#define PROGRAM_NAME_AREA_COLUMN 73
119
120#define isIdentifierChar(c) (isalnum(c) || (c) == '-')
121#define isQuote(c) ((c) == '\'' || (c) == '"')
122
123typedef enum {
124 /* Fixed: program starts at column 8, ends at column 72 */
125 FORMAT_FIXED = 0x1,
126 /* Free: program starts at column 1, no specific end */
127 FORMAT_FREE = 0x2,
128 /* Variable: program starts at column 8, no specific end */
129 FORMAT_VARIABLE = FORMAT_FIXED | FORMAT_FREE
130} CobolFormat;
131
132static struct {
133 vString *line;
134 unsigned long int lineNumber;
135 MIOPos filePosition;
136 const char *nextLine;
137 CobolFormat format;
138} CblInputState;
139
140static void cblppInit (const CobolFormat format)
141{
142 CblInputState.line = vStringNew ();
143 CblInputState.lineNumber = 0;
144 CblInputState.nextLine = NULL;
145 CblInputState.format = format;
146}
147
148static void cblppDeinit (void)
149{
150 vStringDelete (CblInputState.line);
151}
152
153static const char *cblppGetColumn (const char *line,
154 const unsigned int column)
155{
156 unsigned int col = 0;
157
158 for (; *line; line++)
159 {
160 col += (*line == '\t') ? 8 : 1;
161 if (col >= column)
162 return line;
163 }
164
165 return NULL;
166}
167
168static void cblppAppendLine (vString *buffer,
169 const char *line)
170{
171 if (CblInputState.format & FORMAT_FIXED)
172 {
173 const char *indicator = cblppGetColumn (line, INDICATOR_COLUMN);
174
175 if (indicator && *indicator && *indicator != '*' && *indicator != '/')
176 {
177 const char *lineStart = indicator + 1;
178 const char *lineEnd = cblppGetColumn (line, PROGRAM_NAME_AREA_COLUMN);
179
180 if (*indicator == '-')
181 {
182 vStringStripTrailing (buffer);
183 while (isspace (*lineStart))
184 lineStart++;
185 }
186
187 if (CblInputState.format == FORMAT_FIXED)
188 vStringNCatS (buffer, lineStart, lineEnd - lineStart);
189 else
190 vStringCatS (buffer, lineStart);
191 }
192 }
193 else if (line[0] != '*' && line[0] != '/')
194 vStringCatS (buffer, line);
195}
196
197/* TODO: skip *> comments */
198static const char *cblppGetLine (void)
199{
200 const char *line;
201
202 if (CblInputState.nextLine)
203 {
204 line = CblInputState.nextLine;
205 CblInputState.nextLine = NULL;
206 }
207 else
208 line = (const char *) readLineFromInputFile ();
209
210 CblInputState.lineNumber = getInputLineNumber ();
211 CblInputState.filePosition = getInputFilePosition ();
212
213 if (!line)
214 return NULL;
215
216 vStringClear (CblInputState.line);
217 cblppAppendLine (CblInputState.line, line);
218
219 /* check for continuation lines */
220 if (CblInputState.format & FORMAT_FIXED)
221 {
222 while (true)
223 {
224 const char *indicator;
225 line = (const char *) readLineFromInputFile ();
226 if (! line)
227 break;
228 indicator = cblppGetColumn (line, INDICATOR_COLUMN);
229 if (indicator && *indicator == '-')
230 cblppAppendLine (CblInputState.line, line);
231 else
232 break;
233 }
234
235 CblInputState.nextLine = line;
236 }
237
238 return vStringValue (CblInputState.line);
239}
240
241static void initCOBOLRefTagEntry (tagEntryInfo *e, const char *name,
242 const cobolKind kind, const int role)
243{
244 initRefTagEntry (e, name, kind, role);
245 e->lineNumber = CblInputState.lineNumber;
246 e->filePosition = CblInputState.filePosition;
247}
248
249static void initCOBOLTagEntry (tagEntryInfo *e, const char *name, const cobolKind kind)
250{
251 initCOBOLRefTagEntry (e, name, kind, ROLE_DEFINITION_INDEX);
252}
253
254static int makeCOBOLRefTag (const char *name, const cobolKind kind, const int role)
255{
256 if (CobolKinds[kind].enabled)
257 {
258 tagEntryInfo e;
259
260 initCOBOLRefTagEntry (&e, name, kind, role);
261
262 return makeTagEntry (&e);
263 }
264
265 return CORK_NIL;
266}
267
268static int makeCOBOLTag (const char *name, const cobolKind kind)
269{
270 return makeCOBOLRefTag (name, kind, ROLE_DEFINITION_INDEX);
271}
272
273#define CBL_NL(nl) (*((unsigned int *) (nestingLevelGetUserData (nl))))
274
275static NestingLevel *popNestingLevelsToLevelNumber (NestingLevels *levels, const unsigned int levelNumber)
276{
277 NestingLevel *nl;
278
279 while (true)
280 {
281 nl = nestingLevelsGetCurrent (levels);
282 if (! nl || CBL_NL (nl) < levelNumber)
283 break;
284 nestingLevelsPop (levels);
285 }
286
287 return nl;
288}
289
290static bool isNumeric (const char *nptr, unsigned long int *num)
291{
292 char *endptr;
293 unsigned long int v;
294
295 v = strtoul (nptr, &endptr, 10);
296 if (nptr != endptr && *endptr == 0)
297 {
298 if (num)
299 *num = v;
300 return true;
301 }
302 return false;
303}
304
305static void findCOBOLTags (const CobolFormat format)
306{
307 NestingLevels *levels;
308 const char *line;
309
310 cblppInit (format);
311
312 levels = nestingLevelsNew (sizeof (unsigned int));
313
314 while ((line = cblppGetLine ()) != NULL)
315 {
316 char word[64];
317 int keyword;
318 unsigned long int levelNumber;
319
320#define READ_WHILE(word, cond) \
321 do { \
322 unsigned int i; \
323 for (i = 0; i < (ARRAY_SIZE (word) - 1) && *line && (cond); line++) \
324 word[i++] = *line; \
325 word[i] = 0; \
326 } while (0)
327#define READ_LITERAL(word) \
328 do { \
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) \
333 line++; \
334 keyword = lookupCaseKeyword (word, Lang_cobol); \
335 } while (0)
336#define READ_WORD(word, keyword) \
337 do { \
338 READ_WHILE (word, isIdentifierChar (*line)); \
339 keyword = lookupCaseKeyword (word, Lang_cobol); \
340 } while (0)
341#define READ_KEYWORD(keyword) \
342 do { \
343 char READ_KEYWORD__word[64]; \
344 READ_WORD (READ_KEYWORD__word, keyword); \
345 } while (0)
346#define SKIP_SPACES() do { while (isspace (*line)) line++; } while (0)
347
348 SKIP_SPACES ();
349 READ_WORD (word, keyword);
350 SKIP_SPACES ();
351
352 switch (keyword)
353 {
354 case KEYWORD_FD:
355 case KEYWORD_SD:
356 case KEYWORD_RD:
357 READ_WORD (word, keyword);
358 SKIP_SPACES ();
359 if (*word && *line == '.')
360 makeCOBOLTag (word, K_FILE);
361 break;
362
363 case KEYWORD_PROGRAM_ID:
364 if (*line == '.')
365 {
366 line++;
367 SKIP_SPACES ();
368 }
369 READ_LITERAL (word);
370 if (*word)
371 makeCOBOLTag (word, K_PROGRAM);
372 break;
373
374 case KEYWORD_COPY:
375 READ_WORD (word, keyword); // FIXME: also allow LITERAL
376 if (*word)
377 makeCOBOLRefTag (word, K_SOURCEFILE, COBOL_SOURCEFILE_COPIED);
378 break;
379
380 case KEYWORD_CONTINUE:
381 case KEYWORD_END_EXEC:
382 case KEYWORD_EXIT:
383 case KEYWORD_FILLER:
384 /* nothing, just ignore those in following cases */;
385 break;
386
387 default:
388 if (isNumeric (word, &levelNumber))
389 {
390 READ_WORD (word, keyword);
391 SKIP_SPACES ();
392
393 if (*word && keyword != KEYWORD_FILLER)
394 {
395 int kind = KIND_GHOST_INDEX;
396
397 if (*line == '.')
398 kind = K_GROUP;
399 else
400 {
401 int keyword2;
402
403 READ_KEYWORD (keyword2);
404 switch (keyword2)
405 {
406 case KEYWORD_BLANK:
407 case KEYWORD_OCCURS:
408 case KEYWORD_IS:
409 case KEYWORD_JUST:
410 case KEYWORD_PIC:
411 case KEYWORD_REDEFINES:
412 case KEYWORD_RENAMES:
413 case KEYWORD_SIGN:
414 case KEYWORD_SYNC:
415 case KEYWORD_USAGE:
416 case KEYWORD_VALUE:
417 kind = K_DATA;
418 }
419 }
420
421 if (kind != KIND_GHOST_INDEX)
422 {
423 NestingLevel *nl;
424 tagEntryInfo entry;
425 int r;
426 unsigned int nestingLevelNumber;
427
428 /* for nesting purposes, level 77 is identical to 1,
429 * and 66 to 2 */
430 switch (levelNumber)
431 {
432 default: nestingLevelNumber = levelNumber; break;
433 case 77: nestingLevelNumber = 1; break;
434 case 66: nestingLevelNumber = 2; break;
435 }
436
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 */)
443 {
444 nl = nestingLevelsPush (levels, r);
445 CBL_NL (nl) = levelNumber;
446 }
447 }
448 }
449 }
450 else if (*word && *line == '.')
451 makeCOBOLTag (word, K_PARAGRAPH);
452 else
453 {
454 int keyword2;
455
456 READ_KEYWORD (keyword2);
457 SKIP_SPACES ();
458
459 if (keyword2 == KEYWORD_DIVISION && *line == '.')
460 makeCOBOLTag (word, K_DIVISION);
461 else if (keyword2 == KEYWORD_SECTION && *line == '.')
462 makeCOBOLTag (word, K_SECTION);
463 }
464 }
465 }
466
467 nestingLevelsFree (levels);
468 cblppDeinit ();
469}
470
471static void findCOBOLFixedTags (void)
472{
473 findCOBOLTags (FORMAT_FIXED);
474}
475
476static void findCOBOLFreeTags (void)
477{
478 findCOBOLTags (FORMAT_FREE);
479}
480
481static void findCOBOLVariableTags (void)
482{
483 findCOBOLTags (FORMAT_VARIABLE);
484}
485
486static void initializeCobolParser (langType language)
487{
488 Lang_cobol = language;
489}
490
491static parserDefinition* commonCobolParserDefinition (const char *name,
492 simpleParser parser)
493{
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;
502 return def;
503}
504
505extern parserDefinition* CobolParser (void)
506{
507 static const char *const extensions [] = {
508 "cbl", "cob", "CBL", "COB", NULL };
509 parserDefinition* def = commonCobolParserDefinition ("Cobol",
510 findCOBOLFixedTags);
511 def->extensions = extensions;
512 return def;
513}
514
515extern parserDefinition* CobolFreeParser (void)
516{
517 return commonCobolParserDefinition ("CobolFree", findCOBOLFreeTags);
518}
519
520extern parserDefinition* CobolVariableParser (void)
521{
522 return commonCobolParserDefinition ("CobolVariable", findCOBOLVariableTags);
523}
static roleDefinition CobolSourcefileRoles[]
Definition: geany_cobol.c:46
cobolKind
Definition: geany_cobol.c:31
@ K_GROUP
Definition: geany_cobol.c:33
@ K_PARAGRAPH
Definition: geany_cobol.c:37
@ K_DIVISION
Definition: geany_cobol.c:36
@ K_SOURCEFILE
Definition: geany_cobol.c:39
@ K_PROGRAM
Definition: geany_cobol.c:34
@ K_SECTION
Definition: geany_cobol.c:35
@ K_FILE
Definition: geany_cobol.c:32
@ K_DATA
Definition: geany_cobol.c:38
static langType Lang_cobol
Definition: geany_cobol.c:62
static const keywordTable cobolKeywordTable[]
Definition: geany_cobol.c:89
cobolSourcefileRole
Definition: geany_cobol.c:42
@ COBOL_SOURCEFILE_COPIED
Definition: geany_cobol.c:43
static kindDefinition CobolKinds[]
Definition: geany_cobol.c:50
#define DEFINE_KEYWORD(n)
@ KEYWORD_RENAMES
Definition: geany_cobol.c:79
@ KEYWORD_SIGN
Definition: geany_cobol.c:80
@ KEYWORD_RD
Definition: geany_cobol.c:67
@ KEYWORD_PROGRAM_ID
Definition: geany_cobol.c:84
@ KEYWORD_VALUE
Definition: geany_cobol.c:83
@ KEYWORD_SYNC
Definition: geany_cobol.c:81
@ KEYWORD_SD
Definition: geany_cobol.c:66
@ KEYWORD_FD
Definition: geany_cobol.c:65
@ KEYWORD_COPY
Definition: geany_cobol.c:86
@ KEYWORD_END_EXEC
Definition: geany_cobol.c:71
@ KEYWORD_JUST
Definition: geany_cobol.c:76
@ KEYWORD_SECTION
Definition: geany_cobol.c:68
@ KEYWORD_USAGE
Definition: geany_cobol.c:82
@ KEYWORD_FILLER
Definition: geany_cobol.c:72
@ KEYWORD_REDEFINES
Definition: geany_cobol.c:78
@ KEYWORD_EXIT
Definition: geany_cobol.c:85
@ KEYWORD_BLANK
Definition: geany_cobol.c:73
@ KEYWORD_DIVISION
Definition: geany_cobol.c:69
@ KEYWORD_IS
Definition: geany_cobol.c:75
@ KEYWORD_CONTINUE
Definition: geany_cobol.c:70
@ KEYWORD_PIC
Definition: geany_cobol.c:77
@ KEYWORD_OCCURS
Definition: geany_cobol.c:74
#define ATTACH_ROLES(RS)
Definition: kind.h:99
bool referenceOnly
Definition: kind.h:76
int langType
Definition: types.h:13