CONTROL  K'101011'
! STACK=60000, STREAMS=3
BEGIN 
CONSTBYTEINTEGERNAME  INT=K'160060'
RECORDFORMAT  STRDF(INTEGER  A,B,C,BYTEINTEGER  SER,REPLY,  C 
   INTEGER  D,E,F,G,H,I,J,K)
PERMRECORD (STRDF)MAPSPEC  RECORD(INTEGER  X)
RECORDFORMAT  STRPF(RECORD (STRDF)NAME  STRD)
CONSTRECORD (STRPF)NAME  INST1=K'160034', INST2=K'160036', OUTST1=K'160044'
CONSTINTEGERNAME  XOUTST1=K'160044'
CONSTRECORD (STRDF)NAME  NULL=0

CONSTINTEGER  BUFFSIZE = 20000
!  Implementation of Edinburgh Compatible Context Editor in IMP
!  for PDP-11 under DEIMOS by G. Brebner (Sept 1978) based on that 
!  for Ferranti SDS (Serial DISC) by K.Adam & R.Thonnes (April 1977)
!  based on that for DEC PDP-9/15 by H.Dewar (June 1970)
CONSTINTEGER  MIN=1;                  !MAIN INPUT STREAM
CONSTINTEGER  MOUT=1;                 !MAIN OUTPUT STREAM
CONSTINTEGER  SIN=2;                  !SECONDARY INPUT STREAM
!  Secondary input is not a standard feature of ECCE
CONSTINTEGER  STOP=-5000;             !LOOP STOP VALUE
OWNINTEGER  IN=MIN;                   !CURRENT INPUT STREAM
OWNINTEGER  MON;                    !MONITOR INDICATOR
OWNINTEGER  PRINT1, PRINT2;        !PRINT INDICATORS
INTEGER  I,J,K,PP1,SYM
CONSTINTEGER  SEXTRA=122;                 !EXTRA BUFFER FOR SIN
INTEGERNAME  MAINFP;                  ! == FP OR MFP (FOR SIN)

!  The command input phase reads and checks a complete
!command line, forming up an internal representation
!as a sequence of fixed-length command units.
!The basic components of a command unit are:
OWNINTEGER  CODE;                        !COMMAND CODE SYMBOL
OWNINTEGER  TEXT;                     !TEXT POINTER (0 IF NOT RELEVANT)
OWNINTEGER  NUM;                         !REPETITION NUMBER
!Command units are stored sequentially from the start
!of array C (CBASE): text strings are stored backwards
!from the end of the same array (TBASE).
!Parentheses and commas occurring in commands are also
!transformed to command units. The TEXT component of
!open bracket and comma units points to the corresponding
!close bracket: the TEXT component of close bracket
!units points back to the first enclosed unit.
OWNINTEGERARRAY  C(0:179)
OWNINTEGER  CBASE=0
OWNINTEGER  TBASE=179
OWNINTEGER  CI;                       !COMMAND INDEX (AD)
OWNINTEGER  TI;                       !TEXT INDEX (AD)
OWNINTEGER  CMAX;                   !COMMAND MAX (AD)

!  Macro definitions (optional feature)
OWNINTEGERARRAY  STORED(1:192);       !DEFS OF X,Y,Z
OWNINTEGER  POS1, POS2, POS3;   !DEF POINTERS

BYTEINTEGERARRAY  A(0:BUFFSIZE+2)
!  The following variables are pointers to the main array A
OWNINTEGER  TOP=1; !TOP OF BUFF (CONST)
OWNINTEGER  BOT;   BOT=BUFFSIZE-121;  !BOTTOM OF BUFF (CONST)
OWNINTEGER  LBEG;                        !LINE START
OWNINTEGER  PP;                          !PREVIOUS POINTER
OWNINTEGER  FP;                          !FILE POINTER
OWNINTEGER  LEND;                        !LINE END
OWNINTEGER  FEND;                     !END OF FILE IN BUFF
OWNINTEGER  MS;                     !MATCH START
OWNINTEGER  ML;                     !MATCH LIMIT
!  The (part of the) file held in the array A is
!stored as a top half (from TOP inclusive to PP exclusive)
!and a bottom half (from FP inclusive to FEND exclusive).
!The gap corresponds to the position of the file-pointer.
!Text location operations do not therefore simply involve
!moving a pointer: the text passed over is moved across
!the gap.  The additional processing overhead is relatively
!small and is outweighed by the simplification of insertion and
!deletion operations that is achieved by this technique.
!  Illustration of pointer significance:
! [NL] O N E NL T W . . . O NL N E X T NL . . NL L A S T NL [NL]
!      !        !   !     !  !                                !
!      T        L   P     F  L                                F
!      O        B   P     P  E                                E
!      P        E            N                                N
!               G            D                                D
OWNINTEGER  TYPE,CHAIN
OWNINTEGER  PEND
SWITCH  T(0:12)
SWITCH  S('(':'¬')

