!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!                                                                             !
!       DOFILE                                                                !
!       Parameterized DO file facility                                        !
!                                                                             !
!       This program accepts text files, and a parameter line.                !
!       The parameter line contains up to 10, comma or blank-seperated        !
!       pieces of text, and will substitute those text pieces into the file.  !
!       Conditional "assembly" commands allow the resulting file to be        !
!       automatically tailored, depending on the values of the parameters.    !
!       Conditional assembly statements of two kinds are permitted:           !
!       static and dynamic.  Dynamic conditionals are executed by the SDOS    !
!       command interpreter when the file is actually being "done"; IFERROR   !
!       is a good example of this kind); DOFILE does not process these        !
!       in any special way. The other kind of conditional is a .IF;           !
!       they are evaluated as DOFILE encounters them.  Text "skipped" by a    !
!       .IF is NOT output to the final DO file; all other text is output.     !
!       (Note: lines starting with .xxxx where xxxx is a DOFILE conditional   !
!       are also never output).                                               !
!                                                                             !
!       DOFILE arguments are named ?0 to ?9, in the order encountered on       !
!       the command line.  Should an argument not be supplied, it is defined  !
!       as the empty string.  Two commas in a row will define that argument   !
!       as blank, also.                                                       !
!                                                                             !
!       DOFILE automatically substitutes the text specified for an argument   !
!       wherever ?n is found in the text of the DO macro file. Then the line  !
!       is inspected to see if it is a DOFILE conditional.  If not, the line  !
!       is output to the resulting DO file for later processing.              !
!       When EOF on the DO macro file is found, the final DO file produced    !
!       is automatically done.                                                !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

!       Commands:
!       .IFBLANK text
!       .IFGIVEN text
!       .IF text=text
!       .IF text<>text
!       .ELSE
!       .FIN
!       .ELSE .IFxxx
!       Lines starting with "*" are treated as comments
!
!^L
!   Parameters$(n) holds text for ?n
    Dim Paramters$[9](80)
        DIM     MACRO$[20], MLINE$[132], MACROLINE$[132], FF$/:FF/
        DIM     FE$/:FE/, T$[4], ARGS$[150], BYTES4$/0,0,0,0/

!       macro processor state variable
        DIM     MACROSTATE/1/,EMITSTATE/1/,CONDBLOCK/0/,SAVEBLOCK/0/

        DIM     CONIF$/:09,".IF"/,CONELSE$/:09,".ELSE"/
        DIM     CONELSEIF$/:09,".ELSEIF"/,CONFIN$/:09,".FIN"/

!       argument binding variables
        DIM     ARGSP(10), ARGSL(10), NARGS/0/

!       input line and scratch
        DIM     LINE$[132], TEMP$[132]

!       GET LINE VARIABLES
        DIM     CURCHAN/1/, OUTPUT/1/, FILE$[30]

    Dim Version$/"DOFILE preprocessor, V1.0a"/
                              !!!!!!!!!!!!!!!!!!!
                              ! PROGRAM STARTUP !
                              !!!!!!!!!!!!!!!!!!!

    If Col(0)=1
    Then
        ! Dumb user. Prompt for everything.
        PRINT Version$
        !   Print "CopyRight 1982 Software Dynamics" \ ! No need to say this...
        INPUT   "Output file= " FILE$
        IF      FILE$="CONSOLE:" THEN OUTPUT=0
        ELSE    CREATE  #OUTPUT, FILE$
    Else
        ! Smart user. Grab the parameter line and go!
        Input "" Line$
        PRINT Version$
        !   Print "CopyRight 1982 Software Dynamics" \ ! No need to say this...
        ! Pick off the DO macro file name.
        ! Pick of the optional TO file name.
        ! Now rip apart Line$ to get Parameters$
        ???
    Fi
!^L
!   Process the Macro DO file.
    Repeat
GETLINE:
        Input #DoFile,Line$
        If Eof(DoFile)
        Then
            ! Ok. Now process the resulting DO file.
            Exit \ ! Command interpreter will read DO file just opened.
        Fi
        If Find(Line$,"*")=1
        Then
            If Find(Line$,"**")=1 Then ReadNextLine \ ! Handle DO macro comment
            Print #Output,Line$\Goto ReadNextLine
        Else
            SubstituteParameters
            If Find(Line$,".IF") Then
            If Find(Line$,".IFBLANK") Then
            If Find(Line$,".IFGIVEN") Then
            Print #Output,Line$\Goto ReadNextLine
        Fi
    End
                  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                  ! EAT SPACES AND TABS FROM FRONT OF TEMP$ !
                  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

EATSPACES:      IF FIND(TEMP$,' ')=1 OR FIND(TEMP$,'    ')=1 THEN
                        TEMP$=RIGHT$(TEMP$,2)
                        GOTO EATSPACES
                FI
        RETURN






                        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        ! ACTUAL MACRO EXPANSION CODE !
                        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
