IMPLEMENTATION MODULE YaflStatements;

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

FROM YaflCfg          IMPORT YaflCfg, CurrentSpot;
FROM YaflExpressions  IMPORT Expression;
FROM YaflLex          IMPORT LexicalAnalyzer;
FROM YaflPredefined   IMPORT PredefItems, ResultDataItem;
FROM YaflType         IMPORT Type;
FROM YaflDesignator   IMPORT DesigElement;
FROM YaflDeclarations IMPORT SingleDataItem;
FROM YaflMethods      IMPORT MethodDeclaration;
  
  CLASS Statement(gc IN StatementCodeGenerator);
    INHERITS NonTerminal(gc);

    VAR
      TheLevel: INTEGER;
            
    REDEFINE METHOD CREATE (LineNr, ColNr: INTEGER);    
      BEGIN
      BASE (LineNr, ColNr);
      TheLevel := -1;
      END CREATE;
      
    METHOD NestingLevel: INTEGER;
      VAR
        p: NonTerminal;
      BEGIN
      IF TheLevel < 0 THEN
        p := Father;
        WHILE TheLevel < 0 DO
          WHAT p OF
            IN Statement:
              TheLevel := TAG.NestingLevel + 1;
              END;
           ELSE
            p := p.Father;
            IF p = VOID THEN
              TheLevel := 0;
              END;
            END;
          END;
        END;
      RESULT := TheLevel;
      END NestingLevel;
      
    METHOD IsEmpty: BOOLEAN;
      BEGIN
      END IsEmpty;
      
    METHOD UsesValueStack: BOOLEAN;
      BEGIN
      DEBUG
        StdOut.WriteString ("UsesValueStack not redefined: " + WhatAmI);
        END;
      END UsesValueStack;
      
    ------------------------------
    -- The CallsMethod method returns TRUE if the statement
    -- includes a method invocation.    
    ------------------------------
    METHOD CallsMethod: BOOLEAN;
      BEGIN
      DEBUG
        StdOut.WriteLine ("CallsMethod not redefined: " + WhatAmI);
        END;
      END CallsMethod;
      
    METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      DEBUG
        StdOut.WriteLine ("Cyclomatic complexity not redefined for "
                           + WhatAmI);
        END;                           
      END CyclomaticComplexity;
                     
    METHOD MethodContext: MethodImplementation;
      VAR
        TheRef: ONCE Ref (MethodImplementation);
      BEGIN             
      IF TheRef = VOID THEN
        TheRef.CREATE (VOID);
        END;
      GetAncestor (TheRef);
      RESULT := TheRef.Get;
      TheRef.Set (VOID);
      END MethodContext;
      
    METHOD LoopContext: LoopStatement;
      VAR
        TheRef: ONCE Ref (LoopStatement);
      BEGIN             
      IF TheRef = VOID THEN
        TheRef.CREATE (VOID);
        END;
      GetAncestor (TheRef);
      RESULT := TheRef.Get;
      TheRef.Set (VOID);
      END LoopContext;

  END Statement;
---------------------------------------------
  CLASS AtomicStatement(gc IN StatementCodeGenerator);
    INHERITS Statement(gc);
  END AtomicStatement;
---------------------------------------------
  CLASS CompoundStatement(gc IN StatementCodeGenerator);
    INHERITS Statement(gc);
  END CompoundStatement;
----------------------------------------
  CLASS NopStatement;
    INHERITS AtomicStatement(NopStatementCodeGenerator);
    
    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      END SubTree;


    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      END Parse;

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

    REDEFINE METHOD Tag;
      BEGIN
      END Tag;

    REDEFINE METHOD CheckType;
      BEGIN
      END CheckType;
      
    REDEFINE METHOD IsEmpty: BOOLEAN;
      BEGIN
      RESULT := TRUE;
      END IsEmpty;
      
    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN  
      -- Default to FALSE.
      END UsesValueStack;
      
    REDEFINE METHOD CallsMethod: BOOLEAN;
      BEGIN  
      -- Default to FALSE.
      END CallsMethod;
      
    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      -- Default to 0.
      END CyclomaticComplexity;

  END NopStatement;
----------------------------------------

  CLASS Assignment;
    INHERITS AtomicStatement(AssignmentCodeGenerator);

    VAR
      TheNTDesignator: Desig;
      TheRightExpr: TypedNonTerminal;
      ToVoidFlag: BOOLEAN;
        
    METHOD ToVoid: BOOLEAN;
      BEGIN
      RESULT := ToVoidFlag;
      END ToVoid;  
      