!  Symbol classification codes are 16X+Y, where Y
!is the basic code and X is used to sub-classify
!when necessary (eg percent command letters).
OWNINTEGERARRAY  SYMTYPE(33:95) = C 
     64, 3, 3, 3, 2, 3, 3,11, 9,64, 3,12, 2, 3, 3,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3,64,
   3, 2,10,18, 5, 8,52,10, 2, 6,10,10,10,56, 2, 2,
  10,50,10,22, 5, 5, 6, 2,32,32,32, 3,10, 3, 3, 3
!     !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /
!  0  1  2  3  4  5  6  7  8  9  :  ;! <  =  >  ?
!  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O
!  P  Q  R  S  T  U  V  W  X  Y  Z  [  ¬  ]  ^  _


ROUTINE  READ SYM
!  Read command symbol
    IF  PEND # 0 THEN  SYM=PEND AND  PEND=0 ELSE  START 
       WHILE  POS3 # 0 CYCLE ;        !MACRO EXPANSION
          SYM = STORED(POS3);  POS3 = POS3+1
          RETURN  UNLESS  SYM = 0
          POS3 = POS2;  POS2 = POS1;  POS1 = 0
       REPEAT 
       READ SYMBOL(SYM)
    FINISH 
END 

ROUTINE  READ ITEM
!  Read command item (symbol or number), skipping leading
!spaces, standardising case and detecting macro letters.
!Set up the type (ie SYMTYPE) of the item in TYPE.
!For a 'numeric' item, assemble sequence of digits
!as necessary and return value in NUM.
!Note that NUM is not altered for a non-numeric item.
    CYCLE 
       TYPE = 1;                      !DEFAULT FOR NL
       READ SYM UNTIL  SYM # ' '
       RETURN  IF  SYM < ' ';         !TREAT ANY CONTROL AS NL
       SYM = SYM-32 IF  SYM >= 96;    !ENSURE UPPER CASE
       TYPE = SYMTYPE(SYM)
       RETURN  UNLESS  TYPE&15 = 0;   !RETURN UNLESS NUMERIC OR X,Y,Z
       EXIT  UNLESS  TYPE = 32;       !X,Y,Z (MACRO LETTERS)
       POS1 = POS2;  POS2 = POS3
       POS3 = (SYM-'X')<<6+1
    REPEAT 
    IF  TYPE = 0 START ;              !DECIMAL DIGIT
       NUM = SYM-'0'
       CYCLE 
          READ SYM
          EXIT  UNLESS  '0' <= SYM <= '9'
          NUM = (NUM<<2+NUM)<<1-'0'+SYM;!IE NUM*10+SYM-'0'
       REPEAT 
       PEND = SYM
    FINISH  ELSE  START 
       TYPE = 0
       NUM = 0;   RETURN  IF  SYM = '*'
       NUM = STOP+1;  RETURN  IF  SYM = '?'
       NUM = STOP;                    ! '!'
    FINISH 
END 

ROUTINE  UNCHAIN
!  Use temporary links to locate last unmatched
!open bracket (if any), fixing up any comma
!units en route.
    CYCLE 
       TEXT = CHAIN;  RETURN  IF  TEXT = -1
       CHAIN = C(TEXT+1);  C(TEXT+1) = CI
       RETURN  IF  C(TEXT) = '('
    REPEAT 
END 

ROUTINE  STACK(INTEGER  V)
    C(CI) = V;  CI = CI+1
END 