MACROEXPANDER:  MLINE$  =LINE$
        ON MACROSTATE GOTO MACROSTART, EXPANDLOOP, ENDMACDEF
        PRINT   "Error encountered in MACROEXPANDER.  Processing continues."
        MACROSTATE=1
        GOTO MACROEXPANDER

!       state 1: look for either starting to define the macro
!                or a macro call.  If neither, just pass line
!                to the output routine.

MACROSTART:
        TEMP$=UPPERCASE$(MLINE$)
        I=FIND(RIGHT$(TEMP$,LEN(LABEL$)+1),CF$)+LEN(LABEL$)
        MLINE$=LEFT$(MLINE$,I-1) CAT ";" CAT RIGHT$(MLINE$,I)
        GOSUB MACROOUTPUT

! this stuff should be used to process the command line

BINDARGS:       NARGS   =0
        ARGS$   =""

BINDLOOP:       IF      FIND(AF$,"',")=1 THEN LASTARG
        I       =FIND(AF$,",")
        IF      NOT(I) THEN LASTARG
        NARGS   =NARGS+1

        ARGSP(NARGS)    =LEN(ARGS$)+1
        ARGSL(NARGS)    =I-1
        LEN(ARGS$)      =LEN(ARGS$)+I-1
        ARGS$(ARGSP(NARGS),ARGSL(NARGS))=AF$(1,I-1)

        AF$     =RIGHT$(AF$,I+1)
        GOTO    BINDLOOP

LASTARG:        IF      AF$="" THEN EXPAND

        NARGS   =NARGS+1
        ARGSP(NARGS)    =LEN(ARGS$)+1
        ARGSL(NARGS)    =LEN(AF$)
        LEN(ARGS$)      =LEN(ARGS$)+LEN(AF$)
        ARGS$(ARGSP(NARGS),ARGSL(NARGS))=AF$

!       now start the expansion of the macro

EXPAND: MACROSTATE=2
                GOSUB   MACROFETCHINIT

EXPANDLOOP:     GOSUB   MACROFETCH

SUBSARGSLOOP:
        IF I+1 >= LEN(MACROLINE$) THEN DONETHISLINE
        J=I+FIND(RIGHT$(MACROLINE$,I+1),"?")
        IF      J=I THEN DONETHISLINE
        IF      J=LEN(MACROLINE$) THEN DONETHISLINE

        I       =J
        IF      MACROLINE$(I+1)>=ASC '0' AND MACROLINE$(I+1)<=ASC '9'
        THEN
                J       =VAL(MACROLINE$(I+1,1))+1
                IF      J>NARGS THEN
                MACROLINE$=MACROLINE$(1,I-1) CAT '0' CAT ...
&                       RIGHT$(MACROLINE$,I+2)
                GOTO    SUBSARGSLOOP
                FI
                MACROLINE$=MACROLINE$(1,I-1) CAT ...
&                       ARGS$(ARGSP(J),ARGSL(J)) CAT ...
&                       RIGHT$(MACROLINE$,I+2)
                GOTO    SUBSARGSLOOP
        FI

        GOTO    SUBSARGSLOOP

DONETHISLINE:   MLINE$  =MACROLINE$
        GOSUB   MACROOUTPUT
        GOTO    EXPANDLOOP






                           !!!!!!!!!!!!!!!!!!!!!!!!!
                           ! EXPANSION SUBROUTINES !
                           !!!!!!!!!!!!!!!!!!!!!!!!!



                      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                      ! MACRO OUTPUT ROUTINE...FOR NOW !
                      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



!       Do a cheap expression evaluation

        DEF CONDITIONSMET(X)

!        CONDBLOCK=ADJUSTCOND(0) !D!
        IF    FIND(MLINE$,"IMMEDIATE") AND NEWFLAGS&IMMEDIATEFLAG...
&          OR FIND(MLINE$,"INDIR") AND NEWFLAGS&INDIRFLAG...
&          OR FIND(MLINE$,"INDEXOFFX") AND NEWFLAGS&INDEXOFFXFLAG...
&          OR FIND(MLINE$,"INDEXOFFS") AND NEWFLAGS&INDEXOFFSFLAG...
&          OR FIND(MLINE$,"POSTINCREMENT") AND NEWFLAGS&POSTINCFLAG...
&          OR FIND(MLINE$,"PREDECREMENT") AND NEWFLAGS&PREDECFLAG...
&          OR FIND(MLINE$,"DOUBLEINCDEC") AND NEWFLAGS&DOUBLEFLAG...
&          OR FIND(MLINE$,"INDEXED") AND NEWFLAGS&INDEXEDFLAG...
&          OR FIND(MLINE$,"DIRECT") AND NEWFLAGS&DIRECTFLAG
        THEN
