Rexx Language Association
2008 Symposium

An Experimental INCLUDE facility for Rexx

Rationale, Objectives, Development and Results

Les Koehler
3 May 2008


Table of Contents

Abstract

Rationale

Objectives

Development - Setup

Development - PARSE_ARGS

Development - INCLUDE

Development - INCLUDE_LOGIC

Development - SYNTAX

Development - Example of Syntax trap

Results - Sizes

Results - Added

Results - Includes

Results - Performance

Summary and Conclusion


Abstract

I will present an experimental INCLUDE facility intended to increase code reuse and improve maintainability.

Although written for ooRexx, the code can easily be adapted to any Rexx interpreter that implements the ANSI standard.

The presentation will include the code itself, execution measurments before and after as well as size comparisons.


Rationale

The Rexx Compiler for the mainframe provides /*%INCLUDE to allow the programmer to readily reuse existing code.

An examination of the four Excel Automation programs presented last year revealed that they used many blocks of code that were identical for each program.

These repetitive blocks of code tend to overwhelm the unique function of the code being written and make reusability impossible.

Furthermore, if a better technique was discovered while writing a later program, that code had to be manually copied to the other programs, which is burdensome and error prone.


Objectives


Development - Setup

Arg monyy '('opts
/* Opts: Flagname, keyword, min abbrev, default, text */
opt.1=  'PRINT?   PRINT     1 0 Print the worksheet'
opt.2=  'QUIT?     QUIT     1 0 Quit Excel worksheet'
opt.3=  'TIME?     TIME     1 1 Calculate elapsed time'
opt.4=  'KILLQ?    KILLQ    1 0 Kill Excel quietly'
opt.5=  'VISIBLE?  VISIBLE  1 1 Make worksheet visible'
opt.6=  'INCLUDES? INCLUDES 1 0 Show Includes'
opt.7=  'PDF?      PDF      2 1 Convert PDF to PRT'
opt.0=7
address cmd
config_file='c:\WaterMillVillageReportsConfig.txt' /* User specified paths */
props = .Properties~load(config_file)              /* Get the file */
file_path = props~getProperty("prt_file_path")     /* Get our paths */
!incpath = props~getProperty("!incpath")
parse value exception1() with report_exception     /* Get SYNTAX handler */
signal on any name protectall                      /* Trap EVERYTHING */
call parse_args                         /* Sets ERR? MON YY and flags */
call include 'ExcelSetup'
/* Main code goes here */

Development - PARSE_ARGS

PARSE_ARGS:
exposes='REPORT_EXCEPTION !INCTRAK. !INCFILE. !INCIX ,
 !INCLUDES? !INCLINE.',
 '!INCLINES. UNKNOWNS !INCPATH'
me='Cash12ToExcel'         /* <--- Tailor to suit */
inreport='IncomeExpense12' /* <-- */
logevent='12R'
Parse Value '0    0          0      0'               With ,
             err? !inctrak.0 !incix delayed? unknowns
!inclines.=''
Call include_logic 'process_PRT_args_and_options' /* <-- */
If \err? & unknowns='' Then Return
Else Call include_logic 'error_or_unknown', ,
 mon yy rest,'show_PRT_syntax'                    /* <-- */
Exit 12

Development - INCLUDE

INCLUDE:
Parse Value !inctrak.0+1 '!inc' ,
 With !incix !inctrak.!incix ,
 1 !inctrak.0 .
Parse Arg !incfile.!incix, !inc1.!incix, ,
 !inc2.!incix, !inc3.!incix, !inc4.!incix, ,
 !inc5.!incix, !inc6.!incix, !inc7,!incix, ,
 !inc8.!incix

If includes? Then Do
  Say Copies(' ',!incix) 'enter' !incix ,
   !inctrak.!incix  !incfile.!incix
End

!incstream.!incix = .stream~new(!incpath ,
 ||!incfile.!incix'.rex')
!incarray.!incix  = !incstream.!incix~charin(, ,
 !incstream.!incix~chars)~makearray /* Read the file */
!incstream.!incix~close
Do !i.!incix = 1 To !incarray.!incix~items    
/* Process each array element  */
  !incline.!incix = !incarray.!incix[!i.!incix]
  If !incline.!incix='' Then Iterate

  !inctest=left(Strip(!incline.!incix,'L'),2)
  If !inctest='/'||'*' Then Iterate
  If !inctest='-'||'-' Then Iterate
    
  Interpret !incline.!incix
