(* ----------------------------------------------------------------------------
 * $Id: ALDPARSE.mi,v 1.4 1992/10/15 16:27:45 kredel Exp $
 * ----------------------------------------------------------------------------
 * This file is part of MAS.
 * ----------------------------------------------------------------------------
 * Copyright (c) 1989 - 1992 Universitaet Passau
 * ----------------------------------------------------------------------------
 * $Log: ALDPARSE.mi,v $
 * Revision 1.4  1992/10/15  16:27:45  kredel
 * Changed rcsid variable
 *
 * Revision 1.3  1992/02/16  17:54:24  pesch
 * CONST-deviniton moved again.
 *
 * Revision 1.2  1992/02/12  17:32:19  pesch
 * Moved CONST definition to the right place
 *
 * Revision 1.1  1992/01/22  15:11:15  kredel
 * Initial revision
 *
 * ----------------------------------------------------------------------------
 *)

IMPLEMENTATION MODULE ALDPARSE;

(* Aldes Parser Implementation Module. *)


(*************************************************************************)
(*                                                                       *)  
(* PROGRAMMIERPRAKTIKUM                                                  *)
(*                                                                       *)
(* Sommersemester 1990, Universitaet Passau.                             *)
(*                                                                       *)
(* von: Klaus Rieger                                                     *)
(*                                                                       *)
(*************************************************************************)


(* Import lists and declarations. *)

FROM MASSTOR IMPORT BETA, SIL, LIST,
                    LENGTH, LISTVAR,
                    SRED, LIST1, ADV, FIRST, RED, COMP, INV;

FROM MASBIOS IMPORT DIBUFF, CREAD, CREADB, BKSP, LISTS, 
                    CWRITE, MASCHR, MASORD, BLINES, SWRITE;

FROM SACLIST IMPORT AWRITE, AREAD, LIST2, ADV2, FIRST2, 
                    CONC, LIST3, FIRST3, LIST4, LIST5, CLOUT;

FROM MASSYM2 IMPORT SYWRIT, SYMBOL, ENTER, SREAD, GET, PUT, GENSYM;

FROM MASSYM IMPORT ELEMP, MEMQ, UREAD, UWRITE, UWRIT1;

FROM MASLISPU IMPORT EXTYP, Compiledp0, Compiledp1, Declare;
                    
FROM MASLISP IMPORT EQS, NEQS, GTS, LTS, GEQ, LEQ, NOTS, UND, ODER,   
                    ADD, SUB, MUL, QUOT, REM, POW,
                    QUOTE, SETQ, STRNG, COND,  
                    PROGN, VARS, IFS, WHL, RPT, DE, DF, DM, DG,
                    PROGA, GTO, LBEL, ARY,
                    LAMBDA, MLAMBDA, FLAMBDA, GLAMBDA, SIG,
                    TDEF, DEFAULT, SETAV, TINFO, WRITE, WT,  
                    LISTX, ENV;

FROM MASSPEC IMPORT EVALUATE;

CONST rcsidi = "$Id: ALDPARSE.mi,v 1.4 1992/10/15 16:27:45 kredel Exp $";
CONST copyrighti = "Copyright (c) 1989 - 1992 Universitaet Passau";

TYPE Token =  ( null, not, and, or, times, div, mod, plus, minus, power, 
                eql, neq, lss, leq, gtr, geq, sil,  
                comma, rparen, colon, lbrack, rbrack, rbrace, 
                becomes, then, else, to, To, of, do,
       (*expr*) lparen, dollar, quote, string, number, 
       (*stms*) ident, lbrace, label, procedure, return, 
                print, goto, go, if, case, semicolon,
                while, repeat, until, for, begald, begin,
       (*dcl*)  var, pragma, const, global, safe, array, intrinsic, 
       (*stp*)  period, end, eof);


VAR tok: Token;              (* last token type read *)
    tokval, zwischen: LIST;  (* last token read, intermediate token *)
    c, ch:   CHAR;           (* last character read *)
    ach:    LIST;            (* Aldes code of last character read*)
    KW:     LIST;            (* key word inicator *)
    noerr,isfunc:  BOOLEAN;  (* error during parsing, function found *)
    prnt, ret, any, prag: LIST; (* special ALDES symbols. *)
    blv: LIST;               (*block local variables. *)



PROCEDURE FIRST5(L: LIST; VAR AL1,AL2,AL3,AL4,AL5: LIST);
(* First 5.  L is a list of length 5 or more.  a1=FIRST(L),
   a2=SECOND(L), a3=THIRD(L) and a4=FOURTH(L) a5=FIFTH(L). *)
VAR  LP: LIST;
BEGIN
(*1*) ADV(L,AL1,LP); ADV(LP,AL2,LP); ADV(LP,AL3,LP);
      ADV(LP,AL4,LP); AL5:=FIRST(LP); RETURN;
(*2*) END FIRST5;


(**************************************************************************)
(*                                                                        *)
(*                        Error handling                                  *)
(*                                                                        *)
(**************************************************************************)     


PROCEDURE SyntaxWarning(n: LIST);
(* Report warning on syntax error with number n *)
VAR m: INTEGER;
BEGIN
  SWRITE("["); AWRITE(n); SWRITE("] ");
  m:=INTEGER(n); SWRITE("Syntax warning: ");
  CASE m OF
       2: SWRITE("intrinsic declaration") |
       3: SWRITE("pragma declaration") |
       6: SWRITE(". in header")|
       7: SWRITE("array as function") |
       8: SWRITE("global declaration in algorithm") |
       9: SWRITE("const declaration");
   ELSE SWRITE("???") END;
  SWRITE(" unsupported.");
  DIBUFF;
END SyntaxWarning;