ROUTINE  READ LINE
INTEGER  K
!  Read line from file (if bottom part of buffer empty)
    IF  FP # FEND START ;             !LINE AVAILABLE
       LEND = FP
       LEND = LEND+1 WHILE  A(LEND) # NL
    FINISH  ELSE  START 
         ->EOF IF   INST1_STRD==NULL
       SELECT INPUT(IN);  
       FP = BOT-121
       UNTIL  K=NL CYCLE 
          IF  FP# BOT START 
            ->EOF IF  NEXTSYMBOL&K'377'=4
            READ SYMBOL(K)
          ELSE  K=NL
          A(FP)=K;  FP = FP+1
       REPEAT 
       FEND = FP;  LEND = FEND-1
       FP = BOT-121
       ->L1
EOF:   FP = BOT;  LEND = FP;  FEND = LEND
       A(FP)=NL
L1:    SELECT INPUT(0)
       MS = 0;  PRINT1 = 0;  PRINT2 = 0
    FINISH 
END 

ROUTINE  BREAK
    A(PP)=NL;  PP = PP+1;  LBEG = PP
END 

!INITIALISE
!
!  NASTY BIT TO CHANGE 'E FRED' TO 'E FRED/FRED'
!
   IF  OUTST1_STRD==NULL START 
      XOUTST1=K'162602'
      OUTST1_STRD=INST1_STRD
   FINISH 
!
!
    PP = TOP-1;  BREAK;               !FOR BOUNCING OFF
    MAINFP == FP
   IF  INST1_STRD==NULL START 
      FP=BOT;   LEND=FP;   FEND=LEND
      A(FP)=NL
   ELSE 
      READ LINE
   FINISH 
   PRINT STRING('Edit');   NEWLINE

READ COMMAND:
    CI = CBASE;  TI = TBASE;  CHAIN = -1
   IF  IN=MIN THEN  PROMPT(">") ELSE  PROMPT(">>")
    READ ITEM;  ->READ COMMAND IF  TYPE = 1;!IGNORE BLANK LINES
    IF  TYPE = 0 AND  CMAX # 0 START 
       C(CMAX+2) = NUM
       READ ITEM;  ->ER2 IF  TYPE # 1
       ->EXECUTE
    FINISH 
    ->NEXT UNIT UNLESS  SYM = '%'
    READ SYM;  SYM = SYM-32 IF  SYM >= 96
    CODE = SYM;  ->ER5 IF  CODE < 'A'
    READ ITEM
    ->T(SYMTYPE(CODE)>>4);            !SWITCH ON EXT TYPE
!T(1) AT END OF PROGRAM
T(2): !%X, %Y, %Z (MACRO DEFINITION)
    ->ER1 IF  SYM # '='
    I = (CODE-'X')<<6+1
    CYCLE 
       READ SYM
       EXIT  IF  SYM = NL
       STORED(I) = SYM
       ->ER6 IF  I&63 = 0
       I = I+1
    REPEAT 
    STORED(I) = 0
    ->READ COMMAND
T(3): !%M, %F, %Q (MONITOR CONTROL)
    MON = 'M'-CODE
    ->READ COMMAND
NEXT UNIT:
!FIRST ITEM ALREADY READ
    I = TYPE&15;  ->ER2 IF  I < 4
    CODE = SYM;  TEXT = 0;  NUM = 1;  !DEFAULT VALUES
    READ ITEM
    ->T(I);                           !SWITCH ON BASIC TYPE
T(4): !FIND
    NUM = 0 UNLESS  TYPE = 0
T(5): !+DEL,TRAV,UNCOVER
    CODE = NUM<<7+CODE;               !PACK LIMIT NUMBER
    NUM = 1;                          !RESET DEFAULT REPETITION
    READ ITEM IF  TYPE = 0
T(6): !+INSERT,SUBST,VERIFY
    ->ER4 IF  TYPE # 3;               !NOT LEGIT QUOTE SYMBOL ->
    TEXT = TI;  I = SYM
    CYCLE 
       READ SYM
       EXIT  IF  SYM = I
       PEND = SYM AND  EXIT  IF  SYM = NL
       ->ER6 IF  TI <= CI
       C(TI) = SYM;  TI = TI-1
    REPEAT 
    ->ER4 IF  (SYM=NL OR  TI=TEXT) AND  CODE # 'I' AND  CODE # 'S'
    C(TI) = 0;  TI = TI-1;      !TEXT END MARKER
    ->RI