--    REDEFINE METHOD IsNoChange: BOOLEAN;
--      BEGIN
--      IF ToVoid THEN
--        RESULT := TheRightExpr.IsNoChange;
--      ELSE
--        IF TheNTDesignator.Last.Id.GetRef <> VOID THEN
--          WHAT TheNTDesignator.Last.Id.GetRef OF
--            IN ResultDataItem:
--              RESULT := TheRightExpr.IsNoChange;
--              --IF NOT RESULT THEN
--              --  Warning("assignment to RESULT with changes in NO_CHANGE method");
--              -- END;
--              END;
--            IN SingleDataItem:
--              RESULT := TheRightExpr.IsNoChange;
--              --IF NOT RESULT THEN
--              --  Warning("assignment to local variable with changes in NO_CHANGE method");
--              --  END;
--              END;
--          ELSE
--            -- return FALSE;
--            END;
--          END;
--        END;
--      END IsNoChange;

--    REDEFINE METHOD IsNoChangeHC: BOOLEAN;
--      BEGIN
--      RESULT := BASE;
--      IF (NOT RESULT) AND ToVoid THEN
--        RESULT := TheRightExpr.IsNoChangeHC;
--      ELSE
--        IF TheNTDesignator.Last.Id.GetRef <> VOID THEN
--          WHAT TheNTDesignator.Last.Id.GetRef OF
--            IN ResultDataItem:
--              RESULT := TheRightExpr.IsNoChangeHC;
--              END;
--            IN SingleDataItem:
--              RESULT := TheRightExpr.IsNoChangeHC;
--              --IF NOT RESULT THEN
--              --  Warning("assignment to local variable with changes in NO_CHANGE method");
--              --  END;
--              END;
--          ELSE
--            -- return FALSE;
--            END;
--          END;
--        END;
--      END IsNoChangeHC;

    METHOD IsChange: BOOLEAN;
      BEGIN
      IF NOT ToVoid THEN
        IF TheNTDesignator.Last.Id.GetRef <> VOID THEN
          WHAT TheNTDesignator.Last.Id.GetRef OF
            IN ResultDataItem:
              -- return FALSE
              END;
            IN SingleDataItem:
              IF NOT TAG.IsLocal THEN
                RESULT := TRUE;
                END;
              END;
          ELSE
            RESULT := TRUE;
            END;
          END;
        END;
      END IsChange;

    REDEFINE METHOD CallsMethod: BOOLEAN;
      BEGIN  
      IF NOT ToVoidFlag THEN
        RESULT := TheNTDesignator.WithSideEffects;
        END;
      RESULT := RESULT OR TheRightExpr.WithSideEffects;
      END CallsMethod;

    REDEFINE METHOD CREATE(LineNr, ColNr: INTEGER;
                           NTDesig: Desig);
      BEGIN
      BASE(LineNr, ColNr);
      TheNTDesignator := NTDesig;
      ToVoidFlag := NTDesig.IsVoid;
      IF NOT ToVoidFlag THEN
        NTDesig.UseInWriteContext;
        END;
      SetSon (TheNTDesignator);
      END CREATE;
             
    METHOD ParseRightSide(Lkh: LookAhead);
      BEGIN
      TheRightExpr := Lkh.AcceptPlainExpr;
      SetSon (TheRightExpr);
      END ParseRightSide;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE( 2 );
      RESULT[0] := TheNTDesignator;
      RESULT[1] := TheRightExpr;
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      TheNTDesignator := Lkh.AcceptDesig(Lkh.PlainExpressionContext);
      Lkh.Accept (LexicalAnalyzer.Becomes);
      TheRightExpr := Lkh.AcceptPlainExpr;
      SetSon (TheRightExpr);
      SetSon (TheNTDesignator);
      END Parse;

    METHOD RightExpr: TypedNonTerminal;
      BEGIN
      RESULT:= TheRightExpr;
      END RightExpr;
      
    METHOD LeftExpr: Desig;
      BEGIN
      RESULT := TheNTDesignator;
      END LeftExpr;

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

    REDEFINE METHOD Tag;
      BEGIN
      IF NOT ToVoidFlag THEN
        TheNTDesignator.UniqueTag;
        END;
      TheRightExpr.UniqueTag;
      END Tag;
      
    -----------------------------------
    -- Make sure that the left expression
    -- is an acceptable LValue, the right
    -- expression returns a value, and the
    -- types of the expressions are compatible.
    -----------------------------------
    REDEFINE METHOD CheckType;
      VAR
        Left, Right: Type;
      BEGIN
      Right := TheRightExpr.GetType;
      IF Right = VOID THEN
        Error ("Expression does not return a value");
       ELSIF NOT ToVoidFlag THEN
        Left := TheNTDesignator.GetType;
        IF Left = VOID THEN
          Error ("LValue expected");
         
         ELSIF NOT TheNTDesignator.LValue THEN
          Error ("Wrong LValue");
         ELSIF NOT TheRightExpr.Optimized.ExprCompatible(Left) THEN 
          Error ("Type mismatch in assignment (" + Left.Image + "/" +
                                                   Right.Image);
          END;
        END;
      END CheckType;
                        
    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN  
      RESULT := (NOT ToVoidFlag AND TheNTDesignator.UsesValueStack)
                         OR TheRightExpr.UsesValueStack;
      END UsesValueStack;
      
    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      -- Default to 0.
      END CyclomaticComplexity;
 
  END Assignment;

