
%EXTERNALINTEGERARRAY A(1:255)
! INITIALISATION FOR I/O ROUTINES
%EXTERNALBYTEINTEGERARRAY NAMED(1:1024)=10,'R','E','A','D','S','Y','M',
  'B','O','L',10,'N','E','X','T','S','Y','M','B','O','L',10,'S','K',
  'I','P','S','Y','M','B','O','L',11,'P','R','I','N','T','S','Y','M',
  'B','O','L',5,'S','P','A','C','E',6,'S','P','A','C','E','S',7,'N',
  'E','W','L','I','N','E',8,'N','E','W','L','I','N','E','S',7,'N','E',
  'W','P','A','G','E',4,'R','E','A','D',5,'W','R','I','T','E',0(930)
%EXTERNALINTEGERARRAY NAMEDLINK(0:255)=0,76,0(12),89,0(54),84,0(118),
  52,0(11),1,12,23,34,0(4),67,0(23),46,0(5),59,0(17)
%EXTERNALINTEGERARRAY TAGLINK(0:255)=0,13,0(12),16,0(54),14,0(118),8,
  0(11),1,3,4,5,0(4),11,0(23),7,0(5),10,0(17)
%EXTERNALINTEGERARRAY TAG(1:512)=X'40100001',X'01010002',X'41000002',
  X'40000003',X'40100004',X'01010002',X'40000005',X'40100006',
  X'01010002',X'40000007',X'40100008',X'01010002',X'40000009',
  X'4010000A',X'11010002',X'4020000B',X'01010002',X'01010003',0(494)
%EXTERNALINTEGERARRAY LINK(1:512)=2,0,0,0,6,0,0,9,0,0,12,0,0,
  15,0,17,18,0,0(494)
