IMPLEMENTATION MODULE YaflIfStatement;

FROM Linked         IMPORT LinkedList, StringLinkable;
FROM Streams        IMPORT StdOut;
IMPORT String;

FROM LookAhead       IMPORT LookAhead;
FROM YaflCfg         IMPORT YaflCfg, CurrentSpot;
FROM YaflExpressions IMPORT Expression;
FROM YaflLex         IMPORT LexicalAnalyzer;
FROM YaflLiteral     IMPORT BooleanLiteral;
FROM YaflNTList      IMPORT NTList;
FROM YaflPredefined  IMPORT PredefItems;
FROM YaflStatements  IMPORT StatementList;
FROM YaflType        IMPORT Type;
FROM YaflDesignator  IMPORT DesigElement;
FROM YaflGStatements IMPORT IfStatCodeGenerator;

  CLASS IfStatement;
    INHERITS CompoundStatement(IfStatCodeGenerator);
    
    VAR
      IfAltList: NTList(IfAlt);
      TheElseAlt: StatementList;

    METHOD ElseStatements: StatementList;
      BEGIN
      RESULT := TheElseAlt;
      END ElseStatements;
      
    METHOD IfAlts: NTList(IfAlt);    
      BEGIN
      RESULT := IfAltList;
      END IfAlts;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE (1);
      RESULT[0] := TheElseAlt;
      IF IfAltList <> VOID THEN
        RESULT := RESULT + IfAltList.SubTree;
        END;
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      VAR
        TheCond: TypedNonTerminal;
        TheStatementList: StatementList;
        TheIfAlt: IfAlt;
      BEGIN
      TheIfAlt.CREATE(Lkh.LineNr, Lkh.ColNr);
      Lkh.Accept (LexicalAnalyzer.If);
      TheCond := Lkh.AcceptPlainExpr;
      Lkh.Accept (LexicalAnalyzer.Then);
      TheStatementList := Lkh.AcceptStatementList;
      TheIfAlt.SetCondition(TheCond);
      TheIfAlt.SetStatementList (TheStatementList);
      IfAltList := Lkh.AcceptIfAltList;
      IF IfAltList = VOID THEN
        IfAltList.CREATE;
        END;
      IfAltList.Insert (0, TheIfAlt);
      IfAltList.SetFather (THIS);
      TheElseAlt := Lkh.AcceptElseAlt;
      SetSon(TheElseAlt);
      Lkh.Accept (LexicalAnalyzer.End);
      END Parse;

    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "IfStatement";
      END WhatAmI;

    REDEFINE METHOD Tag;
      BEGIN
      IfAltList.UniqueTag;
      IF TheElseAlt <> VOID THEN
        TheElseAlt.UniqueTag;
        END;
      END Tag;

     REDEFINE METHOD CheckType;
       BEGIN
       IfAltList.UniqueCheckType;
       IF TheElseAlt <> VOID THEN
         TheElseAlt.UniqueCheckType;
         END;
       END CheckType;

     REDEFINE METHOD UsesValueStack: BOOLEAN;
       BEGIN
       RESULT := (THERE_IS Alt IN IfAltList :- Alt.UsesValueStack) OR
                 ((TheElseAlt <> VOID) AND TheElseAlt.UsesValueStack);
       END UsesValueStack;
       
     REDEFINE METHOD CallsMethod: BOOLEAN;
       BEGIN
       RESULT := (THERE_IS Alt IN IfAltList :- Alt.CallsMethod) OR
                 ((TheElseAlt <> VOID) AND TheElseAlt.CallsMethod);
       END CallsMethod;
      
    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      RESULT := 1;
      FOR Alt IN IfAltList DO
        RESULT := RESULT + Alt.CyclomaticComplexity;
        END;
      IF TheElseAlt <> VOID THEN
        RESULT := RESULT + TheElseAlt.CyclomaticComplexity;
        END;      
      END CyclomaticComplexity;
    
  END IfStatement;

----------------------------------------

  CLASS IfAlt;
    INHERITS NonTerminal(ElsIfCodeGenerator);

    VAR
      TheCond: TypedNonTerminal;
      TheStatementList: StatementList;

    METHOD SetCondition (TheCond: TypedNonTerminal);
      BEGIN
      THIS.TheCond := TheCond;
      SetSon (TheCond);
      END SetCondition;
      
    METHOD SetStatementList (StmtList: StatementList);
      BEGIN
      THIS.TheStatementList := StmtList;
      SetSon (StmtList);
      END SetStatementList;
      
    METHOD Statements: StatementList;
      BEGIN
      RESULT := TheStatementList;
      END Statements;            
      
    METHOD CallsMethod: BOOLEAN;
      BEGIN
      RESULT := (TheCond.WithSideEffects) OR 
                ((TheStatementList <> VOID) AND 
                 (TheStatementList.CallsMethod));
      END CallsMethod;

    -----------------------------------                                                 
    -- The IF condition must be boolean
    -----------------------------------  
    REDEFINE METHOD CheckType;
      VAR
        TheCondType: Type;
      BEGIN
      TheCondType := TheCond.GetType;
      IF (TheCondType = VOID) OR
         (TheCondType.SimpleType <> PredefItems.Boolean) OR
         (TheCondType.ArrayLevel <> 0) THEN
        Error ("Non boolean IF condition");
        END;
      IF TheStatementList <> VOID THEN
        TheStatementList.UniqueCheckType;
        END;
      END CheckType;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      IF TheStatementList = VOID THEN
        RESULT.CREATE(1);
       ELSE
        RESULT.CREATE(2);
        RESULT[1] := TheStatementList;
        END;
      RESULT[0] := TheCond;
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      Lkh.Accept (LexicalAnalyzer.Elsif);
      TheCond := Lkh.AcceptPlainExpr;
      SetSon (TheCond);
      Lkh.Accept (LexicalAnalyzer.Then);
      TheStatementList := Lkh.AcceptStatementList;
      SetSon (TheStatementList);
      END Parse;


    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "IfAlt";
      END WhatAmI;

    REDEFINE METHOD Tag;
      BEGIN
      TheCond.UniqueTag;
      IF TheStatementList <> VOID THEN
        TheStatementList.UniqueTag;
        END;
      END Tag;

    METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := TheCond.UsesValueStack OR 
               ((TheStatementList <> VOID) AND 
                (TheStatementList.UsesValueStack));
      END UsesValueStack;
      
    METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      RESULT := 1;
      IF TheStatementList <> VOID THEN
        RESULT := RESULT + TheStatementList.CyclomaticComplexity;
        END;
      END CyclomaticComplexity;      

    METHOD Cond: TypedNonTerminal;
      BEGIN
      RESULT := TheCond;
      END Cond;
    
    METHOD Always: BOOLEAN;
      BEGIN
      IF Cond.GetFolded <> VOID THEN
        WHAT Cond.GetFolded OF
          IN BooleanLiteral:
            RESULT := TAG.Value;
            END;
          END;
        END;
      END Always;

    METHOD Never: BOOLEAN;
      BEGIN
      IF Cond.GetFolded <> VOID THEN
        WHAT Cond.GetFolded OF
          IN BooleanLiteral:
            RESULT := NOT TAG.Value;
            END;
          END;
        END;
      END Never;

  END IfAlt;

END YaflIfStatement;