T(8): !MOVE,ERASE
    ->NQ UNLESS  SYM = '-'
    CODE = CODE+10;                   !'E'->'O' 'M'->'W'
RI: READ ITEM
    ->RN
T(9): !CLOSE BRACKET
    UNCHAIN;  ->ER3 IF  TEXT = -1
    C(TEXT+2) = NUM
    TEXT = TEXT+3
T(10): !+GET,KILL,ETC.
NQ: ->ER1 IF  TYPE = 3;               !WOULD FAIL LATER BUT WITH LESS HELPFUL R
RN: READ ITEM IF  TYPE = 0
    ->PUT
T(12): !COMMA
    READ ITEM IF  TYPE = 1;           !IGNORE FOLLOWING NL
T(11): !OPEN BRACKET
    TEXT = CHAIN;  CHAIN = CI
    NUM = 0
PUT:STACK(CODE);  STACK(TEXT);  STACK(NUM)
    ->ER6 IF  CI+2 >= TI
    ->NEXT UNIT UNLESS  TYPE = 1
    UNCHAIN;  ->ER3 IF  TEXT # -1
    CMAX = CI
    STACK(')');  STACK(CBASE);  STACK(1); !EXTRA ')'
    STACK(0)
    ->EXECUTE
!COMMAND INPUT ERROR REPORTS
ER1:SPACE;  PRINT SYMBOL(CODE)
ER2:CODE = SYM
    ->ER5
ER3:PRINT STRING(" ()")
    ->ER7
ER4:PRINT STRING(" TEXT FOR")
T(0):
ER5:SPACE;  PRINT SYMBOL(CODE&127)
    ->ER7
ER6:PRINT STRING(" SIZE")
ER7:PRINT SYMBOL('?')
    NEWLINE;  CMAX = 0 IF  CI # CBASE
    READ SYM WHILE  SYM#NL
    ->READ COMMAND

ROUTINE  LEFT STAR
    CYCLE 
       RETURN  IF  PP = LBEG
       FP = FP-1;  PP = PP-1
       A(FP)=A(PP)
    REPEAT 
END 

ROUTINE  RIGHT STAR
    CYCLE 
       RETURN  IF  FP = LEND
       A(PP)=A(FP)
       PP = PP+1;  FP = FP+1
    REPEAT 
END 


ROUTINE  MAKE SPACE
INTEGER  K,P1,P2
!  Check that sufficient breathing space remains
!in the internal file buffer.  If not, expel part
!of the file.
    RETURN  IF  MAINFP-PP > 240
    P1 = TOP;  P2 = (P1+LBEG)>>1;     !OUTPUT ABOUT HALF
    P2 = LBEG IF  CODE = 'C';         !BUT ALL IF CLOSING
    PRINT STRING('EDIT:SIGNAL 20') AND  NEWLINE AND  STOP  IF  P2 = TOP;
                                      ! LOGICAL ERROR
   IF  OUTST1_STRD_SER#1 START 
      SELECT OUTPUT(MOUT)
       UNTIL  K=NL AND  P1>=P2 CYCLE 
          K = A(P1);  PRINT SYMBOL(K);  P1 = P1+1
       REPEAT 
       SELECT OUTPUT(0)
   ELSE 
      P1=P2
      P1=P1+1 WHILE  A(P1)#NL
   FINISH 
    LBEG = TOP+LBEG-P1;  P2 = PP;  PP = TOP
    WHILE  P1 # P2 CYCLE ;            !COPY UP REMAINDER
       A(PP)=A(P1)
       PP = PP+1;  P1 = P1+1
    REPEAT 
END 

ROUTINE  REFRESH
    FP = FP+1;  MAKE SPACE;  READ LINE
END 

ROUTINE  PRINT LINE
INTEGER  P
    PRINT1 = LEND;  PRINT2 = FP+PP
    P = LBEG
    CYCLE  
       IF  P = PP START 
          PRINT SYMBOL('^') IF  P # LBEG AND  NUM = 0
          P = FP
       FINISH 
       EXIT  IF  P = LEND
       PRINT SYMBOL(A(P))
       P = P+1
    REPEAT 
    PRINT STRING("**END**") IF  P = FEND
    NEWLINE