%EXTERNALINTEGER NAMEDP=95
%EXTERNALINTEGER TAGASL=19
%EXTERNALINTEGER EXPROPT=0
%EXTERNALINTEGER CONDOPT=0
%EXTERNALINTEGER TAGSOPT=0
!-----------------------------------------------------------------------
%EXTERNALROUTINESPEC DEFINE(%STRING(63) S)
%EXTERNALROUTINESPEC STATEMENT(%INTEGER STATEMENTP)
%EXTERNALSTRING(255)%FNSPEC STRINT(%INTEGER N,P)
%EXTERNALROUTINESPEC FAULT(%STRING(63) MESS)
%EXTERNALROUTINESPEC DUMP(%STRING(7) OPN,REG,BASE,%INTEGER DISP)
%EXTERNALSTRING(255)%FNSPEC NAME(%INTEGER IDENT)
!-----------------------------------------------------------------------
%EXTERNALROUTINE SKIMP(%STRING(63) S)
%ROUTINESPEC READPS
%ROUTINESPEC READ STATEMENT
%ROUTINESPEC RPSYM(%INTEGERNAME L)
%INTEGERFNSPEC FINDK(%STRINGNAME K)
%INTEGERFNSPEC COMPARE(%INTEGER P)
%RECORDFORMAT KDF(%BYTEINTEGER L,N,A,B)
%RECORDARRAY KD(1:255)(KDF)
%STRING(15)%ARRAY PN(256:319)
%INTEGERARRAY PP(256:319)
%INTEGERARRAY PS(1:512)
%INTEGERARRAY T,TT(1:256)
%INTEGER TP,AP,TTP,TTPP,I,PSFLAG
%STRING(63) SOURCE,OBJECT,OPTIONS,OPTION
%OWNINTEGER LEXOPT=0
%OWNINTEGER ANALOPT=0
  %UNLESS S->SOURCE.(",").OBJECT %THEN FAULT("PARAMETERS ?") %AND %STOP
  %IF OBJECT->OBJECT.(",").OPTIONS %THEN %START
    %UNTIL OPTIONS="" %CYCLE
      %UNLESS OPTIONS->OPTION.(",").OPTIONS %THEN OPTION=OPTIONS %C
        %AND OPTIONS=""
      %IF OPTION->("NO").OPTION %THEN I=0 %ELSE I=1
      %IF OPTION="LEX" %THEN LEXOPT=I
      %IF OPTION="ANAL" %THEN ANALOPT=I
      %IF OPTION="EXPR" %THEN EXPROPT=I
      %IF OPTION="COND" %THEN CONDOPT=I
      %IF OPTION="TAGS" %THEN TAGSOPT=I
    %REPEAT
  %FINISH
  READPS
  DEFINE("STREAM02,".OBJECT)
  SELECT OUTPUT(2)
  SET MARGINS(2,1,72)
  PRINT STRING("

                   SKIMP COMPILER MKII

SOURCE: ".SOURCE."
OBJECT: ".OBJECT."
OPTIONS: ")
  %IF LEXOPT=1 %THEN PRINT STRING("LEX ")
  %IF ANALOPT=1 %THEN PRINT STRING("ANAL ")
  %IF EXPROPT=1 %THEN PRINT STRING("EXPR ")
  %IF CONDOPT=1 %THEN PRINT STRING("COND ")
  %IF TAGSOPT=1 %THEN PRINT STRING("TAGS ")
  NEWLINE
  %IF PSFLAG#0 %THEN FAULT("PHRASE STRUCTURE FAULTY") %AND %STOP
  DEFINE("STREAM01,".SOURCE)
  SELECT INPUT(1)
  ! SET UP TAGS AVAILABLE SPACE LIST
  %CYCLE I=TAGASL,1,511
    LINK(I)=I+1
  %REPEAT
  %CYCLE    ;! FOR EACH STATEMENT
    READ STATEMENT
    TTP=TP-1
    TP=1
    AP=1
    %IF COMPARE(258)=0 %OR TP#TTP %THEN FAULT("SYNTAX ?") %ELSE %START
      %IF ANALOPT=1 %THEN %START
        NEWLINE
        %CYCLE I=1,1,AP-1    ;! PRINT ANALYSIS RECORD
          %IF A(I)<0 %THEN PRINT STRING("  (".STRINT(I,1)."/". %C
            PN(A(I)<<1>>17).")") %AND A(I)=A(I)&X'FFFF'
          PRINT STRING("  ".STRINT(A(I),1))
        %REPEAT
        NEWLINES(2)
      %FINISH %ELSE %START
        %CYCLE I=1,1,AP-1    ;! REMOVE PHRASE NUMBERS
          %IF A(I)<0 %THEN A(I)=A(I)&X'FFFF'
        %REPEAT
      %FINISH
      STATEMENT(1)    ;! GENERATE CODE FOR STATEMENT
    %FINISH
  %REPEAT
!-----------------------------------------------------------------------
%ROUTINE READPS
! READ PHRASE STRUCTURE FROM FILE 'SKIMPPS' AND REDUCE IT
%STRING(31)%ARRAY KA(1:128)
%INTEGERARRAY KNA(1:128)
%STRING(31) K
%INTEGER KAP,KDASL,KN,I,L,PSP,PNP,ALT
%INTEGERNAME NP
%ROUTINESPEC INSERT(%STRING(15) K)
%ROUTINESPEC EXTRACT(%INTEGER I,%STRING(15) K)
%ROUTINESPEC ASSIGN(%INTEGER I)
%INTEGERFNSPEC NEWKD
%ROUTINESPEC RETURNKD(%INTEGER I)
%ROUTINESPEC RETURNLIST(%INTEGER I)
%INTEGERFNSPEC PHRASE
%ROUTINESPEC LITERAL
%ROUTINESPEC KEYWORD
  DEFINE("STREAM03,SKIMPPS")
  SELECT INPUT(3)
  DEFINE("STREAM04,SKIMPPSL")
  SELECT OUTPUT(4)
  PRINT STRING("

PHRASE STRUCTURE

")
  ! SCAN FILE TO BUILD KEYWORD DICTIONARY
  KAP=1
  %CYCLE
    RPSYM(L)
    %IF L='$' %THEN %EXIT
    %IF L='"' %THEN %START
      K=""
      %CYCLE
        RPSYM(L)
        %IF L='"' %THEN %EXIT
        %IF 'A'<=L<='Z' %THEN K=K.TOSTRING(L)
      %REPEAT
      KA(KAP)=K
      KAP=KAP+1
    %FINISH
  %REPEAT
  %CYCLE I=1,1,26
    KD(I)=0
  %REPEAT
  %CYCLE I=27,1,254
    KD(I)_B=I+1
  %REPEAT
  KDASL=27
  I=1
  %UNTIL I=KAP %THEN INSERT(KA(I)) %AND I=I+1
  KN=128
  %CYCLE I=1,1,26
    %IF KD(I)_L#0 %THEN ASSIGN(I)
  %REPEAT
  KAP=1
  %CYCLE I=1,1,26
    %IF KD(I)_L#0 %THEN EXTRACT(I,"")
  %REPEAT
  PRINT STRING("

KEYWORDS

")
  %CYCLE I=1,1,KAP-1
    PRINT STRING(STRINT(KNA(I),3)."  ".KA(I)."
")
  %REPEAT
  ! REREAD FILE AND REDUCE PHRASE STRUCTURE
  SELECT INPUT(0)
  CLOSE STREAM(3)
  SELECT INPUT(3)
  PN(256)="NAME"
  PP(256)=0
  PN(257)="CONST"
  PP(257)=0
  PNP=258
  PSP=1
  %CYCLE    ;! FOR EACH PHRASE DEFINITION
    READ SYMBOL(L)
    %IF L='$' %THEN %EXIT
    %IF L='<' %THEN %START    ;! START OF PHRASE DEFINITION
      PP(PHRASE)=PSP
      %CYCLE    ;! FOR EACH ALTERNATIVE
        ALT=PSP
        NP==PS(PSP+1)
        NP=0    ;! NUMBER OF PHRASES
        PSP=PSP+2
        %CYCLE    ;! FOR EACH ITEM
          READ SYMBOL(L)
          %IF L='<' %THEN PS(PSP)=PHRASE %AND PSP=PSP+1 %AND NP=NP+1
          %IF L='''' %THEN LITERAL
          %IF L='"' %THEN KEYWORD
          %IF L=',' %OR L=';' %THEN %EXIT
        %REPEAT
        PS(ALT)=PSP
        %IF L=';' %THEN %EXIT
      %REPEAT
      PS(PSP)=0
      PSP=PSP+1
    %FINISH
  %REPEAT
  PSFLAG=0
  %CYCLE I=258,1,PNP-1
    %IF PP(I)=0 %THEN FAULT("<".PN(I)."> NOT DEFINED") %AND PSFLAG=1
  %REPEAT
  PRINT STRING("

PHRASES

")
  %CYCLE I=256,1,PNP-1
    PRINT STRING(STRINT(I,3).STRINT(PP(I),6)."   ".PN(I)."
")
  %REPEAT
  PRINT STRING("

REDUCED PHRASE STRUCTURE

")
  %CYCLE I=1,1,PSP-1
    %IF (I-1)&15=0 %THEN PRINT STRING("
".STRINT(I,3)."   ")
   WRITE(PS(I),3)
  %REPEAT
  NEWLINES(2)
  %RETURN
!-----------------------------------------------------------------------
%ROUTINE INSERT(%STRING(15) K)
! SEARCH FOR AND INSERT KEYWORD INTO DICTIONARY
%INTEGER I,J,L
  L=CHARNO(K,1)
  K->(TOSTRING(L)).K
  I=L-'A'+1
  %IF KD(I)_L#0 %THEN %START
SEARCH:%IF K="" %THEN %START
      %IF KD(I)_A#0 %THEN EXTRACT(KD(I)_A,"") %AND %C
        RETURNLIST(KD(I)_A) %AND KD(I)_A=0
      %RETURN
    %FINISH
    %IF KD(I)_A=0 %THEN INSERT(K) %AND %RETURN
    L=CHARNO(K,1)
    K->(TOSTRING(L)).K
    I=KD(I)_A
    %CYCLE
      %IF KD(I)_L=L %THEN ->SEARCH
      %IF KD(I)_B=0 %THEN %EXIT
      I=KD(I)_B
    %REPEAT
    J=I
    I=NEWKD
    KD(J)_B=I
  %FINISH
  ! INSERT REMAINDER OF LETTERS
  %CYCLE
    KD(I)_L=L
    %IF K="" %THEN %RETURN
    L=CHARNO(K,1)
    K->(TOSTRING(L)).K
    J=I
    I=NEWKD
    KD(J)_A=I
  %REPEAT
%END
!-----------------------------------------------------------------------
%ROUTINE EXTRACT(%INTEGER I,%STRING(15) K)
%STRING(15) KK
  %IF I=0 %THEN %RETURN
  KK=K.TOSTRING(KD(I)_L)
  %IF KD(I)_A=0 %THEN KA(KAP)=KK %AND KNA(KAP)=KD(I)_N %AND KAP=KAP+1%C
    %ELSE EXTRACT(KD(I)_A,KK)
  EXTRACT(KD(I)_B,K)
%END
!-----------------------------------------------------------------------
%ROUTINE ASSIGN(%INTEGER I)
  %IF I=0 %THEN %RETURN
  %IF KD(I)_A=0 %THEN KD(I)_N=KN %AND KN=KN+1 %ELSE ASSIGN(KD(I)_A)
  ASSIGN(KD(I)_B)
%END
!-----------------------------------------------------------------------
%INTEGERFN NEWKD
%INTEGER I
  %IF KDASL=0 %THEN PRINT STRING("KD ASL EMPTY") %AND %STOP
  I=KDASL
  KDASL=KD(I)_B
  KD(I)=0
  %RESULT=I
%END
!-----------------------------------------------------------------------
%ROUTINE RETURNKD(%INTEGER I)
  KD(I)_B=KDASL
  KDASL=I
%END
!-----------------------------------------------------------------------
%ROUTINE RETURNLIST(%INTEGER I)
  %IF I=0 %THEN %RETURN
  RETURNLIST(KD(I)_A)
  RETURNLIST(KD(I)_B)
  RETURNKD(I)
%END
!-----------------------------------------------------------------------
%INTEGERFN PHRASE
%STRING(15) P
%INTEGER I,L
  P=""
  %CYCLE
    READ SYMBOL(L)
    %IF L='>' %THEN %EXIT %ELSE P=P.TOSTRING(L)
  %REPEAT
  %CYCLE I=256,1,PNP-1
    %IF PN(I)=P %THEN %RESULT=I
  %REPEAT
  PN(PNP)=P
  PP(PNP)=0
  PNP=PNP+1
  %RESULT=PNP-1
%END
!-----------------------------------------------------------------------
%ROUTINE LITERAL
%INTEGER L
  %CYCLE
    READ SYMBOL(L)
    %IF L='''' %THEN %RETURN %ELSE PS(PSP)=L %AND PSP=PSP+1
  %REPEAT
%END
!-----------------------------------------------------------------------
%ROUTINE KEYWORD
%STRING(31) K
%INTEGER L
  K=""
  %CYCLE
    READ SYMBOL(L)
    %IF L='"' %THEN %EXIT
    %IF 'A'<=L<='Z' %THEN K=K.TOSTRING(L)
  %REPEAT
  PS(PSP)=FINDK(K) %AND PSP=PSP+1 %UNTIL K=""
%END
%END
!-----------------------------------------------------------------------
%ROUTINE READ STATEMENT
%ROUTINESPEC STORE(%INTEGER L)
%ROUTINESPEC KEYWORD
%ROUTINESPEC NAME
%ROUTINESPEC CONST
%INTEGER I,L,KSH
  ! LINE RECONSTRUCT PHASE
  NEWLINES(3)
  TTP=1
  KSH=0
  %CYCLE    ;! FOR EACH CHARACTER
    RPSYM(L)
    %IF L='%' %THEN KSH=128 %ELSE %START
      %UNLESS 'A'<=L<='Z' %THEN KSH=0
      %IF L#' ' %THEN %START    ;! DISCARD SPACES
        %IF L='!' %AND TTP=1 %THEN %START
          RPSYM(L) %UNTIL L=';' %OR L=NL    ;! DISCARD COMMENTS
        %FINISH %ELSE %START
          STORE(L)
          %IF L='''' %THEN %START
            %UNTIL L='''' %CYCLE
              RPSYM(L)
              STORE(L)
            %REPEAT
          %FINISH %ELSE %START
            %IF L=';' %OR L=NL %THEN %START
              %IF TTP=2 %THEN TTP=1 %ELSE %START
                %IF L=';' %THEN NEWLINE %AND %EXIT
                %IF TT(TTP-2)='C'+128 %THEN TTP=TTP-2 %ELSE %EXIT
              %FINISH
            %FINISH
          %FINISH
        %FINISH
      %FINISH
    %FINISH
  %REPEAT
  ! LEXICAL PHASE
  TP=1
  TTPP=1
  %UNTIL TTPP=TTP %CYCLE    ;! FOR EACH LEXICAL ITEM
    I=TT(TTPP)
    %IF I>=128 %THEN KEYWORD %ELSE %START
      %IF 'A'<=I<='Z' %THEN NAME %ELSE %START
        %IF '0'<=I<='9' %OR I='''' %THEN CONST %ELSE %C
          T(TP)=I %AND TP=TP+1 %AND TTPP=TTPP+1
      %FINISH
    %FINISH
  %REPEAT
  %IF LEXOPT=1 %THEN %START
    NEWLINE
    %CYCLE TTPP=1,1,TP-2
      PRINT STRING("   ".STRINT(T(TTPP),1))
    %REPEAT
    NEWLINE
  %FINISH
  %RETURN
!-----------------------------------------------------------------------
%ROUTINE STORE(%INTEGER L)
  %IF TTP>256 %THEN FAULT("STATEMENT TOO LONG") %AND %STOP
  TT(TTP)=L+KSH
  TTP=TTP+1
%END
!-----------------------------------------------------------------------
%ROUTINE KEYWORD
%STRING(255) K
%INTEGER I
  K=""
  %WHILE TT(TTPP)>128 %THEN K=K.TOSTRING(TT(TTPP)-128) %AND TTPP=TTPP+1
  %UNTIL K="" %OR I=0 %THEN I=FINDK(K) %AND T(TP)=I %AND TP=TP+1
%END
!-----------------------------------------------------------------------
%ROUTINE NAME
%STRINGNAME SNAME
%INTEGER I,L,HASH
  SNAME==STRING(ADDR(NAMED(NAMEDP)))
  HASH=0
  SNAME=""
  L=TT(TTPP)
  %UNTIL L<'0' %OR '9'<L<'A' %OR L>'Z' %THEN %CYCLE
    %IF NAMEDP+LENGTH(SNAME)>=1022 %THEN FAULT("NAME DICTIONARY FULL")%C
      %AND %STOP
    %IF LENGTH(SNAME)=255 %THEN FAULT("NAME TOO LONG") %AND %STOP
    SNAME=SNAME.TOSTRING(L)
    HASH=HASH<<8!L
    TTPP=TTPP+1
    L=TT(TTPP)
  %REPEAT
  HASH=HASH-HASH//251*251
  I=HASH
  %CYCLE    ;! SCAN DICTIONARY
    %IF NAMEDLINK(I)=0 %THEN NAMEDLINK(I)=NAMEDP %AND %C
       NAMEDP=NAMEDP+LENGTH(SNAME)+1 %AND %EXIT    ;! INSERT NAME
    %IF SNAME=STRING(ADDR(NAMED(NAMEDLINK(I)))) %THEN %EXIT
    I=(I+1)&255
    %IF I=HASH %THEN FAULT("NAME DICTIONARY FULL") %AND %STOP
  %REPEAT
  T(TP)=256    ;! <NAME>
  T(TP+1)=I    ;! IDENT
  TP=TP+2
%END
!-----------------------------------------------------------------------
%ROUTINE CONST
%INTEGER L,VALUE,FLAG,COUNT,MAXBY10,MAXLD
  VALUE=0
  FLAG=0
  %IF TT(TTPP)='''' %THEN %START
    COUNT=0
    %CYCLE
      TTPP=TTPP+1
      %IF TT(TTPP)='''' %THEN %START
        TTPP=TTPP+1
        %IF TT(TTPP)#'''' %THEN %EXIT
      %FINISH
      VALUE=VALUE<<8!TT(TTPP)
      COUNT=COUNT+1
    %REPEAT
    %UNLESS 1<=COUNT<=4 %THEN FLAG=1
  %FINISH %ELSE %START
    MAXBY10=X'7FFFFFFF'//10
    MAXLD=X'7FFFFFFF'-MAXBY10*10
    L=TT(TTPP)
    %UNTIL L<'0' %OR L>'9' %CYCLE
      %IF VALUE>MAXBY10 %OR (VALUE=MAXBY10 %AND L>MAXLD) %THEN FLAG=1 %C
        %ELSE VALUE=VALUE*10+L-'0'
      TTPP=TTPP+1
      L=TT(TTPP)
    %REPEAT
  %FINISH
  T(TP)=257    ;! <CONST>
  %IF FLAG#0 %THEN FAULT("CONSTANT INVALID") %AND VALUE=0
  T(TP+1)=VALUE
  TP=TP+2
%END
%END
!-----------------------------------------------------------------------
%ROUTINE RPSYM(%INTEGERNAME L)
  READ SYMBOL(L)
  PRINT SYMBOL(L)
%END
!-----------------------------------------------------------------------
%INTEGERFN FINDK(%STRINGNAME K)
! LOOK KEYWORD UP IN DICTIONARY
%INTEGER I,L
  L=CHARNO(K,1)
  K->(TOSTRING(L)).K
  I=L-'A'+1
  %IF KD(I)_L=0 %THEN %RESULT=0
SEARCH:%IF K="" %OR KD(I)_A=0 %THEN %RESULT=KD(I)_N
  L=CHARNO(K,1)
  K->(TOSTRING(L)).K
  I=KD(I)_A
  %CYCLE
    %IF KD(I)_L=L %THEN ->SEARCH
    %IF KD(I)_B=0 %THEN %RESULT=0
    I=KD(I)_B
  %REPEAT
%END
!-----------------------------------------------------------------------
%INTEGERFN COMPARE(%INTEGER P)
%INTEGER APP,TPP,ALT,ALTEND,PSP,PSI
  A(AP)=P<<16!X'80000001'       ;! PHRASE NUMBER & ALTERNATIVE 1
  %IF P<=257 %THEN %START       ;! <NAME> OR <CONST>
    %IF P=T(TP) %THEN %START    ;! SUCCESS
      A(AP+1)=T(TP+1)
      AP=AP+2
      TP=TP+2
      %RESULT=1
    %FINISH %ELSE %RESULT=0
  %FINISH
  TPP=TP       ;! PRESERVE TEXT POINTER
  APP=AP       ;! PRESERVE ANALYSIS RECORD POINTER
  PSP=PP(P)    ;! START OF PHRASE DEFINITION
  %CYCLE       ;! FOR EACH ALTERNATIVE
    ALT=AP+1
    ALTEND=PS(PSP)
    AP=ALT+PS(PSP+1)    ;! LEAVE GAP FOR FORWARD POINTERS
    %IF AP>255 %THEN FAULT("ANALYSIS RECORD TOO LONG") %AND %STOP
    PSP=PSP+2
    %CYCLE    ;! FOR EACH ITEM
      %IF PSP=ALTEND %THEN %RESULT=1    ;! SUCCESS
      PSI=PS(PSP)
      %IF PSI>=256 %THEN %START         ;! PHRASE
        A(ALT)=AP                       ;! FORWARD POINTER
        %IF COMPARE(PSI)=0 %THEN %EXIT
        ALT=ALT+1
      %FINISH %ELSE %START              ;! LITERAL OR KEYWORD
        %IF PSI#T(TP) %THEN %EXIT
        TP=TP+1
      %FINISH
      PSP=PSP+1
    %REPEAT
    %IF PS(ALTEND)=0 %THEN %RESULT=0    ;! FAILURE
    PSP=ALTEND
    TP=TPP           ;! BACKTRACK TEXT POINTER
    AP=APP           ;! BACKTRACK ANALYSIS RECORD POINTER
    A(AP)=A(AP)+1    ;! NEXT ALTERNATIVE NUMBER
  %REPEAT
%END
%END
%ENDOFFILE