PROCEDURE SyntaxError(n: LIST);
(* Report on syntax error with number n *)
VAR m: INTEGER;
BEGIN
  SWRITE("["); AWRITE(n); SWRITE("] ");
  m:=INTEGER(n); noerr:=FALSE;
  CASE m OF
         1: SWRITE("identifier") |
         2: SWRITE(")") |
         3: SWRITE("factor") |
         4: SWRITE("operator") (*unused*) |
         5: SWRITE(", or )") (*unused*) |
         6: SWRITE("variable") (*unused*) |
         7: SWRITE(", or ;") (*unused*) |
         8: SWRITE("declaration") |
         9: SWRITE("=") |
        10: SWRITE(", or .") |
        11: SWRITE("[") |
        12: SWRITE("]") |
        13: SWRITE("string") |
        14: SWRITE("number") |
        15: SWRITE("to") |
        16: SWRITE("then") |
        17: SWRITE("of") |
        18: SWRITE("{") |
        19: SWRITE("; or }") |
        20: SWRITE("do") |
        21: SWRITE(":=") (*unused*) |
        22: SWRITE(",...,") |
        23: SWRITE(", or ,...,") |
        24: SWRITE("(") |
        25: SWRITE("||") |
        26: SWRITE("statement") |
        27: SWRITE("}") |
        28: SWRITE("} or ;") (*unused*) |
        29: SWRITE("; or until") |
        30: SWRITE("and or or un-") |
        31: SWRITE("/ un-") |
        32: SWRITE("| un-") |
        33: SWRITE("= or :=") |
        34: SWRITE("expression") |
        35: SWRITE(", or identifier") |
        36: SWRITE("declaration or algorithm") |
        37: SWRITE(".") |
  ELSE SWRITE("don't know what is")
       END;
  SWRITE(" expected ");
  DIBUFF;
END SyntaxError;


PROCEDURE SyntaxTest(s: Token; n: LIST);
(*Test if curent token is expected. *)
VAR m: INTEGER;
BEGIN
(*1*) IF tok < s THEN SyntaxError(n);
         REPEAT GetTok UNTIL tok >= s;
         END;
(*9*) END SyntaxTest;


(**************************************************************************)
(*                                                                        *)
(*                             Scanner                                    *)
(*                                                                        *)
(**************************************************************************)


PROCEDURE KeyWord(s: Token; VAR S: ARRAY OF CHAR);
(* Declare key word *)
VAR   X, P: LIST;
BEGIN
  X:=ENTER(LISTS(S));
  P:=LIST(ORD(s));
  PUT(X,KW,P);
END KeyWord;   


PROCEDURE GetCh;
(* Get next character *)
BEGIN
  ach:=CREAD();
  ch:=MASCHR(ach);
END GetCh;


PROCEDURE GetString(): LIST;
(* Get String *)
VAR s: LIST;
BEGIN
  s:=SIL;
  LOOP GetCh;
       IF ch = '"' THEN GetCh;
          IF ch <> '"' THEN EXIT END;
          END;
       s:=COMP(ach,s);
       END;
  s:=INV(s);
  RETURN(s);
END GetString;


PROCEDURE GetIdent(): LIST;
(*Get Identifier. *)
VAR s, p: LIST;
BEGIN
(*1*) BKSP; s:=SREAD();
(*2*) p:=GET(s,KW);
      IF p <> SIL 
         THEN tok:=VAL(Token,CARDINAL(p)) 
         ELSE p:=GET(s,EXTYP); 
              IF p = SIL THEN 
                 IF NOT MEMQ(s,blv) THEN blv:=COMP(s,blv) END;
                 END; 
              END;
      RETURN(s);
(*3*) END GetIdent;

          
PROCEDURE Comment(): LIST;
(* Skip comment. Recursively nested comments are accepted *)
VAR   lev, L: LIST;
BEGIN L:=SIL;
  lev:=1; 
  REPEAT GetCh;  
         IF ch = ']' THEN GetCh; 
                          lev:=lev-1 END;
         IF ch = '[' THEN GetCh; 
                          lev:=lev+1 END;
         (* L:=COMP(ach,L); future extension *) 
  UNTIL lev = 0;
  (* L:=RED(L); L:=INV(L); future extension *)
  RETURN(L); 
END Comment;


PROCEDURE GetTok;
(* Get next token *)
BEGIN  
  WHILE ch = " " DO GetCh END;
  tokval:=SIL;
  CASE ch OF
         '"'  : tok:= string;
                tokval:= GetString() |
         "("  : GetCh; 
                IF ch = ")" THEN GetCh; tok:= sil
                            ELSE tok:= lparen
                                 END |
         ")"  : tok:= rparen; GetCh |
         "["  : IF tok # ident THEN tokval:=Comment(); GetTok; 
                   ELSE tok:= lbrack; GetCh END | 
         "]"  : tok:= rbrack; GetCh |
         "{"  : tok:= lbrace; GetCh |
         "}"  : tok:= rbrace; GetCh |
         "~"  : tok:= not; GetCh |
         "*"  : tok:= times; GetCh |
         "^"  : tok:= power; GetCh |
         "/"  : GetCh;
                IF ch = "\" THEN tok:= and; GetCh
                            ELSE tok:= div END |
         "\"  : GetCh;
                IF ch = "/" THEN tok:= or; GetCh; 
                            ELSE SyntaxError(31) END |
         "+"  : tok:= plus; GetCh |
         "-"  : tok:= minus; GetCh |
         "="  : tok:= eql; GetCh |
         "#"  : tok:= neq; GetCh |
         "<"  : GetCh;
                IF ch = "=" THEN GetCh; tok:= leq
                            ELSE tok:= lss END |
         ">"  : GetCh;
                IF ch = "=" THEN GetCh; tok:= geq
                            ELSE tok:= gtr END |
         ";"  : tok:= semicolon; GetCh |
         ","  : GetCh;
                IF ch = "." THEN GetCh;
                   IF ch = "." THEN GetCh;
                      IF ch = "." THEN GetCh;
                         IF ch = "," THEN GetCh; tok:= To END;
                         END; END
                   ELSE tok:= comma END |
         "'"  : tok:= quote; GetCh |
         "."  : tok:= period; GetCh |
         "$"  : tok:= dollar; GetCh |
         ":"  : GetCh;
                IF ch = "=" THEN GetCh; tok:= becomes
                            ELSE tok:=colon END |
      "0".."9": tok:= number; BKSP;
                tokval:= AREAD(); GetCh |
      "a".."z": tok:= ident;
                tokval:= GetIdent();
                GetCh |
      "A".."Z": tok:= ident;
                tokval:= GetIdent(); GetCh |
         "|"  : GetCh;
                IF ch = "|" THEN GetCh; tok:= end    
                            ELSE SyntaxError(32) END |
  ELSE tok:= null; GetCh
  END;
