Nested Perform Loops in COBOL? - cobol

Why can I not do this nested perform loop in COBOL?
If I put END-PERFORM. in any line sooner than where I have the last one just before EXIT PROGRAM - it works. But I need the program to display the INPUT C value every time. in the outer perform loop. Its driving me nuts.
PROCEDURE DIVISION USING INPUTC CIPHER.
COMPUTE CIPHERMAX = CIPHER.
MULTIPLY -1 BY CIPHER
---> PERFORM VARYING CIPHER FROM 0 BY 1
UNTIL CIPHERMAX = CIPHER
DISPLAY 'This is loop number: ' CIPHER
INSPECT INPUTC CONVERTING
"avcdefghijklmnopqrstuvwxyz" to "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
COMPUTE CONVERTNUM = FUNCTION MOD (CIPHER, 26)
INSPECT FUNCTION REVERSE(INPUTC) TALLYING LENGTHNUM FOR LEADING SPACES
COMPUTE LENGTHNUM = LENGTH OF CIPHER - LENGTHNUM
---> PERFORM UNTIL SENTRY = LENGTHNUM
IF ((FUNCTION ORD(INPUTC(SENTRY:1)) + CONVERTNUM) > (FUNCTION ORD('Z')))
MOVE FUNCTION CHAR((FUNCTION ORD(INPUTC(SENTRY:1)) + CONVERTNUM) - 26) TO RECHAR
ELSE
MOVE FUNCTION CHAR(FUNCTION ORD(INPUTC(SENTRY:1)) + CONVERTNUM) TO RECHAR
END-IF
IF (((FUNCTION ORD(INPUTC(SENTRY:1))) >= (FUNCTION ORD('A'))) AND
((FUNCTION ORD(INPUTC(SENTRY:1))) <= (FUNCTION ORD('Z'))))
IF ((FUNCTION ORD(INPUTC(SENTRY:1)) + CONVERTNUM) > (FUNCTION ORD('Z')))
INSPECT INPUTC(SENTRY:1) REPLACING ALL INPUTC(SENTRY:1) BY RECHAR
ELSE
INSPECT INPUTC(SENTRY:1) REPLACING ALL INPUTC(SENTRY:1) BY RECHAR
END-IF
ELSE
INSPECT INPUTC(SENTRY:1) REPLACING ALL INPUTC(SENTRY:1) BY INPUTC(SENTRY:1)
END-IF
COMPUTE SENTRY = SENTRY + 1
---> END-PERFORM
DISPLAY INPUTC.
COMPUTE LOOPI = LOOPI + 1
--->END-PERFORM.
EXIT PROGRAM.
END PROGRAM SOLVE.

That nasty scope terminating period after DISPLAY INPUTC. is terminating the scope of the nested PERFORM statements. Get rid of the period and all should work fine.
The only periods you should ever use in the Procedure Division when coding a program to COBOL-85 standard are the ones required to terminate section and paragraph headers and another one to terminate the current paragraph, section or program.

You have a period in the display INPUTC statement. Remove the period and you should be ok. The Period always ends a statement.

Related

Is it possible to consume tick in a Forth definition?

When reading about the tick (') operator I wondered if it can be useful inside a word definition. I know that there is ['] to be used inside a definition, but I thought about using it to read the word name following invocation.
An example:
4 variable cnt
: cycle: ( arg fn -- )
'
4 cnt !
begin
cr
dup execute
-1 cnt +!
cnt # 0 = until
drop
;
I can use cycle: to repeat some word invocation, as follows.
: hello ." hello" ;
cycle: hello
Which prints hello four times, as expected.
But the following code won't define a word that prints hello four times:
: 4hello cycle: hello ;
The tick operator still expects a word from the input stream following invocation of 4hello.
Is it possible to inject it somehow when using cycle: in a word definition, so it won't "leak" outside?
Yes, it's possible. You would have to make cycle: immediate. And then also change it to postpone its actions, rather than perform them at runtime.
Postponing means to delay the actions of words. Immediate words are compiled into the current definition, and normal words are arranged to be compiled when the current definition is executing.
In this case it might look something like this.
: (cycle) 4 0 do dup execute loop drop ;
: cycle: ' postpone literal postpone (cycle) ; immediate
Note that this version no longer works outside definitions.

