MODULE Parser;

IMPORT S := Scanner, R := Requests, Code;

PROCEDURE Chk(sym: INTEGER; msg: ARRAY OF CHAR);
BEGIN IF S.sym=sym THEN S.GetSym ELSE R.Fail(msg) END END Chk;

PROCEDURE VarDeclaration;
(* VarDeclaration -> VAR Identifier {"," Identifier} . *)
BEGIN
  REPEAT
    S.GetSym;
    IF S.sym#S.ident THEN R.Fail("Bezeichner in erwartet!") END;
    Code.DCL(S.identifier);
    S.GetSym;
  UNTIL S.sym#S.comma;
END VarDeclaration;

PROCEDURE Factor;
(* Factor         -> Identifier | Constant . *)
BEGIN
  CASE S.sym OF
  | S.ident : Code.MOVEvarD0  (S.identifier); S.GetSym;
  | S.const : Code.MOVEconstD0(S.constant  ); S.GetSym;
  ELSE R.Fail("Faktor erwartet") END;
END Factor;

PROCEDURE Expression;
(* Expression     -> [ "+" | "-" ] Factor { ( "+" | "-" ) Factor } . *)
VAR neg: BOOLEAN;   (* muß D0 negiert werden? *)
BEGIN
  neg := FALSE;
  IF S.sym IN {S.plus,S.minus} THEN neg := S.sym=S.minus; S.GetSym END;
  Factor;
  IF neg THEN Code.NEGD0 END;
  WHILE S.sym IN {S.plus,S.minus} DO
    neg := S.sym=S.minus; S.GetSym;
    Code.MOVED0D1;
    Factor;
    IF neg THEN Code.NEGD0 END;
    Code.ADDD1D0;
  END;
END Expression;

PROCEDURE Statement;
(* Statement      -> Assignment | While | Print .            *)
(* Assignment     -> Identifer "=" Expression .              *)
(* While          -> WHILE Expression DO { Statement } END . *)
(* Print          -> PRINT Expression                        *)
VAR
  varname: S.Identifier;                      (* Ziel einer Zuweisung *)
  start,end : INTEGER;                   (* Labels bei einer Schleife *)
BEGIN
  CASE S.sym OF
  | S.ident: varname := S.identifier; S.GetSym;          (* Zuweisung *)
             Chk(S.equal,"'=' erwartet!");
             Expression;
             Code.MOVED0var(varname);
  | S.while: S.GetSym;                                    (* Schleife *)
             start := Code.GetLabel(); end := Code.GetLabel();
             Code.Label(start);
             Expression;
             Chk(S.do,"DO erwartet!");
             Code.TSTD0; Code.BLE(end);
             WHILE S.sym#S.end DO Statement END;
             Chk(S.end,"END erwartet!");
             Code.BRA(start); Code.Label(end);
  | S.print: S.GetSym; Expression; Code.PrintD0;           (* Ausgabe *)
  ELSE R.Fail("Statement erwartet!") END;
END Statement;

PROCEDURE Program*;
(* Program -> PROGRAM [ VarDeclaration ] BEGIN { Statement } END. *)
VAR start: INTEGER;
BEGIN
  S.GetSym; Chk(S.program,"PROGRAM erwartet!");
  start := Code.GetLabel(); Code.BRA(start);
  IF S.sym=S.var THEN VarDeclaration END;
  Chk(S.begin,"BEGIN erwartet!");
  Code.StartUp(start);
  WHILE S.sym#S.end DO Statement END;
  Code.CleanUp;
  Chk(S.end,"END erwartet!");
END Program;

END Parser.