End
If includes? Then Do
  Say Copies(' ',!incix) 'exit ' !incix ,
   !inctrak.!incix !incfile.!incix
End
Parse Value !inctrak.0-1 '!inc' ,
 With !incix !inctrak.!incix 1 !inctrak.0 .
Return

Development - INCLUDE_LOGIC

INCLUDE_LOGIC:
Parse Value !inctrak.0+1 '!incl' With !incix !inctrak.!incix 1 !inctrak.0 .
Parse Arg !incfile.!incix,!inc1,!inc2,!inc3,!inc4,!inc5,!inc6,!inc7,!inc8
Parse Arg !incfile.!incix,!inc1.!incix,!inc2.!incix,!inc3.!incix, ,
 !inc4.!incix,!inc5.!incix,!inc6.!incix,!inc7.!incix,!inc8.!incix
If Symbol('INCLUDES?')='VAR' Then Do
  If includes? Then Do
    Say Copies(' ',!incix) 'enter' !incix !inctrak.!incix !incfile.!incix
  End
End
Else delayed?=1
  /* Fastest method in ooRexx to read in an entire file and convert it to an array */
!incstream.!incix = .stream~new(!incpath||!incfile.!incix'.rex')
!incarray.!incix  = !incstream.!incix~charin(,!incstream.!incix~chars)~makearray
!incstream.!incix~close
!inclines.!incix=''
Do !i.!incix = 1 To !incarray.!incix~items    /* Process each array element (rows in our text file) */
  !incline.!incix = !incarray.!incix[!i.!incix]
  If !incline.!incix='' Then Iterate
  If Strip(Substr(!incline.!incix,1,2))='/'||'*' Then Iterate
  If Strip(Substr(!incline.!incix,1,2))='-'||'-' Then Iterate
    
  !inclines.!incix=!inclines.!incix||!incline.!incix';'
End
If !inclines.!incix\='' Then Interpret !inclines.!incix
Else Say 'Nothing found in' !incstream.!incix 'to interpret by INCLUDE_LOGIC'
If delayed? & includes? Then Do
  Say Copies(' ',!incix) 'enter' !incix !inctrak.!incix !incfile.!incix
  delayed?=0
End
If includes? Then Do
  Say Copies(' ',!incix) 'exit ' !incix !inctrak.!incix !incfile.!incix
End
Parse Value !inctrak.0-1 '!incl' With !incix !inctrak.!incix 1 !inctrak.0 .
Return

Development - SYNTAX

SYNTAX:
PROTECTALL:
lastline=Sourceline()                      /* SHOW_SOURCE needs these */
_sigl=sigl
Interpret report_exception  
Call include_logic 'show_source'
If !incix>1 Then Do
  Say 'Include active:'
  Do i=!incix-1 To 0 By -1
    Select
      When !inctrak.i='!inc' Then Do
        Say 'INCLUDE had' Translate(!incfile.i) 'running at index' i
        Say 'Statement executing:'
        Say !incline.i
      End
      When !inctrak.i='!incl' Then Do
        Say 'INCLUDE_LOGIC had' Translate(!incfile.i) 'running at index' i
        Say 'Statement executing:'
        Say !inclines.i
      End
      Otherwise Nop
    End
  End
End
/* If we have an error, we want to be sure to quit Excel, don't it will be an orphaned process */
If Symbol('XLOBJ')='VAR' Then xlobj~Quit
Exit


Development - Example of SYNTAX

**************RxException***************
CONDITION  =NOVALUE
DESCRIPTION=A1
INSTRUCTION=SIGNAL
PROPAGATED =0
RC         =RC
SIGL       =181
SOURCE     =WindowsNT COMMAND C:\THE\__tmprun.the
SOURCELINE =If !inclines.!incix\='' Then Interpret !inclines.!incix
STATUS     =OFF
**************RxException***************
C:\THE\__tmprun.the:
============================================================
Source lines..................
  170: !incarray.!incix  = !incstream.!incix~charin(,!incstream.!incix~chars)~makearray
  171: !incstream.!incix~close
  172: !inclines.!incix=''
  173: Do !i.!incix = 1 To !incarray.!incix~items    /* Process each array element (rows in our text file) */
  174:   !incline.!incix = !incarray.!incix[!i.!incix]
  175:   If !incline.!incix='' Then Iterate
  176:   If Strip(Substr(!incline.!incix,1,2))='/'||'*' Then Iterate
  177:   If Strip(Substr(!incline.!incix,1,2))='-'||'-' Then Iterate
  178:     
  179:   !inclines.!incix=!inclines.!incix||!incline.!incix';'
  180: End