END GetTok; 


(**************************************************************************)
(*                                                                        *)
(*                             Parser                                     *)
(*                                                                        *)
(**************************************************************************)


PROCEDURE Aparse(): LIST;
(* Parse a set of ALDES-2 declarations and algorithms. *) 
VAR s: LIST;
BEGIN
  noerr:=TRUE; ch:=" ";
  GetTok;
  s:=Program();
  IF tok # end THEN SyntaxError(25) END;
  IF NOT noerr THEN s:= Generate(null,s) END;
  RETURN(s);
END Aparse; 


PROCEDURE Program(): LIST;
(* Parse "program" and generate code *)
VAR s,t: LIST;
BEGIN
  s:= SIL; t:= SIL;
  LOOP
         IF tok = end THEN EXIT 
      ELSIF (var <= tok) AND (tok <= intrinsic) THEN s:= Declaration(); 
                             t:= COMP(s,t); 
      ELSIF tok = ident THEN s:= Algorithm(); t:= COMP(s,t); 
       ELSE SyntaxTest(end,36) END;
      END;
  t:=INV(t);
  t:= Generate(begin,t);
  RETURN(t);
END Program;


PROCEDURE Algorithm(): LIST;
(* Parse Algorithm and generate code *)
VAR d, d1, s, s1, s2, t, t1, u, a, B, B1, B2, b: LIST;
    func:BOOLEAN;
BEGIN
(*1*) (*initialization. *)
      d:= SIL; t:= SIL; u:= SIL; s1:= SIL;
      isfunc:= FALSE;
      zwischen:= SIL;
      B:=blv;
(*2*) (* Header produces the two first arguments, which are needed
      by  Generate(procedure,t) *)
      t:= Header(func);
      IF func THEN FIRST2(t,zwischen,t1); 
                   isfunc:= TRUE; 
              ELSE t1:=t END;
(*3*) (*declarations. *)   
      WHILE (var <= tok) AND (tok <= intrinsic) DO 
            IF tok <= global THEN SyntaxWarning(8) END;
            d1:= Declaration(); 
            d:=COMP(d1,d);
            END; (* WHILE *)
      (* d= (VARn (...) any) ... (VAR1 (...) any) *)
      B1:=blv;
(*4*) (*labled statement sequences. *)
      a:=SIL;
      REPEAT s:=SIL; u:=SIL; (*get label*)
             IF tok = lparen THEN GetTok
                             ELSE SyntaxError(24) END;
             IF tok = number THEN u:= tokval; 
                                  GetTok
                             ELSE SyntaxError(14) END;
             IF tok = rparen THEN GetTok;
                                  u:= Generate(label,u); 
                             ELSE SyntaxError(2) END;
             IF u # SIL THEN a:= COMP(u,a) END; 
             LOOP (*get statements. *)
                  s1:= Statement();
                  s:= COMP(s1,s);  
                  IF tok = semicolon THEN GetTok;
                  ELSIF tok = period THEN GetTok; EXIT;
                  ELSIF tok = end THEN EXIT END;
                  END; 
             s:= INV(s);
             s:=Generate(begin,s); (* (PROGN ....)) *)
             IF s <> SIL THEN a:= COMP(s,a) END;
             UNTIL tok = end;
      a:= INV(a); (* a= ( (label u) (PROGN ....) ...) *)
      a:=Generate(begald,a); (* (PROGA ....)) *)
      d:=COMP(a,d);
(*5*) (*generate return. *)
      IF func THEN s1:= Generate(return,LIST1(zwischen));
                   d:= COMP(s1,d) END;
      d:= INV(d);
(*6*) (*analyse declarations. *) 
      B2:=blv;   
      IF B2 <> B1 THEN 
         WHILE RED(B2) <> B1 DO B2:=RED(B2) END;
         SRED(B2,SIL); b:=LIST2(blv,LISTX); (*trick*)
         b:=Generate(var,b); d:=COMP(b,d);
         SWRITE("Variable(s) declared: "); UWRITE(blv);
         END;
      blv:=B; 
      IF func THEN (*remove local variable from blv*)
         IF FIRST(blv) = zwischen THEN blv:=RED(blv) END;
         END;
(*7*) (*finalize. *)
      GetTok; (* || *) (*avoid reading next algorithm header until now*)
      d:= Generate(begin,d); 
      t:= COMP(d,t1); t:= INV(t);                
      b:=FIRST(t); (*add proc name to blv, since global*)
      IF NOT MEMQ(b,blv) THEN blv:=COMP(b,blv) END;
      t:= Generate(procedure,t);
      RETURN(t);
(*9*) END Algorithm; 


PROCEDURE Declaration(): LIST;
(* Parse declarations and generate code *)
VAR t: LIST;
BEGIN
  t:= SIL; 
  IF (tok = global) OR (tok = safe) THEN t:= GlobDec();
  ELSIF tok = intrinsic THEN t:= IntrinsicDec(); 
  ELSIF tok = const     THEN SyntaxWarning(9); 
                             t:= PrCoDec();
  ELSIF tok = pragma    THEN SyntaxWarning(3); 
                             t:= PrCoDec();
  ELSIF tok = array     THEN t:= ArrayDec();
                        ELSE SyntaxError(8) END; 
  IF tok = period THEN GetTok; 
                  ELSE SyntaxError(37) END; 
  RETURN(t);