!              PRINT " RETURN= 1" !D!
              RETURN 1
        ELSE
!              PRINT " RETURN= 0" !D!
              RETURN 0
        FI

        END

MACROOUTPUT: ! Jam useful stuff into the output file.  Flush the rest.

        IF    MACROSTATE=2
        THEN  ON EMITSTATE GOTO EMITALL,EMITCOND,EMITELSE,EMITFIN
        ELSE
              PRINT #OUTPUT,MLINE$
              RETURN
        FI

EMITALL: ! Not in conditional output:
         ! emit everything, up to "IF".
         ! When found, adjust count for matching "FIN".  If the
         ! the conditions were met, emit until "ELSE", "ELSEIF", or "FIN".

        IF    FIND(MLINE$,CONIF$) THEN
              CONDBLOCK=ADJUSTCOND(1)
              IF    CONDITIONSMET(0) THEN EMITSTATE=2
              ELSE
                    EMITSTATE=3
                    SAVEBLOCK=CONDBLOCK
              FI
        ELSE  PRINT #OUTPUT,MLINE$
        RETURN

EMITCOND: ! In conditional output:
          ! emit everything, up to "IF", "ELSE", "ELSEIF", or "FIN".
          ! When found, process an "IF" with EMITALL, a "FIN" with EMITFIN.
          ! An "ELSE" or "ELSEIF" causes further output to be flushed until
          ! a "FIN" is found.

        IF    FIND(MLINE$,CONELSE$) THEN
              EMITSTATE=4
              SAVEBLOCK=CONDBLOCK
              RETURN
        FI
        IF    FIND(MLINE$,CONIF$) THEN
              IF    CONDBLOCK>=254 THEN
                    PRINT "Nesting error!!"
                    PRINT #OUTPUT,"** Nesting error **"
              ELSE
                    CONDBLOCK=ADJUSTCOND(1)
                    IF    NOT(CONDITIONSMET(0)) THEN
                          EMITSTATE=3
                          SAVEBLOCK=CONDBLOCK
                    FI
                    RETURN
              FI
        FI
        IF    FIND(MLINE$,CONFIN$) THEN
              IF    CONDBLOCK THEN CONDBLOCK=ADJUSTCOND(-1)
              ELSE
                    PRINT "Too many FIN's!!"
                    PRINT #OUTPUT,"** Too many FIN's **"
              FI
              RETURN
        FI
        PRINT #OUTPUT,MLINE$
        RETURN

EMITELSE: ! Flush output until an "ELSE", "ELSEIF", or "FIN"
          ! is found.

        IF    FIND(MLINE$,CONIF$) THEN
              IF    CONDBLOCK>=254 THEN
                    PRINT "Nesting error!!"
                    PRINT #OUTPUT,"** Nesting error **"
              ELSE CONDBLOCK=ADJUSTCOND(1)
              RETURN
        FI
        IF    FIND(MLINE$,CONFIN$) THEN
              IF    CONDBLOCK THEN
                    CONDBLOCK=ADJUSTCOND(-1)
                    IF    CONDBLOCK < SAVEBLOCK THEN
                          IF    CONDBLOCK THEN EMITSTATE=2
                          ELSE  EMITSTATE=1
                    ELSE  RETURN
              ELSE
                    PRINT "Too many FIN's!!"
                    PRINT #OUTPUT,"** Too many FIN's **"
                    RETURN
              FI
        FI
        IF    CONDBLOCK <= SAVEBLOCK THEN
              IF    FIND(MLINE$,CONELSEIF$) THEN
                    IF    CONDITIONSMET(0) THEN EMITSTATE=2
                    RETURN
              FI
              IF    FIND(MLINE$,CONELSE$) THEN EMITSTATE=2 FI
        FI
        RETURN

EMITFIN: ! Flush output until a "FIN" is found.  Continue flushing until
         ! "FIN"s matching all flushed "IF"s have been flushed.  If, at
         ! this time, not all "FIN"s have been matched, return to the
         ! EMITCOND state.  If all "FIN"s have been matched, return to the
         ! EMITALL state.  

        IF    FIND(MLINE$,CONIF$) THEN
              IF    CONDBLOCK>=254 THEN
                    PRINT "Nesting error!!"
                    PRINT #OUTPUT,"** Nesting error **"
              ELSE  CONDBLOCK=ADJUSTCOND(1)
              RETURN
        FI
        IF    FIND(MLINE$,CONFIN$) THEN
              IF    CONDBLOCK THEN CONDBLOCK=ADJUSTCOND(-1)
              ELSE
                    PRINT "Too many FIN's!!"
                    PRINT #OUTPUT,"** Too many FIN's **"
              FI
        FI
        IF    CONDBLOCK < SAVEBLOCK THEN
              IF    CONDBLOCK THEN EMITSTATE=2
              ELSE  EMITSTATE=1 FI
        FI
        RETURN

END