----------------------------------------
  CLASS MethodInvStatement;
    INHERITS AtomicStatement(MethInvStatCodeGenerator);
    VAR
      TheNTDesignator: Desig;
                   
    METHOD NTDesignator: Desig;
      BEGIN            
      RESULT := TheNTDesignator;
      END NTDesignator;  
      
    REDEFINE METHOD CallsMethod: BOOLEAN;
      BEGIN
      RESULT := TheNTDesignator.WithSideEffects;
      END CallsMethod;

    REDEFINE METHOD CREATE(LineNr, ColNr: INTEGER;
                           NTDesig: Desig);
      BEGIN
      BASE(LineNr, ColNr);
      TheNTDesignator := NTDesig;
      SetSon (TheNTDesignator);
      END CREATE;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE(1);
      RESULT[0] := TheNTDesignator;
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      ASSERT TheNTDesignator.Last <> VOID;
      IF TheNTDesignator.Last.BrExpr <> VOID THEN
        Error("Badly formed method invocation");
        END;
      END Parse;

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

    REDEFINE METHOD Tag;
      BEGIN
      TheNTDesignator.UniqueTag;
      END Tag;

    REDEFINE METHOD CheckType;
      BEGIN
      IF TheNTDesignator.GetType <> VOID THEN
        Error ("The return value is not assigned");
        END;
      END CheckType;

    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN  
      RESULT := TheNTDesignator.UsesValueStack;
      END UsesValueStack;

    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      -- Default to 0.
      END CyclomaticComplexity;

  END MethodInvStatement;
