GNU COBOL
Well, they said it couldn't be done. Actually, it was I who said it couldn't be done. Now it's done, and an obsolete language feature re-implemented using the method applied.
The question states:
If any additional whitespace or statements (which do not interrupt the
flow of the code) is added to the source code, it should be reflected
at run-time (after compiling if applicable).
Any amount of stuff can be inserted prior to the three DISPLAY
s which cause the start of the output, and anything after the DISPLAY
s would "interrupt the flow of the code", so that's OK.
COBOL used to have a TRACE
verb (statement) which simply listed source line-numbers as they were executed (no access to the line number in the program). Although of limited use, I've included an implementation of TRACE.
ID Division.
Program-ID. HIWHERE.
ENVIRONMENT DIVISION.
configuration section.
source-computer. TinkerToy with debugging mode.
Procedure Division.
Declaratives.
Debug-Declaratives Section.
Use For Debugging on a b
.
Debug-Declaratives-Paragraph.
Display Debug-Line "!"
.
End Declaratives
.
Main-Program Section.
DISPLAY "Perform"
Display "Hello World, from line " no advancing Perform b
display "GO TO"
Display "Hello World, from line " no advancing GO TO a
.
a.
dISPLay "Fall through"
Display "Hello World, from line " no advancing. b.
The-Last-bit-OF-the-PROGRAM.
GOBACK
.
The output is
Perform
Hello World, from line 18!
GO TO
Hello World, from line 20!
Fall through
Hello World, from line 23!
As an exhibition of the power and flexibility of writing the language, this example uses mixed-case, entirely lowercase, and entirely uppercase, all at the same time. It does not matter, as when processed, everything is "folded" to UPPERCASE.
The only standard COBOL way to get at a source line-number in the running program, from the running program, is with a DEBUGGING
DECLARATIVE
. Within a SECTION
, strictly within a paragraph within a SECTION
, of a such a declarative you have access to the special-register DEBUG-LINE
. This contains the source line-number of the verb (statement) which caused transfer of control to a particular procedure-name (paragraph or SECTION
).
So, with PERFORM
, or GO TO
, or "fall through" the paragraph in the debugging declaratives SECTION
is executed.
OK, but DISPLAY
does not cause transfer of control.
No problem. Put it on the same line as the transfer of control.
Problem, since if "any additional whitespace or statements (which do not interrupt the flow of the code) is added to the source code, it should be reflected at run-time (after compiling if applicable)".
So, put it on the same line but in front of a transfer of control, split the content of the DISPLAY
into two pieces (remember, "In this context, we want the first line number of the statement which outputs the string to be displayed") and output the first part prior to the transfer of control, and the second part, from the DEBUG-LINE
, once inside the debugging procedure.
The final tricky bit is for the "fall through" ("procedures" can be PERFORM
ed, can be the target of a GO TO
, or can be entered simply by being the next line along). In this instance, put the DISPLAY
on the line which defines the procedure, but in front of the definition.
The names of the "procedures" (a
and b
) have been severely shortened to allow them to fit on the same source-line as the DISPLAY
. Strictly a COBOL procedure-name should start somewhere from column eight to column 11. However, the syntax is, these days, much more relaxed about that. To the extent that I can define a procedure name on the same line as some code. Even embedded in code. Care, and an occasional full-stop, is required.
In the PROCEDURE DIVISION
each full-stop shown is required, and no more are.
To compile:
cobc -x -g hiwhere.cbl
To execute (linux):
COB_SET_DEBUG=Y ./hiwhere
Finally, the return of TRACE (without READY/RESET).
ID Division.
Program-ID. tRacE.
ENVIRONMENT DIVISION.
configuration section.
source-computer. TinkerToy with debugging mode.
Procedure Division.
Declaratives.
Debug-Declaratives Section.
Use For Debugging on a
.
Debug-Declaratives-Paragraph.
Display Debug-Line
.
End Declaratives
.
Main-Program Section.
* Just append "perform a" to a single-line statement.
DISPLAY "1" . perform a
Display "2" . perform a
display "3" . perform a
* Or prepend "perform a." for a multi-line statement, or a
* statement which won't "come back".
perform a. GOBACK
.
a.
CONTINUE
.
Output is:
1
17
2
18
3
19
20
Where 1, 2 and 3 are output from the three DISPLAY statements, and 17, 18, 19 and 20 are the line numbers of the "executable" (non-debugging) lines.