END Declaration;


PROCEDURE Header(VAR func: BOOLEAN): LIST;
(* parse "algorithm - header" and generate code *)
VAR s,u,up,v,a: LIST;
    zwischen: LIST;
BEGIN
  s:= SIL; u:= SIL; up:= SIL; v:= SIL; a:= SIL;
  func:= FALSE;
  IF tok = ident 
     THEN a:= tokval; GetTok;
     IF tok = period THEN SyntaxWarning(6); 
                          GetTok; 
                          func:= FALSE;
                          s:= COMP(a,s)
  ELSIF tok = lparen THEN s:= COMP(a,s); 
                          GetTok;
                          u:= IdentifierList(); 
                          IF tok = semicolon THEN GetTok;  
                             up:= IdentifierList(); END;
                          IF tok = rparen THEN GetTok
                                          ELSE SyntaxError(2) END;
                          u:=LIST2(u,up);
                          s:= COMP(u,s);
                          func:= FALSE;
  ELSIF tok = sil THEN s:= COMP(a,s);
                       u:= LIST2(SIL,SIL); 
                       s:= COMP(u,s);
                       func:= FALSE;
                       GetTok      
  ELSIF tok = becomes THEN zwischen:=a;  
                           GetTok;
                           IF tok = ident THEN a:= tokval;
                                               s:= COMP(a,s);
                                               GetTok
                               ELSE SyntaxError(1) END;
                           IF tok = lparen THEN GetTok;
                                                u:= IdentifierList();
                                                u:=LIST2(u,up);
                                                s:= COMP(u,s)
                        ELSIF tok = sil THEN u:=LIST2(SIL,SIL);
                                             s:=COMP(u,s)
                              ELSE  SyntaxError(24) END;
                           IF (tok = rparen) OR (tok = sil)
                              THEN GetTok;
                                   func:= TRUE;
                              ELSE SyntaxError(2) END; 
                           s:= LIST2(zwischen,s); 
                           END      
   ELSE SyntaxError(1); END; (* IF *)
  RETURN(s);
END Header;


PROCEDURE Sequence(): LIST;
(* parse statement sequences and generate code *)
VAR s,t: LIST;
BEGIN
  s:= SIL; t:= SIL;
  IF tok # lbrace THEN t:= Statement() (* only one statement !! *)
     ELSE GetTok;
          LOOP s:= Statement(); 
               t:= COMP(s,t);
                  IF tok = semicolon THEN GetTok
               ELSIF tok = rbrace THEN GetTok; EXIT 
                ELSE SyntaxError(19); END;
               END;
          IF t # SIL THEN t:= INV(t); t:= Generate(begin,t) END;
     END;
  RETURN(t);
END Sequence;


PROCEDURE Statement(): LIST;
(* Parse Statement and generate code *)
VAR t: LIST;
BEGIN
  t:=SIL;
  SyntaxTest(ident,26);
  CASE tok OF
       print: t:=PrintStat(); |
       goto : t:=GotoStat(); |
       go   : t:=GoStat(); |
       if   : t:=IfStat(); |
       case : t:=CaseStat(); |                           
       while: t:=WhileStat(); |
      repeat: t:=RepeatStat(); |
         for: t:=ForStat(); |
      return: GetTok;
              IF isfunc THEN t:= Generate(return,LIST1(zwischen))
                        ELSE t:= Generate(return,SIL) END; |
      ident : t:=IdentStat(); 
         ELSE SyntaxTest(period,26) END; (* Case *)
  RETURN(t)      
END Statement;


PROCEDURE PrintStat(): LIST;
(* parse "print - staement" and generate code *)
VAR t: LIST;
BEGIN
  t:= SIL;
  GetTok;
  IF tok = string THEN t:= tokval;
                       GetTok;
                       t:=Generate(print,t) 
                  ELSE SyntaxError(13) END;
  RETURN(t)
END PrintStat;


PROCEDURE GotoStat(): LIST;
(* parse "goto - statement" and generate code *) 
VAR s,t: LIST;
BEGIN
  t:= SIL; GetTok;
  IF tok = lparen THEN GetTok;
     IF tok = number THEN s:= tokval; GetTok;
        IF tok = rparen THEN t:= Generate(goto,s); GetTok
                        ELSE SyntaxError(2) END
        ELSE SyntaxError(14) END
  ELSIF tok = number THEN s:= tokval;
                          t:= Generate(goto,s);
                          GetTok
        ELSE SyntaxError(14) END;
  RETURN(t);
END GotoStat;


PROCEDURE GoStat(): LIST;
(* parse "go to - statement" and generate code *)
VAR s,t: LIST;
BEGIN
  t:= SIL; GetTok;
  IF tok # to THEN SyntaxError(15)
     ELSE GetTok;
     IF tok = lparen THEN GetTok;
        IF tok = number THEN s:= tokval; GetTok;
           IF tok = rparen THEN t:= Generate(goto,s); GetTok
                           ELSE SyntaxError(2) END
           ELSE SyntaxError(14) END
     ELSIF tok = number THEN s:= tokval;
                             t:= Generate(goto,s);
                             GetTok
                        ELSE SyntaxError(14); END;
     END;
  RETURN(t);
END GoStat;


PROCEDURE IfStat(): LIST;
(* parse "if - statement" and generate code *)
VAR s,t: LIST;
BEGIN
  t:= SIL;
  GetTok;
  s:= CondSeq();
  t:= COMP(s,t);                
  IF tok = then THEN GetTok
                ELSE SyntaxError(16) END;
  s:= Sequence(); 
  t:= COMP(s,t);                 
  IF tok = else THEN GetTok;
                     s:= Sequence();
                     t:= COMP(s,t)   
  END;
  t:= INV(t);
  t:= Generate(if,t);
  RETURN(t)    