----------------------------------------
  CLASS DebugStatement;
    INHERITS CompoundStatement(DebugStatCodeGenerator);
    VAR
      TheStatementList: StatementList;
      
    METHOD Statements: StatementList;
      BEGIN
      RESULT := TheStatementList;
      END Statements;

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

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      Lkh.Accept (LexicalAnalyzer.Debug);
      TheStatementList := Lkh.AcceptStatementList;
      SetSon (TheStatementList);
      Lkh.Accept (LexicalAnalyzer.End);
      END Parse;

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

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

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

    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := (TheStatementList <> VOID) AND 
                (TheStatementList.UsesValueStack);
      END UsesValueStack;
      
    REDEFINE METHOD IsEmpty: BOOLEAN;
      BEGIN
      RESULT := TRUE;
      END IsEmpty;
      
    REDEFINE METHOD CallsMethod: BOOLEAN;
      BEGIN
      RESULT := (TheStatementList <> VOID) AND 
                (TheStatementList.CallsMethod);
      END CallsMethod;

    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      IF TheStatementList <> VOID THEN
        RESULT := TheStatementList.CyclomaticComplexity;
        END;
      END CyclomaticComplexity;
    
  END DebugStatement;

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

  CLASS AssertStatement;
    INHERITS AtomicStatement(AssertStatCodeGenerator);
    
    VAR
      TheExpr: TypedNonTerminal;

    REDEFINE METHOD CallsMethod: BOOLEAN;
      BEGIN
      ASSERT TheExpr <> VOID;
      RESULT := TheExpr.WithSideEffects;
      END CallsMethod;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE(1);
      RESULT[0] := TheExpr;
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      Lkh.Accept (LexicalAnalyzer.Assert);
      TheExpr := Lkh.AcceptPlainExpr;
      SetSon (TheExpr);
      END Parse;

    METHOD Expr: TypedNonTerminal;
      BEGIN
      RESULT:= TheExpr;
      END Expr;

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

    REDEFINE METHOD Tag;
      BEGIN
      TheExpr.UniqueTag;
      END Tag;

    REDEFINE METHOD CheckType;
      VAR
        TheType: Type;
      BEGIN
      TheType := TheExpr.GetType;
      IF (TheType = VOID) OR (TheType.SimpleType <> PredefItems.Boolean) OR
         (TheType.ArrayLevel <> 0) THEN
        Error ("Non boolean assert argument");
        END;
      END CheckType;

    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := TheExpr.UsesValueStack;
      END UsesValueStack;

    REDEFINE METHOD IsEmpty: BOOLEAN;
      BEGIN
      RESULT := TRUE;
      END IsEmpty;
      
    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      -- Default result to 0.
      END CyclomaticComplexity;

  END AssertStatement;

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

  CLASS InLineStatement;
    INHERITS AtomicStatement(InLineStatCodeGenerator);

    VAR
      LineList: LinkedList;
      
    REDEFINE METHOD CallsMethod: BOOLEAN;
      BEGIN
      RESULT := TRUE;
      END CallsMethod;
      
    REDEFINE METHOD CREATE(LineNr, ColNr: INTEGER);
      BEGIN
      BASE(LineNr, ColNr);
      LineList.CREATE;
      END CREATE;


    METHOD GetLineList: LinkedList;
      BEGIN
      RESULT := LineList;
      END GetLineList;

    METHOD AddLine (a: ARRAY OF CHAR);
      VAR
        LinkStr: StringLinkable;
      BEGIN
      LinkStr.CREATE(a);
      LineList.Append (LinkStr);
      END AddLine;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      -- Void subtree
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      Lkh.Accept (LexicalAnalyzer.Inline);
      WHILE Lkh.Ok AND (Lkh.CurrentToken <> LexicalAnalyzer.End) DO
        IF Lkh.CurrentToken = LexicalAnalyzer.InlineString THEN
          AddLine (Lkh.CurrentInline);
         ELSE
          Lkh.Error ("Inline expected");
          END;
        Lkh.GetToken;
        END;
      Lkh.Accept (LexicalAnalyzer.End);
      END Parse;

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

    REDEFINE METHOD Tag;
      BEGIN
      MethodContext.SetUseInline;
      END Tag;

    REDEFINE METHOD CheckType;
      BEGIN
      END CheckType;

    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := TRUE;  -- Just to be sure, in case of...
      END UsesValueStack;
      
    REDEFINE METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      -- Default result to 0.
      END CyclomaticComplexity;
      
  END InLineStatement;

----------------------------------------
  CLASS StatementList;
    INHERITS NonTerminal(StatListCodeGenerator);
    
    VAR
      TheStatList: NTList(Statement);
    
    REDEFINE METHOD CREATE(LineNr, ColNr: INTEGER);
      BEGIN
      BASE (LineNr, ColNr);
      TheStatList.CREATE;
      END CREATE;
      
    METHOD Append (Stat: Statement);
      BEGIN
      ASSERT Stat <> VOID;
      Stat.SetFather (THIS);
      TheStatList.Append (Stat);
      END Append;
      
    METHOD GetList: NTList(Statement);
      BEGIN
      RESULT := TheStatList;
      END GetList;
      
    REDEFINE METHOD Tag;
      BEGIN
      TheStatList.UniqueTag;
      END Tag;
      
    METHOD Size: INTEGER;
      BEGIN
      RESULT := TheStatList.Size;
      END Size;
      
    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE (TheStatList.Size);
      FOR i := 0 TO TheStatList.Size - 1 DO
        RESULT[i] := TheStatList.Get(i);
        END;
      END SubTree;
      
    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "StatementList";
      END WhatAmI;
      
    METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := THERE_IS Stat IN TheStatList :- Stat.UsesValueStack;
      END UsesValueStack;
      
    METHOD CallsMethod: BOOLEAN;
      BEGIN
      RESULT := THERE_IS Stat IN TheStatList :- Stat.CallsMethod;
      END CallsMethod;
      
    METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      FOR Stat IN TheStatList  DO
        RESULT := RESULT + Stat.CyclomaticComplexity;
        END;
      END CyclomaticComplexity;
      
    METHOD IsEmpty: BOOLEAN;
      BEGIN
      RESULT := FOR_ALL Stat IN TheStatList :- Stat.IsEmpty;
      END IsEmpty;
      
    REDEFINE METHOD CheckType;
      BEGIN
      TheStatList.UniqueCheckType;
      END CheckType;
      
  END StatementList; 
  
END YaflStatements;