>>181: If !inclines.!incix\='' Then Interpret !inclines.!incix
  182: Else Say 'Nothing found in' !incstream.!incix 'to interpret by INCLUDE_LOGIC'
  183: If delayed? & includes? Then Do
  184:   Say Copies(' ',!incix) 'enter' !incix !inctrak.!incix !incfile.!incix
  185:   delayed?=0
  186: End
  187: If includes? Then Do
  188:   Say Copies(' ',!incix) 'exit ' !incix !inctrak.!incix !incfile.!incix
  189: End
  190: Parse Value !inctrak.0-1 '!incl' With !incix !inctrak.!incix 1 !inctrak.0 .
  191: Return
  192: 
============================================================
Include active:
INCLUDE_LOGIC had PROCESS_PRT_ARGS_AND_OPTIONS running at index 1
Statement executing:
Parse Value hoadate(monyy) With err? mon yy rest;log?=1;Do k=a1 To opt.0       /* Initialize the option flags */;  Parse Var opt.k flg? wd ln def .;  Interpret flg?'='def;End;Trace or;Do o=1 To Words(opts) /* Examine all the options passed */;  oo=Word(opts,o)                   /* Process one option */;  known?=0;  Do k=1 To opt.0 While \known? /* Compare it against what we know */;    Parse Var opt.k flg? wd ln val .;    known?=Abbrev(wd,oo,ln) /* If its an abbrev ... */;    If known? Then Interpret flg?'=1' /* Set the flag to TRUE */;    else do                      /* Check for prefixed with NO */;      known?=abbrev('NO'wd,oo,ln+2);      If known? Then Interpret flg?'=0' /* If so, set to FALSE */;    end;  End;  If \known? Then unknowns=unknowns oo  /* Accumulate unknown opts */;End;

Results - Sizes

Delin- Balance Income/ Total
quency Cash12 Sheet Expense
Bytes - Old 14570 10511 17120 11902 54103
Bytes - New 8125 4981 11220 6251 30577
Difference 6445 5530 5900 5651 23526
Percentage 44.23 52.61 34.46 47.48 43.48
Lines - Old 417 261 475 298 1451
Lines - New 257 126 328 159 870
Difference 160 135 147 139 581
Percentage 38.37 51.72 30.95 46.64 40.04

Results - Added

     ADDED Bytes Lines
Include 542 15
include_logic 619 16
parse_args 255 7
                       TOTAL: 1416 38



Results - Includes

      INCLUDES Bytes Lines
ExcelSetup 1972 46 1 1 1 1
autofit 734 18 1 1 1 1
justify_and_draw_boxes 1356 37 1 1 1 1
insert_headers 732 17 1 1 1 1
SaveAndSetupPrint 1363 27 1 1 1 1
finish_up 638 20 1 1 1 1
proceess_args_and_options 1039 29 1 1 1 1
error_or_unknown 339 14 1 1 1 1
get_prev_file 788 17 1 0 1 0
show_syntax 647 19 1 1 1 1
                     TOTAL: 9608 244
Net Lines Saved: 206 189 206 189
Added function/cmt lines: 46 54 59 50


Results - Performance

Backup Include Delta Percent
Low 37.672 38.172 0.500 0.01327
High 38.031 38.797 0.766 0.02014
Difference 0.359 0.625 0.266
Median 37.829 38.500 0.671 0.01774
Average 37.858 38.511 0.653 0.01725


Summary and Conclusion

This INCLUDE facility has been used for two additional programs and has met all its Objectives.

Although some code is added, the additions are overshadowed by the lines saved.

A required change to the four existing programs was readily made by adding an option and making just one change to INCLUDEd source.

Although there is a performance impact, it is minor for single use imbeds.

With apologies to Mike Cowlishaw, I find that this INCLUDE facility is productive and easy to use, allowing the programmer to readily modularize his code, similar in nature to the ::ROUTINE directive in ooRexx.