END IfStat;


PROCEDURE CaseStat(): LIST;
(* parse "case - statement and generate code *) 
VAR a,c,s,t,v: LIST;
BEGIN
  v:=SIL; t:=SIL; 
  GetTok;
  a:= Expression();
  IF tok = of THEN GetTok
              ELSE SyntaxError(17) END;
  IF tok = lbrace THEN GetTok
                   ELSE SyntaxError(18) END;
  REPEAT
     c:= TermList();    (*  (c1 c2 ... cn)  *)
     IF tok = do THEN GetTok
                 ELSE SyntaxError(20) END;
     s:= Sequence();
     IF tok = semicolon THEN GetTok 
     ELSIF tok # rbrace THEN SyntaxError(27) END;
          
     (* list preparation and call of procedure CaseLabel *)

     v:= SIL;
     v:= LIST3(a,c,s);
     v:= CaseLabel(v);
     t:= COMP(v,t)                
  UNTIL tok = rbrace;
  GetTok;       
  t:= INV(t);          
  t:= Generate(case,t);
  RETURN(t)  
END CaseStat;


PROCEDURE WhileStat(): LIST;
(* parse "while - statement" and generate code *)
VAR t,s: LIST;
BEGIN
  t:=SIL;
  GetTok;  
  s:= CondSeq();
  IF tok = do THEN GetTok
              ELSE SyntaxError(20) END;
  t:= Sequence(); 
  t:=LIST1(t);      
  t:= COMP(s,t);  
  t:= Generate(while,t);
  RETURN(t)
END WhileStat;


PROCEDURE RepeatStat(): LIST;
(* parse "repeat - statement" and generate code *)
VAR t,s: LIST;
BEGIN  
  t:=SIL;s:=SIL;
  GetTok;
  IF tok = lbrace THEN t:= Sequence(); (* PROGN (s1..sn) *) 
     IF tok = until THEN GetTok;
                         s:= CondSeq();
                         s:= LIST1(s); 
                         s:= COMP(t,s); (* PROG(s,..sn) Cond *)
                         t:= Generate(repeat,s);
                    ELSE 
         (* repeat - statement without until ----> Condition() = TRUE *)  
                         s:= WT; 
                         s:= LIST1(s);  
                         t:=COMP(t,s);
                         t:= Generate(repeat,t);
                         END (* IF *)
     ELSE (* tok # "{" *)
          LOOP s:= Statement(); 
               t:= COMP(s,t);
               IF tok = semicolon THEN GetTok 
               ELSIF tok = until THEN GetTok; EXIT 
                ELSE SyntaxError(29) END;  
               END;
          IF t # SIL THEN t:= INV(t) END;
          t:= Generate(begin,t);
          s:= CondSeq();
          s:= LIST1(s);   
          s:= COMP(t,s);
          t:= Generate(repeat,s);
          END; (* IF *)
 RETURN(t); 
END RepeatStat;


PROCEDURE ForStat(): LIST;
(* parse "for - statement" and generate code *) 
VAR i,s,t,t1,t2,t3,v: LIST;
BEGIN
  t:= SIL; s:= SIL;
  GetTok;
  IF tok = ident THEN i:= tokval; GetTok
                 ELSE SyntaxError(1) END;
     IF tok = eql THEN GetTok
  ELSIF tok = becomes THEN GetTok
   ELSE SyntaxError(33) END;
  t1:= Expression(); 
  IF tok = To THEN GetTok; 
                   t2:= SIL; 
                   t3:= Expression()
  ELSIF tok = comma THEN GetTok;
                         t2:= Expression();
                         IF tok = To THEN GetTok; 
                                          t3:= Expression()
                                     ELSE SyntaxError(22) END
        ELSE SyntaxError(23) END; (* IF *)
  IF tok = do THEN GetTok; s:=Sequence();
     ELSE SyntaxError(20) END;
  t:= LIST5(i,t1,t2,t3,s);
  t:= Generate(for,t);
  RETURN(t)
END ForStat;


PROCEDURE IdentStat(): LIST;
(* parse "identifier - statement" and generate code *)
VAR s,t,u,v: LIST;
BEGIN
(*1*) t:= SIL; u:=SIL; v:=SIL;
      s:= Variable();
(*2*) IF tok = becomes THEN GetTok; u:= Expression();
                       t:= LIST2(s,u); t:= Generate(becomes,t);
   ELSIF tok = sil THEN u:= SIL; 
         IF NOT SYMBOL(s) THEN SyntaxWarning(7) END;
         t:= COMP(s,u); GetTok;
   ELSIF tok = lparen THEN GetTok; u:= TermList(); 
                           IF tok = semicolon THEN GetTok;
                              v:=TermList();
                              IF tok = rparen THEN GetTok;
                                              ELSE SyntaxError(2) END;
                              ELSE
                              IF tok = rparen THEN GetTok;
                                              ELSE SyntaxError(2) END;
                              END; 
                           IF NOT SYMBOL(s) THEN SyntaxWarning(7) END;
                           t:= CONC(u,v); t:=COMP(s,t); (*call*)
    ELSE t:= LIST1(s); (*call*) END; 
      RETURN(t); 
(*3*) END IdentStat;


PROCEDURE GlobDec(): LIST;
(* parse global or safe declarations and generate code *)
VAR   s, t, u, v: LIST;
BEGIN
(*1*) s:= SIL; t:= SIL; 
      GetTok;
(*2*) LOOP s:=Variable();
           t:=COMP(s,t); 
              IF tok = comma THEN GetTok
           ELSIF tok = period THEN EXIT
            ELSE SyntaxError(8); EXIT END; (* IF *)
           END; 
(*3*) t:=INV(t); t:=LIST2(t,any); t:=Generate(var,t);
      RETURN(t);
(*9*) END GlobDec;


PROCEDURE IntrinsicDec(): LIST;
(* parse intrinsic declarations and generate code *)
VAR s,t: LIST;
BEGIN
(*1*) s:= SIL; t:= SIL; 
      SyntaxWarning(2);
      GetTok;
(*2*) s:=IdentifierList();
      IF tok # period THEN SyntaxError(8) END; 
(*3*) t:=LIST2(s,any);
      t:= Generate(var,t);
      RETURN(t);
(*9*) END IntrinsicDec;  


PROCEDURE PrCoDec(): LIST;
(* parse pragma or const declarations and generate code *)
VAR   s, t, u, x, y: LIST;
BEGIN
(*1*) GetTok; x:= SIL; y:= SIL;
(*2*) REPEAT u:= SIL; 
             s:=Variable(); x:=COMP(s,x);
             IF tok = eql THEN GetTok; u:= Expression()
                          ELSE SyntaxError(9) END;
             u:=LIST2(s,u); u:=Generate(becomes,u);
             y:=COMP(u,y);
             IF tok = comma THEN GetTok
             ELSIF tok # period THEN SyntaxError(10) END
             UNTIL tok = period;
(*3*) x:=INV(x); y:=INV(y);
      x:=LIST2(x,any); x:=Generate(var,x);
      y:=COMP(x,y);
      y:= Generate(begin,y);
      RETURN(y);
(*4*) END PrCoDec;


PROCEDURE ArrayDec(): LIST;
(* parse array declarations and generate code *)
VAR   s, t, u: LIST;
BEGIN
(*1*) s:= SIL; u:= SIL;
      GetTok;
(*2*) REPEAT
             IF tok = ident THEN s:= tokval; 
                                 GetTok; 
                            ELSE SyntaxError(1) END;
             IF tok = lbrack THEN GetTok
                             ELSE SyntaxError(11) END;
             t:=TermList(); 
             t:=LIST2(s,t); t:=Generate(array,t);
             u:=COMP(t,u);
             IF tok = rbrack THEN GetTok;
                             ELSE SyntaxError(12) END;
             IF tok = comma THEN GetTok
                ELSIF tok # period THEN SyntaxError(10) END;
             UNTIL tok = period; 
(*3*) u:= INV(u); u:=LIST2(u,any);
      t:= Generate(var,u);
      RETURN(t);
(*9*) END ArrayDec;


PROCEDURE CondSeq(): LIST;
(* parse codition sequences and generate code *)
VAR c,cl,t: LIST;
   oper: Token;
BEGIN
  t:= SIL;
  cl:= SIL;
  c:= Condition();
  cl:= COMP(c,cl);
  IF tok = and THEN oper:= tok;
                    GetTok;
                    REPEAT
                      c:= Condition();
                      cl:= COMP(c,cl);
                      IF tok = and THEN GetTok END;
                      IF tok = or THEN SyntaxError(30) END;
                    UNTIL (tok = then) OR (tok = until) OR (tok = do) 
                           OR (tok = period) OR (tok = end) OR (tok = rparen)
                           OR (tok = semicolon) OR (tok = rbrace) 
                           OR (tok = or);
                    IF cl # SIL THEN cl:= INV(cl) END;
                    t:= Generate(oper,cl);    
  ELSIF tok = or THEN oper:= tok;
                      GetTok;
                      REPEAT
                        c:= Condition();
                        cl:= COMP(c,cl);
                        IF tok = or THEN GetTok END;
                        IF tok = and THEN SyntaxError(30) END;
                      UNTIL (tok = then) OR (tok = until) OR (tok = do) 
                            OR (tok = period) OR (tok = end) OR (tok = rparen)
                            OR (tok = semicolon) OR (tok = rbrace) 
                            OR (tok = and);
                      IF cl # SIL THEN cl:= INV(cl) END;
                      t:= Generate(oper,cl);
  ELSE t:=c
  END;
  RETURN(t);
END CondSeq;


PROCEDURE Condition():LIST;
(* parse condition and generate code *)
VAR s,t,u: LIST;
    oper: Token;
BEGIN
  t:= SIL;
  IF tok = not THEN GetTok;
                    t:= Condition();
                    t:= Generate(not,LIST1(t))
  ELSIF tok = lparen THEN GetTok;
                          t:= CondSeq();
                          IF tok = rparen THEN GetTok
                                          ELSE SyntaxError(2)
                          END
  ELSE u:= Expression(); 
       IF (eql <= tok) AND (tok <= geq) THEN oper:= tok; GetTok;
                                             s:= Expression();
                                             t:= LIST2(u,s);
                                             t:= Generate(oper,t);
                                         ELSE SyntaxError(11) 
       END; 
  END; 
  RETURN(t);                 
END Condition;             
    

PROCEDURE IdentifierList(): LIST;
(* parse identifierlist; result is a inverted list *)
VAR s,t: LIST;
BEGIN
  s:=SIL;
  LOOP IF tok = ident THEN s:=COMP(tokval,s); GetTok
                      ELSE SyntaxError(1) END;
       IF tok = comma THEN GetTok  
       ELSIF tok = semicolon THEN EXIT;
       ELSIF tok = rparen THEN EXIT
       ELSIF tok = period THEN EXIT
       ELSE SyntaxError(35); EXIT 
            END;
       END;
  s:=INV(s);
  RETURN(s);
END IdentifierList;  


PROCEDURE TermList(): LIST;
(* parse termlist *)
VAR s,t: LIST;
BEGIN
  s:= SIL; t:= SIL;
  REPEAT
    s:= Expression(); 
    t:= COMP(s,t); 
    IF tok = comma THEN GetTok END;
  UNTIL (tok = do) OR (tok = semicolon) OR (tok = rparen) OR (tok = rbrack);
  t:= INV(t);
  RETURN(t);
END TermList;


PROCEDURE Expression(): LIST;
(* parse expressions and generate code *)
VAR s,t,u: LIST;
    oper: Token;
BEGIN
  s:= SIL; t:= SIL; u:= SIL;
  IF (plus <= tok) AND (tok <= minus) THEN oper:= tok;
     GetTok; s:= Term();
     IF oper = minus THEN s:= Generate(oper,LIST1(s)) END
  ELSE s:= Term() END; 
  WHILE (plus <= tok) AND (tok <= minus) DO
    oper:= tok; GetTok;
    u:= Term(); t:= LIST2(s,u);
    s:= Generate(oper,t);
  END; 
  RETURN(s);
END Expression;


PROCEDURE Term(): LIST;
(*Parse term and generate code. *)
VAR s, t, u: LIST;
    oper: Token;
BEGIN 
  s:=Factor();
  WHILE (times <= tok) AND (tok <= mod) DO
    oper:=tok; 
    GetTok; 
    u:=Factor(); 
    t:=LIST2(s,u); 
    s:=Generate(oper,t); 
  END;
  RETURN(s);
END Term;


PROCEDURE Factor(): LIST;
(*Parse factor and generate code. *)
VAR s, t, u: LIST;
    oper: Token;
BEGIN 
  s:=Potency();
  WHILE tok = power DO
    oper:=tok; 
    GetTok; 
    u:=Potency(); 
    t:=LIST2(s,u); 
    s:=Generate(oper,t); 
  END;
  RETURN(s);
END Factor;


PROCEDURE Potency(): LIST;
(*Parse potency and generate code. *)
VAR s, t, u: LIST;
    oper: Token;
BEGIN 
  t:=SIL; s:= SIL; u:= SIL;
  SyntaxTest(lparen,34);
  IF tok = ident THEN t:=tokval; GetTok;
                      IF tok = lparen THEN GetTok;
                                       u:= TermList();
                                       GetTok; 
                                       t:=COMP(t,u); 
                                       (* generate function call *)
                       ELSIF tok = sil THEN u:= SIL;
                                            GetTok;
                                            t:= COMP(t,LIST1(u))
                       ELSIF tok = lbrack THEN GetTok;
                                            s:= TermList();
                                            t:= LIST2(t,s);
                                            t:= Generate(array,t);
                                            IF tok = rbrack THEN GetTok
                                               ELSE SyntaxError(12);
                                            END
                        END;
  ELSIF tok = number THEN t:= tokval; 
                          GetTok
  ELSIF tok = string THEN t:=tokval; 
                          t:=Generate(string,t); 
                          GetTok; 
  ELSIF tok = lparen THEN GetTok; 
                          t:=Expression(); (*already code*)
                          IF tok = rparen THEN GetTok ELSE SyntaxError(2) END
  ELSIF tok = sil THEN t:= SIL; 
                       GetTok
  ELSE SyntaxError(3); GetTok END;
  RETURN(t);
END Potency;


PROCEDURE Variable(): LIST;
(* parse variable. identifier or array *)
VAR   s, t: LIST;
BEGIN
(*1*) t:= SIL; s:=SIL;
(*2*) IF tok = ident THEN s:= tokval; GetTok; 
                     ELSE SyntaxError(1) END;
(*3*) IF tok = lbrack THEN GetTok; t:= TermList();
         s:=LIST2(s,t); s:=Generate(array,s); 
         IF tok = rbrack THEN GetTok; 
                         ELSE SyntaxError(2) END;
         END;
      RETURN(s) 
(*9*) END Variable;


(**************************************************************************)
(*                                                                        *)
(*                       Code generation                                  *)
(*                                                                        *)
(**************************************************************************)


PROCEDURE Generate(s: Token; t: LIST): LIST;
(* Generate code for token s *)
VAR code: LIST;

BEGIN 
  CASE s OF 
       power  : code:=COMP(POW,t) |
       times  : code:=COMP(MUL,t) |
       plus   : code:=COMP(ADD,t) |
       minus  : code:=COMP(SUB,t) |
       div    : code:=COMP(QUOT,t) |
       mod    : code:=COMP(REM,t) | 
       neq    : code:=COMP(NEQS,t) |
       eql    : code:=COMP(EQS,t) |
       lss    : code:=COMP(LTS,t) |
       leq    : code:=COMP(LEQ,t) |
       gtr    : code:=COMP(GTS,t) |
       geq    : code:=COMP(GEQ,t) |
       and    : code:=COMP(UND,t) |
       or     : code:=COMP(ODER,t) |
     becomes  : code:=COMP(SETAV,t) |
       if     : code:=COMP(IFS,t) |
       case   : code:=COMP(COND,t) |
       not    : code:=COMP(NOTS,t) |
       var    : code:=COMP(VARS,t) |
       array  : code:=COMP(ARY,LIST1(t)) |
       pragma : code:=COMP(prag,t) |
       begin  : IF t = SIL THEN code:=t
                   ELSIF RED(t) = SIL THEN code:=FIRST(t)
                   ELSE code:=COMP(PROGN,t) END |
       while  : code:=COMP(WHL,t) |
       begald : code:=COMP(PROGA,t) |
       repeat : code:=COMP(RPT,t) |
          for : code:=Genfor(t) |
        print : code:=COMP(prnt,LIST1(t)) |
         goto : code:=COMP(GTO,LIST1(t)) | 
        label : code:=COMP(LBEL,LIST1(t))  |
        ident : IF RED(t) = SIL THEN code:=FIRST(t)
                                ELSE code:= COMP(DE,t)
                END  |
          sil : code:=t |
    procedure : code:=COMP(DE,t) | 
       return : code:=COMP(ret,t) |
       string : code:=COMP(STRNG,t) 
         ELSE   code:=LIST2(QUOTE,t); 
  END;
  RETURN(code);
END Generate;


PROCEDURE Genfor(t: LIST): LIST;
(* generate code for "for - statement" *)
VAR h1,h2,h3,h4,i,t1,t2,t3,s,w,v: LIST;
BEGIN
(*0*) FIRST5(t,i,t1,t2,t3,s);
(*1*) (*start and step size *)
      h1:= LIST2(i,t1);
      h1:= Generate(becomes,h1);        (* (Assign i t1) *)
      h1:= LIST1(h1);
      IF t2 # SIL THEN w:= GENSYM();
         h2:= LIST2(t2,t1);
         h2:= Generate(minus,h2);  (* (sub t2 t1) *)
         h2:= LIST2(w,h2);
         h2:= Generate(becomes,h2); (* (Assign w (sub t2 t1)) *)
         h1:= COMP(h2,h1);   (* (Assign ...)  (Assign i t1) *)
         END;
(*2*) (*step direction *) 
      IF t2 # SIL THEN 
         v:=  GENSYM();  
         h2:= LIST2(v,-1);
         h2:= Generate(becomes,h2);   (* (Assign v -1) *)
         h2:= LIST1(h2); 
         h3:= LIST2(v,1);
         h3:= Generate(becomes,h3);   (* (Assign v +1) *)
         h2:= COMP(h3,h2);            (* (Assign v +1) (Assign v -1) *)
         h3:= LIST2(0,w);
         h3:= Generate(leq,h3);   (* (LE 0 w) *)
         h2:= COMP(h3,h2);        (* (LE 0 w) (Assign v +1) (Assign v -1) *)
         h2:= Generate(if,h2); (* (IFS (LE 0 w) ... (Assign v -1) *)
         h1:= COMP(h2,h1);   (* (IFS ... ) (Gensym()) ... (Assign i t1) *)
         END;
(*3*) (*loop condition *)
      IF t2 = SIL THEN h2:=LIST2(i,t3)  (* (LE i t3) *)
         ELSE h2:= LIST2(t3,i);
         h2:= Generate(minus,h2);      (* (sub t3 i) *)
         h2:= LIST2(v,h2);
         h2:= Generate(times,h2);      (* (mul v (sub t3 i)) *)
         h2:= LIST2(0,h2);
         END;
      h2:= Generate(leq,h2);        (* (LE 0 (mul v (sub t3 i))) *)
(*4*) (*loop body with increment *)
      IF t2 = SIL THEN h3:= LIST2(i,1)
                  ELSE h3:= LIST2(i,w) END;

      h3:= Generate(plus,h3);   (* (Add i w) *)
      h3:= LIST2(i,h3);
      h3:= Generate(becomes,h3);    (* (Assign i (Add i w)) *)
      h3:= LIST2(s,h3);             (* (s (Assign i (Add i w))) *)
      h3:= Generate(begin,h3);      (* (PROGN s (Assign i...)))) *)   
(*5*) (*initialization and while *)
      h3:= LIST1(h3);    
      h2:= COMP(h2,h3);             (* (LE 0 ... ))) (PROGN ...    )))) *)
      h2:= Generate(while,h2);      (* (WHL (LE ...))) (PROGN ... ))))) *)
      h1:= COMP(h2,h1);             (* (WHL ... ) ....  (Assign i t1) *)
      h1:= INV(h1);
      h1:= Generate(begin,h1);      (* (PROGN (WHL ... )... (Assign i t1)) *)
      RETURN(h1);