Loop using GO TO

I need to write a "for loop" in COBOL without using the 'PERFORM ... THRU ...' structure. My idea is to add a paragraph that I can jump back to once certain conditions are met. Here is what I came up with:
PROGRAM-BEGIN.
PAR-A.
IF I <= 10 THEN
SET J TO 1
PAR-B.
IF J <= 10 THEN
DISPLAY ARRAY(I,J)
SET J UP BY 1
GO TO PAR-B
END-IF.
SET I UP BY 1
GO TO PAR-A
END-IF.
PROGRAM-DONE.
Clearly this doesn't work because writing in this way will incur a syntax error. Can anyone help me on this? I can only use IF and GO TO.
This is what you'd need with your existing structure. You've not shown the initialisation of I, but you'll need one. You've attempted to avoid an explicit termination condition/GO TO,
PROGRAM-BEGIN.
SET I TO 1
PAR-A.
IF I <= 10 THEN
SET J TO 1
ELSE
GO TO PROGRAM-DONE
END-IF
.
PAR-B.
IF J <= 10 THEN
DISPLAY ARRAY(I,J)
SET J UP BY 1
GO TO PAR-B
END-IF
SET I UP BY 1
GO TO PAR-A
.
PROGRAM-DONE.
Note the use of the full-stops/periods. You should adopt that for your code, you'll have fewer troubles.
That comma is also trying to disguise itself as a full-stop/period/mark on the screen, and why even include it if you are jamming everything up against each other:
DISPLAY ARRAY ( I J )
There, isn't that nicer?
From the start, work on your names. Use descriptive names. I and J are just plain dumb, and in some wonderful situations you will even confuse them with the number 1.
SET first-level-index
second-level-index TO 1
.
output-results.
IF second-level-index
NOT GREATER THAN 10
DISPLAY
">"
the-data
( first-level-index
second-level-index )
"<"
SET second-level-index UP BY 1
GO TO output-results
END-IF
IF first-level-index
NOT GREATER THAN 10
SET second-level-index TO 1
SET first-level-index UP BY 1
GO TO output-results
END-IF
.
Or
set-up-for-loop.
SET first-level-index TO 1
.
outer-loop.
SET second-level-index TO 1
.
inner-loop.
IF second-level-index
NOT GREATER THAN 10
DISPLAY
">"
the-data
( first-level-index
second-level-index )
"<"
SET second-level-index UP BY 1
GO TO inner-loop
END-IF
IF first-level-index
NOT GREATER THAN 10
SET first-level-index UP BY 1
GO TO outer-loop
END-IF
.
You'd give those paragrpah-names descibing the actual task.
Be aware that comparing indexes (your I and J) to literals requires some twists and turns for the compiler.
To fix the syntax error caused by putting a paragraph name inside
the if, you can use the GOTO to move the PARB paragraph out of the first IF statement:
PROGRAM-BEGIN.
PAR-A.
IF I <= 10 THEN
SET J TO 1
GOTO PAR-B
END-IF
PAR-B.
IF J <= 10 THEN
DISPLAY ARRAY(I,J)
SET J UP BY 1
GO TO PAR-B
END-IF.
SET I UP BY 1
GO TO PAR-A
PROGRAM-DONE.

PostScript execution of nested procedures