END 

INTEGERFN  MATCHED
INTEGER  I,J,K,L,T1,FP1,LIM
!  Used for Find, Uncover, Delete, Traverse
    LIM = C(CI-3)&(¬127);       !SEARCH LIMIT (<<7)
    T1 = C(TEXT);               !EXTRACT FIRST CHAR FOR SPEED
    CYCLE 
       PP1 = PP;  FP1 = FP
       WHILE  FP # LEND CYCLE 
          K = A(FP)
          IF  K = T1 AND  (FP#MS OR  CODE='D' OR  CODE='T') START 
             I = FP;  J = TEXT
             UNTIL  A(I)#L CYCLE 
                I = I+1;  J = J-1;  L = C(J)
                IF  L = 0 START ;     !COMPLETE STRING MATCHED
                   MS = FP;  ML = I
                   RESULT  = 1
                FINISH 
             REPEAT 
          FINISH 
          A(PP)=K
          PP = PP+1;  FP = FP+1
       REPEAT 
       LIM = LIM-128
       EXIT  IF  LIM = 0 OR  FP = FEND
       IF  CODE # 'U' THEN  BREAK ELSE  PP = PP1
       REFRESH
    REPEAT 
    PP = PP1;  FP = FP1
    RESULT  = 0
YES:MS = FP;  ML = I
    RESULT  = 1
END 


EXECUTE:
    CI = CBASE
GET:CODE = C(CI)&127;  ->MONITOR IF  CODE = 0
    TEXT = C(CI+1)
    NUM = C(CI+2)
    CI = CI+3
REP:NUM = NUM-1
    ->S(CODE)
OK:  INT=0 AND  ->MONITOR IF  INT='A'
      ->REP UNLESS  NUM=0 OR  NUM=STOP
    ->GET
S('¬'): !INVERT
NO: ->GET IF  NUM < 0
    CI = CI+3 AND  ->GET IF  C(CI) = '¬'
SKP:I = C(CI);  CI = C(CI+1) IF  I = '('
    CI = CI+3
    NUM = C(CI-1)-1 AND  ->NO IF  I = ',' OR  I = ')'
    ->SKP IF  I # 0

!EXECUTION ERROR
    PRINT STRING("FAILURE: ")
    IF  CODE='O' OR  CODE='W' START 
       PRINT SYMBOL(CODE-10);  CODE = '-'
    FINISH 
    PRINT SYMBOL(CODE)
    IF  TEXT # 0 START 
       PRINT SYMBOL('''')
       WHILE  C(TEXT) # 0 CYCLE 
          PRINT SYMBOL(C(TEXT))
          TEXT = TEXT-1
       REPEAT 
       PRINT SYMBOL('''')
    FINISH 
    NEWLINE
   READ SYMBOL(SYM) WHILE  SYM#NL
    PRINT1 = 0

MONITOR:
    ->READ COMMAND IF  SYM # NL OR  MON < 0
    ->READ COMMAND IF  PRINT1 = LEND AND  (MON = 0 OR  PRINT2 = FP+PP)
    NUM = 0;  PRINT LINE
    ->READ COMMAND

!INDIVIDUAL COMMANDS
S('('): !OPEN BRACKET
    !RESET REPETITION NUMBER ON CLOSE BRACKET
    C(TEXT+2) = NUM+1
    ->GET
S(')'): !CLOSE BRACKET
    ->GET IF  NUM = 0 OR  NUM = STOP
    C(CI-1) = NUM;              !UPDATED REPETITION NUMBER
S(','): !+COMMA
    CI = TEXT
    ->GET
S('R'): !RIGHT SHIFT
    ->NO IF  FP = LEND
    A(PP)=A(FP)
    PP = PP+1;  FP = FP+1
    ->OK
S('L'): !LEFT SHIFT
    ->NO IF  PP = LBEG OR  IN = SIN
    FP = FP-1;  PP = PP-1
    A(FP)=A(PP)
    MS = 0
    ->OK
S('E'): !ERASE
    ->NO IF  FP = LEND
    FP = FP+1
    ->OK
S('O'): !ERASE BACK
    ->NO IF  PP = LBEG
    PP = PP-1
    ->OK
S('V'): !VERIFY
    I = FP-1;  J = TEXT+1
V1: I = I+1;  J = J-1
    K = C(J)
    ->V1 IF  A(I) = K
    ->NO IF  K # 0
    MS = FP;  ML = I
    ->OK
S('F'): !FIND
    ->NO IF  MATCHED = 0
    ->OK
S('U'): !UNCOVER
    ->NO IF  MATCHED = 0;  PP = PP1
    ->OK
S('D'): !DELETE
    ->NO IF  MATCHED = 0;  FP = ML
    ->OK
S('T'): !TRAVERSE
    ->NO IF  MATCHED = 0
S('S'): !+SUBSTITUTE
    ->NO IF  FP # MS
     FP = ML
S('I'): !+INSERT
    MAKE SPACE
    ->NO IF  PP-LBEG+LEND-FP > 120
    I = TEXT
    CYCLE 
       ->OK IF  C(I) = 0
       A(PP)=C(I)
       PP = PP+1;  I = I-1
    REPEAT 
S('G'): !GET (LINE FROM TT)
   PROMPT(":")
    MAKE SPACE
    READ SYMBOL(I)
    SKIP SYMBOL AND  ->NO IF  I = ':' AND  NEXT SYMBOL=NL
    LEFT STAR
    WHILE  I # NL CYCLE 
       A(PP)=I;  PP = PP+1
       READ SYMBOL(I)
    REPEAT 
S('B'): !+BREAK (INSERT NEWLINE)
    BREAK
    ->OK
S('P'): !PRINT
    PRINT LINE
    ->GET IF  NUM = 0
S('M'): !+MOVE
    RIGHT STAR
    ->NO IF  FP = FEND
    BREAK
M1: REFRESH
    ->OK
S('K'): !KILL (LINE)
    PP = LBEG;  FP = LEND
K1: ->NO IF  FP = FEND
    ->M1
S('J'): !JOIN (DELETE NEWLINE)
    RIGHT STAR
    ->NO IF  PP-LBEG > 120
    ->K1
S('W'): !MOVE BACK
    ->NO IF  IN = SIN
    MAKE SPACE
    ->NO IF  LBEG = TOP
    LEND = FP-PP+LBEG-1
    CYCLE 
       K = A(PP-1)
       EXIT  IF  K = NL AND  PP # LBEG
       FP = FP-1;  PP = PP-1;  A(FP)=K
    REPEAT 
    LBEG = PP;  MS = 0
    ->OK

ROUTINE  SWITCH INPUTS
OWNINTEGER  MFP,MLEND,MEND,SFP,SEND
    IF  IN = MIN START 
      IF  INST2_STRD==NULL START 
         PRINTSTRING('NO ST2');   NEWLINE
         RETURN 
      FINISH 
       LEFT STAR
       IN = SIN
       MFP = FP;  MLEND = LEND;  MEND = FEND
       MAINFP == MFP
       BOT = BOT+SEXTRA;  FP = SFP;  FEND = SEND
       READ LINE
    FINISH  ELSE  START 
       PP = LBEG
       IN = MIN
       BOT = BOT-SEXTRA;  SFP = FP;  SEND = FEND
       FP = MFP;  LEND = MLEND;  FEND = MEND
       MAINFP == FP
    FINISH 
END 

T(1): !%C, %S
    IF  CODE = 'S' START 
       SWITCH INPUTS
       ->MONITOR
    FINISH 
!CLOSE FILE (+EOF ON COMMAND STREAM)
EOF:STOP  IF  OUTST1_STRD_SER=1
    CODE = 'C'
    SWITCH INPUTS IF  IN = SIN
    CYCLE 
       RIGHT STAR
       EXIT  IF  FP = FEND
       BREAK
       REFRESH
    REPEAT 
    SELECT OUTPUT(MOUT)
    WHILE  TOP # PP CYCLE 
       PRINT SYMBOL(A(TOP));  TOP = TOP+1
    REPEAT 
ENDOFPROGRAM