(*6*) END Genfor;
          

PROCEDURE CaseLabel(v: LIST): LIST;
(* generate code for "case - statement" *)
VAR   h1,a,c,C,s,L: LIST;
BEGIN
(*1*) FIRST3(v,a,C,s);
      L:= SIL;
(*2*) WHILE C # SIL DO ADV(C,c,C);
            h1:= LIST2(a,c);
            h1:= Generate(eql,h1);            (* (EQ a ci) *)
            IF L = SIL THEN L:=h1             (* (EQ a c1) *)
                       ELSE L:=LIST2(L,h1);   (* (OR L (EQ a ci)) *)
                            L:=Generate(or,L) END;
            END;  (* WHILE *)
(*3*) L:= LIST2(L,s);  (* (cond,s) *)
      RETURN(L);
(*4*) END CaseLabel;


(**************************************************************************)
(*                                                                        *)
(*       Initialization of Scanner and Parser                             *)  
(*                                                                        *)
(**************************************************************************)


PROCEDURE InitAscanner;
(*Initialize scanner procedures. *)
BEGIN
  Declare(KW,"AldesKeyWord");
  (* Enter key words *)
  KeyWord(do,"do");
  KeyWord(if,"if");
  KeyWord(case,"case");
  KeyWord(of,"of");
  KeyWord(then,"then");
  KeyWord(else,"else");
  KeyWord(while,"while");
  KeyWord(for,"for");
  KeyWord(until,"until");
  KeyWord(repeat,"repeat");
  KeyWord(safe,"safe");
  KeyWord(global,"global");
  KeyWord(intrinsic,"intrinsic");
  KeyWord(pragma,"pragma");
  KeyWord(const,"const");
  KeyWord(array,"array");
  KeyWord(To,",...,");
  KeyWord(goto,"goto");
  KeyWord(go,"go");
  KeyWord(to,"to");
  KeyWord(print,"print");
  KeyWord(return,"return")
END InitAscanner;


PROCEDURE InitAparser;
(*Initialize parser and scanner procedures. *)
VAR   X, s, v: LIST;
BEGIN
  InitAscanner; 
  Compiledp1(CLOUT,"PRINT"); 
  Declare(prnt,"PRINT"); 
  Declare(any,"ANY");
  Declare(ret,"RETURN");
  Declare(prag,"PRAGMA");
(*3*) blv:=SIL; LISTVAR(blv); 
      X:=ENV; 
      WHILE X <> SIL DO ADV2(X,s,v,X);
            IF NOT MEMQ(s,blv) THEN blv:=COMP(s,blv) END;
            END;
END InitAparser;


BEGIN
      InitAparser;
END ALDPARSE.            
(* -EOF- *)