(I'm back with yet another question :-) )
Given the following PostScript code:
/riverside { 5 pop } def
/star { 6 pop 2 {riverside} repeat } def
star
I'm wondering how nested procedures should be handled. (I'm creating my own interpreter).
When I execute the star procedure, halfway it finds a nameObjec(riverside) and replaces it with an executable array containing the values from the riverside procedure and executes them.
If I execute the repeat operator the interpreter crashes because there is only one item left on the stack.
Should I actually execute an executable array (=procedure) directly when I'm already in an executable array (=prodecure), or should the executable arrays (=procedures) always be pushed on the (operand?/execution?)stack? or only be executed by another operator?
How many times should this riverside be executed? (2 or 3 times?) I guess 2?
For your information: this is the situation that I have when I execute star on the 3rd line (see the ERROR):
% begin execute 3rd line (star)
% OP = operand stack
% EX = execution stack
% handle 6
OP: 6
EX: star
% handle pop (removes 6 from OP)
OP: -
EX: star
% handle 2
OP: 2
EX: star
% set the riverside executable array on the EX, execute the values
OP: 2
EX: star riverside
% repeat operator:
CRASH, only one item on the OP left, but repeat operator requires 2 operands.
OP: 5
EX:
% end
Please shine a light on this matter, because it is somewhat complex/confusing :-)
Update:
another code sample might be this one:
/starside
{ 72 0 lineto
currentpoint translate
-144 rotate } def
/star
{ moveto
currentpoint translate
4 {starside} repeat
closepath
gsave
.5 setgray fill
grestore
stroke } def
200 200 star
showpage
when the interpreter tokenizes /star { moveto ... if it encounters the nested {starside} how will that be treated? (+ what if there was {starside 5 2 mul pop} instead of only {starside} ?)
I believe you need to look at section 3.5.3 of the PLRM. Although this deals with a simple executable array the concept is the same. When the token scanner encounters a '{' it starts to build an executable array. Until it reaches a matching '}' token the scanner simply stores what it encounters on the operand stack. When it encounters the matching '{' then the objects are converted into an executable array (and stored on the operand stack)
In the case of the scanner encountering an executable name, it stores the name on the operand stack. It does not execute the name, nor does it even perform lookup on it to retrieve the associated object.
So immediately before the execution of '}' in your example, the operand stack would contain twp objects, the '{' opening array, and the executable name riverside. When you encounter the '}' then the scanner creates the actual executable array and stores it on the operand stack. (Note, implementation details vary here)
So immediately before the execution of 'repeat' you would have two objects on the stack, the counter and an executable array containing a single executable name.
You don't look up the name until the executable array containing the name is executed.
This might make it clearer:
%!
/test {(This is my initial string\n) print} def
2 {test} repeat
2 {test} /test {(This is my second string\n) print} def repeat
Notice that I've redefined 'test' after creating the executable array containing the executable name 'test', yet the execution uses the later definition of test. As you can see, its vitally important not to do name lookup too early!

Custom interpreter

I'm trying to write an interpreter in Gforth, but it doesn't work. All I get is an infinite list of num num num num ...
: ?refill
source nip >in # =
if
refill drop
then
;
: inter
begin
?refill
bl word find dup
if
state # =
if
." comp "
,
else
." exec "
execute
then
else
dup rot count >number
if
abort
then
drop drop state #
if
." lit "
['] lit , ,
else
." num "
then
then
again
;
inter
: test 10 20 ;
Your interpreter does work, it just does not block, see the first couple of words from the output:
num exec lit lit exec num num num ...
However, you leave a 0 on the stack somewhere, thats why you create a stack overflow, you can use ~~ in the code to check the stack and track the unconsumed 0.
Bernd Paysan has introduced Recognizers to GForth, I suggest you take a look at them, as they would ease your task of writing an interpreter.

Read numbers following a keyword into an array in Fortran 90 from a text file

I have many text files of this format
....
<snip>
'FOP' 0.19 1 24 1 25 7 8 /
'FOP' 0.18 1 24 1 25 9 11 /
/
TURX
560231
300244
70029
200250
645257
800191
900333
600334
770291
300335
220287
110262 /
SUBTRACT
'TURX' 'TURY'/
</snip>
......
where the portions I snipped off contain other various data in various formats. The file format is inconsistent (machine generated), the only thing one is assured of is the keyword TURX which may appear more than once. If it appears alone on one line, then the next few lines will contain numbers that I need to fetch into an array. The last number will have a space then a forward slash (/). I can then use this array in other operations afterwards.
How do I "search" or parse a file of unknown format in fortran, and how do I get a loop to fetch the rest of the data, please? I am really new to this and I HAVE to use fortran. Thanks.
Fortran 95 / 2003 have a lot of string and file handling features that make this easier.
For example, this code fragment to process a file of unknown length:
use iso_fortran_env
character (len=100) :: line
integer :: ReadCode
ReadLoop: do
read (75, '(A)', iostat=ReadCode ) line
if ( ReadCode /= 0 ) then
if ( ReadCode == iostat_end ) then
exit ReadLoop
else
write ( *, '( / "Error reading file: ", I0 )' ) ReadCode
stop
end if
end if
! code to process the line ....
end do ReadLoop
Then the "process the line" code can contain several sections depending on a logical variable "Have_TURX". If Have_TRUX is false you are "seeking" ... test whether the line contains "TURX". You could use a plain "==" if TURX is always at the start of the string, or for more generality you could use the intrinsic function "index" to test whether the string "line" contains TURX.
Once the program is in the mode Have_TRUX is true, then you use "internal I/O" to read the numeric value from the string. Since the integers have varying lengths and are left-justified, the easiest way is to use "list-directed I/O": combining these:
read (line, *) integer_variable
Then you could use the intrinsic function "index" again to test whether the string also contains a slash, in which case you change Have_TRUX to false and end reading mode.
If you need to put the numbers into an array, it might be necessary to read the file twice, or to backspace the file, because you will have to allocate the array, and you can't do that until you know the size of the array. Or you could pop the numbers into a linked list, then when you hit the slash allocate the array and fill it from the linked list. Or if there is a known maximum number of values you could use a temporary array, then transfer the numbers to an allocatable output array. This is assuming that you want the output argument of the subroutine be an allocatable array of the correct length, and the it returns one group of numbers per call:
integer, dimension (:), allocatable, intent (out) :: numbers
allocate (numbers (1: HowMany) )
P.S. There is a brief summary of the language features at http://en.wikipedia.org/wiki/Fortran_95_language_features and the gfortran manual has a summary of the intrinsic procedures, from which you can see what built in functions are available for string handling.
I'll give you a nudge in the right direction so that you can finish your project.
Some basics:
Do/While as you'll need some sort of loop
structure to loop through the file
and then over the numbers. There's
no for loop in Fortran, so use this
type.
Read
to read the strings.
To start you need something like this:
program readlines
implicit none
character (len=30) :: rdline
integer,dimension(1000) :: array
! This sets up a character array with 30 positions and an integer array with 1000
!
open(18,file='fileread.txt')
do
read(18,*) rdline
if (trim(rdline).eq.'TURX') exit !loop until the trimmed off portion matches TURX
end do
See this thread for way to turn your strings into integers.
Final edit: Looks like MSB has got most of what I just found out. The iostat argument of the read is the key to it. See this site for a sample program.
Here was my final way around it.
PROGRAM fetchnumbers
implicit none
character (len=50) ::line, numdata
logical ::is_numeric
integer ::I,iost,iost2,counter=0,number
integer, parameter :: long = selected_int_kind(10)
integer, dimension(1000)::numbers !Can the number of numbers be up to 1000?
open(20,file='inputfile.txt') !assuming file is in the same location as program
ReadLoop: do
read(20,*,iostat=iost) line !read data line by line
if (iost .LT. 0) exit !end of file reached before TURX was found
if (len_trim(line)==0) cycle ReadLoop !ignore empty lines
if (index(line, 'TURX').EQ.1) then !prepare to begin capturing
GetNumbers: do
read(20, *,iostat=iost2)numdata !read in the numbers one by one
if (.NOT.is_numeric(numdata)) exit !no more numbers to read
if (iost2 .LT. 0) exit !end of file reached while fetching numbers
read (numdata,*) number !read string value into a number
counter = counter + 1
Storeloop: do I =1,counter
if (I<counter) cycle StoreLoop
numbers(counter)=number !storing data into array
end do StoreLoop
end do GetNumbers
end if
end do ReadLoop
write(*,*) "Numbers are:"
do I=1,counter
write(*,'(I14)') numbers(I)
end do
END PROGRAM fetchnumbers
FUNCTION is_numeric(string)
IMPLICIT NONE
CHARACTER(len=*), INTENT(IN) :: string
LOGICAL :: is_numeric
REAL :: x
INTEGER :: e
is_numeric = .FALSE.
READ(string,*,IOSTAT=e) x
IF (e == 0) is_numeric = .TRUE.
END FUNCTION is_numeric

Resources