IMPLEMENTATION MODULE YaflClImplementation;

IMPORT LookAhead;
FROM Conversions       IMPORT IntConversions;
FROM YaflParser        IMPORT NonTerminal;
FROM YaflLex           IMPORT LexicalAnalyzer;
FROM List              IMPORT List;
FROM YaflSymbols       IMPORT SymbolTable;
FROM YaflModules       IMPORT DefinitionModule;
FROM YaflDeclarations  IMPORT InheritsClause, ConstDeclaration;
FROM YaflCfg           IMPORT YaflCfg, CurrentSpot;
FROM YaflIdentifiers   IMPORT IdentList;
FROM YaflMethods       IMPORT MethodDeclaration;
FROM YaflMetDefinition IMPORT MethodDefinition;
FROM YaflNTList        IMPORT NTList, MethodList;
FROM YaflParamClasses  IMPORT ClassFormal, ClassActual, ClassActualSet,
                              ClassFormalSet;
FROM YaflPredefined    IMPORT PredefItems, PredefClass, PredefDataItem, 
                              ThisDataItem;
FROM YaflPragmas       IMPORT Pragma;
FROM YaflClasses       IMPORT VirtualClassDecl, ConstrainedClassDecl;
FROM Streams           IMPORT StdOut;
FROM YaflDictionary   IMPORT ClassReference, MethodReference, 
                              ClassDictionary, MethodDictionary;
IMPORT SYSTEM;

  CLASS ClassImplementation;
    INHERITS ClassDeclaration(MethodImplementation, 
                              ImplementationModule,
                              ClassImplCodeGenerator);

    VAR
      TheMethodsInDual: DeclList (MethodDeclaration);
      TheClassBody: StatementList;
      TheClassDef: ClassDefinition;
      EndId: Ident; -- before this variable was local to the Parse method;
                    -- we moved it here for the debugger;
      TheLineCount: INTEGER;                    
      TheAttributesList, TheOnceList : DeclList (SingleDataItem);
      
                                                    
    REDEFINE METHOD CREATE (LineNr, ColNr : INTEGER);
      BEGIN      
      BASE (LineNr, ColNr);
      TheAttributesList.CREATE;
      SubDecls.AppendList (TheAttributesList);
      END CREATE;                  
      
    --------------------------------------------------
    -- The following attribute and method denote the
    -- methods attached to THIS for which a new entry
    -- must be reserved in the dual.
    --------------------------------------------------
    METHOD MethodsInDual : DeclList(MethodDeclaration);
      BEGIN
      IF TheMethodsInDual = VOID THEN
        TheMethodsInDual.CREATE;
        ---------------------------
        -- Method redefinitions should
        -- not be stored in the dual structure.
        ---------------------------
        FOR CurMeth IN Methods | CurMeth <> VOID,
                                 CurMeth.NeedsSeparateAnchor DO
          TheMethodsInDual.Append (CurMeth);
          END;
        ----------------------------
        -- Also, make room for deffered methods declared in the
        -- definition module only
        ----------------------------
        IF (Definition <> VOID) THEN
          FOR CurMethD IN Definition.Methods | CurMethD <> VOID,
                                               CurMethD.Deferred DO
            TheMethodsInDual.Append (CurMethD);
            END;
          END;
        ----------------------------
        -- Setting the method numbers in the minimal dual
        ----------------------------
        FOR i := 0 TO TheMethodsInDual.Size -1 DO
          TheMethodsInDual.Get(i).SetMethodNumberInMinDual(i);
          END;
        END; -- IF
      RESULT := TheMethodsInDual; 
      END MethodsInDual;
      
    METHOD ClassBody : StatementList;
      BEGIN
      RESULT := TheClassBody;
      END ClassBody;

    METHOD EndIdent: Ident; 
      BEGIN
      RESULT := EndId;
      END EndIdent;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE (1);
      RESULT [0] := TheClassBody;
      RESULT := BASE + RESULT;
      END SubTree;
      
    REDEFINE METHOD ClassFormals: ClassFormalSet;
      BEGIN
      IF Canonic <> THIS THEN
        RESULT := Canonic.ClassFormals;
       ELSE
        RESULT := BASE;
        END;
      END ClassFormals;
      
    REDEFINE METHOD MakeConstrainedClass (Actuals: ClassActualSet):
                                           ConstrainedClassDecl;
      BEGIN
      IF Definition <> VOID THEN
        RESULT := Definition.MakeConstrainedClass (Actuals);
       ELSE
        RESULT := BASE(Actuals);
        END;
      END MakeConstrainedClass;
                                                 
    REDEFINE METHOD Parse(Lkh: LookAhead);
      VAR
        FirstLine: INTEGER;
      BEGIN
      --------------------------
      -- Parse the Class header.
      --------------------------
      FirstLine := Lkh.LineNr;
      ParseClassDeclaration(Lkh);
      ASSERT Id <> VOID;
      IF Lkh.Ok THEN
        ParseDeclarationList(Lkh, TRUE);
        END;
      TheClassBody := Lkh.AcceptClassBody;
      SetSon (TheClassBody);              
      IF (TheClassBody <> VOID) AND (TheClassBody.Size > 0) THEN
        Warning ("Class initialization statement sequences are obsolete");
        END;
      Lkh.Accept (LexicalAnalyzer.End);
      EndId := Lkh.AcceptIdent;
      IF (EndId <> VOID) AND (EndId.Data <> Id.Data) THEN
        Error ("Non matching closing identifier");
        END;
      TheLineCount := Lkh.LineNr - FirstLine + 1;
      Lkh.Accept (LexicalAnalyzer.SemiColon);
      END Parse;
      
    REDEFINE METHOD LineCount: INTEGER;
      BEGIN
      RESULT := TheLineCount;
      END LineCount;

    VAR
      GotDefinition: BOOLEAN;

    METHOD Definition: ClassDefinition;
      VAR
        TheDefModule: DefinitionModule;
        ClassDecl: Declaration;
      BEGIN
      ------------------------------------------
      -- Find the Class definition of this class
      ------------------------------------------
      IF NOT GotDefinition THEN
        GotDefinition := TRUE;
        TheDefModule := Module.DefModule;
        --------------------------------
        -- A non accessible definition
        -- module error should have been
        -- catched much earlier
        --------------------------------
        IF TheDefModule <> VOID THEN
          ClassDecl := TheDefModule.GetClass(Id.Data);
          IF ClassDecl <> VOID THEN
            WHAT ClassDecl OF
              IN ClassDefinition:
                TheClassDef := TAG;
                END; -- In
              END; -- What
            END; -- If
          END; -- If
        END;
      RESULT := TheClassDef;
      END Definition;

    METHOD EnterOnceFields;
      BEGIN
      FOR i := 0 TO TheAttributesList.Size - 1 DO
        IF TheAttributesList.Get(i).Once THEN
          TheAttributesList.Get(i).Enter;
          END;
        END;
      END EnterOnceFields;

    REDEFINE METHOD ThisType: Type;
      BEGIN
      IF Definition <> VOID THEN
        RESULT := Definition.ThisType;
       ELSE
        RESULT := BASE;
        END;
      END ThisType;

    REDEFINE METHOD This: ThisDataItem;
      BEGIN
      IF Definition <> VOID THEN
        RESULT := Definition.This;
       ELSE
        RESULT := BASE;
        END;
      END This;

    REDEFINE METHOD Tag;
      VAR
        ClassDef: ClassDefinition;
      BEGIN
      --------------------------------------------
      -- Memorise the current Class Implementation
      --------------------------------------------
      CurrentSpot.PushCurrentClass (THIS);
      SymbolTable.Check;
      SymbolTable.PushLevel;
      EnterFormalClasses;                  
      
      IF CyclicInherits THEN
        Error ("Cyclic inheritance");
       ELSIF SubDecls.Size > 0 THEN
        ClassDef := Definition;
        SymbolTable.PushLevel;
        
        EnterInheritedSymbols;
        IF (ClassDef <> VOID) AND (ClassDef.SubDecls.Size > 0) THEN
          -------------------------------
          -- Enter the symbols present in
          -- the corresponding definition
          -- module
          -------------------------------
          ClassDef.SubDecls.Enter;
          END;
        SubDecls.Enter;
        TagSubDecls;
        SymbolTable.PopLevel;
       ELSE
        TagSubDecls;
        END;
      SymbolTable.PopLevel;
      IF TheClassBody <> VOID THEN
        SymbolTable.PushLevel;
        EnterOnceFields;
        TheClassBody.UniqueTag;
        SymbolTable.PopLevel;
        END;
--      CreateTheListOfMethodsInDual;
      CurrentSpot.PopCurrentClass;
      END Tag;

    METHOD TagSubDecls;
      BEGIN
      IF TheAttributesList <> VOID THEN
        TheAttributesList.UniqueTag;
        END;
      IF TheOnceList <> VOID THEN
        TheOnceList.UniqueTag;
        END;
      SubDecls.UniqueTag;
      END TagSubDecls;

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


    ------------------------------------------
    -- Check the conformity between the
    -- class implementation and its definition
    ------------------------------------------
    METHOD CheckConformity;
      VAR
        Def: ClassDefinition;
        Decl: Declaration;
        MethodImpl: MethodImplementation;
        Meth: MethodDefinition;
      BEGIN
      Def := Definition;
      IF Def <> VOID THEN
        ------------------------------
        -- First, check Class Formals.
        ------------------------------
        IF NOT ((Def.ClassFormals = VOID) IFF (ClassFormals = VOID)) THEN
          Error ("Class Formals differ from the definition[1]");
         ELSIF ClassFormals <> VOID THEN
          IF NOT ClassFormals.Match(Def.ClassFormals) THEN
            Error ("Class Formals differ from the definition[2]");
            END;
          END;
        ---------------------------
        -- Then, check inheritance
        -- relationships.
        ---------------------------
        IF NOT (Def.Inherits = VOID IFF Inherits = VOID) THEN
          Error("Inheritance relationship differs from the definition[1]");
         ELSIF Inherits <> VOID THEN
          Inherits.Match(Def.Inherits);
          END;
        ----------------------------------
        -- What if once ?
        ----------------------------------
        IF (Once AND NOT Def.Once) OR (Def.Once AND NOT Once) THEN
          Error ("Non matching definition and implementation ONCE flag");
          END;
        ----------------------------------
        -- Then, check methods and constants
        ----------------------------------
        FOR i := 0 TO Def.Methods.Size - 1 DO
          Meth := Def.Methods.Get(i);
          Decl := FindLocalDecl(Meth.Id.Data);
          IF Decl = VOID THEN
            MethodImpl := VOID;
           ELSE
            WHAT Decl OF
              IN MethodImplementation:
                MethodImpl := TAG;
                END;
             ELSE
              MethodImpl := VOID;
              END;
            END;
          IF Meth.Deferred THEN
            IF (MethodImpl <> VOID) THEN
              MethodImpl.Error ("A deferred method should "+
                                "not be implemented");
              END;
           ELSE
            -------------------------------
            -- The Methods are not deferred
            -------------------------------
            IF MethodImpl = VOID THEN
              Meth.Error ("Missing method implementation: " + Meth.Id.Data);
             ELSE
              IF NOT Meth.Match (MethodImpl, TRUE) THEN
                MethodImpl.Error("The implementation does not "+
                                  "match its definition");
                END;
              END;
            END;
          END;    
        -----------------------------
        -- We must only make sure that
        -- constants declaration do not
        -- conflict with implementation
        -- module's declaration.
        -----------------------------
        FOR i := 0 TO Consts.Size - 1 DO
          Decl := GetDecl(Consts.Get(i).Id.Data);
          IF (Decl <> VOID) AND (Decl <> Consts.Get(i)) THEN
            Decl.Error ("Conflicting definition with a constant");
            END;
          END;
        END;
      END CheckConformity;          
      
    METHOD RegisterOnceVariable (TheDecl: SingleDataItem);
      BEGIN                    
      ASSERT TheDecl <> VOID;
      ASSERT TheDecl.Once;
      ASSERT TheDecl.UseObjPtr;
      IF TheOnceList = VOID THEN
        TheOnceList.CREATE;
        END;
      TheOnceList.Append (TheDecl);
      END RegisterOnceVariable;
      
    METHOD GetOnceList: DeclList (SingleDataItem);
      BEGIN
      RESULT := TheOnceList;
      END GetOnceList;  
        
    METHOD ResetOnceList;
      BEGIN
      TheOnceList := VOID;
      END ResetOnceList;
      
    REDEFINE METHOD GetDecl (IdSearched: ARRAY OF CHAR): Declaration;
      BEGIN
      IF Definition <> VOID THEN
        RESULT := Definition.GetLocalDecl (IdSearched);
        END;
      IF RESULT = VOID THEN
        RESULT := GetLocalDecl (IdSearched);
        END;
      END GetDecl;

    ---------------------------------------------------
    -- Check types in all method bodies
    ---------------------------------------------------
    REDEFINE METHOD CheckType;
      BEGIN
      --------------------------------------------
      -- Memorise the current Class Implementation
      --------------------------------------------
      CurrentSpot.PushCurrentClass (THIS);
      
      --------------------------------------------
      -- Call the generic checking type method
      -- attached to ClassDeclarations; which
      -- checks inheritance clauses.
      --------------------------------------------
      BASE; 
      SubDecls.UniqueCheckType;
      IF (Definition <> VOID) THEN
        Definition.Consts.UniqueCheckType;
        Definition.Invariants.UniqueCheckType;
        END;
      ----------------------------
      -- Check the class's attributes for
      -- read and write accesses.
      ---------------------------- 
      FOR i := 0 TO TheAttributesList.Size - 1 DO
        TheAttributesList.Get(i).CheckUsage;
        END;
      CurrentSpot.PopCurrentClass;
      END CheckType;

    --------------------------------------------------------
    -- The Compatible method is redefined in such a way
    -- that the definition module attached to THIS is tested
    -- as well.
    --------------------------------------------------------
    REDEFINE METHOD Compatible (Other: ClassDeclaration): BOOLEAN;
      BEGIN
      RESULT := BASE (Other);
      IF (NOT RESULT) AND (Definition <> VOID) THEN
        RESULT := Definition.Compatible(Other);
        END;
      END Compatible;

    REDEFINE METHOD Match (Other: ClassDeclaration): BOOLEAN;
      BEGIN
      ASSERT Other <> VOID;
      RESULT := (Other = THIS) OR
                (Definition = Other);
      END Match;
      
    REDEFINE METHOD Canonic: ClassDeclaration;
      BEGIN
      IF Definition <> VOID THEN
        RESULT := Definition;
      ELSE
        RESULT := BASE;
        END;
      END Canonic;

    METHOD MarkableObjectsInAttributes: INTEGER;
      VAR 
        Attribute: SingleDataItem;
      BEGIN
      RESULT := 0;
      FOR i := 0 TO TheAttributesList.Size - 1 DO
        Attribute := TheAttributesList.Get(i);
        IF Attribute.GetType.UseObjPtr AND NOT Attribute.Once THEN
          RESULT := RESULT + 1;
          END;
        END;
      END MarkableObjectsInAttributes;  
      
    METHOD Attributes: DeclList(SingleDataItem);
      BEGIN
      RESULT := TheAttributesList;
      END Attributes;

    METHOD FindLocalDecl (Name: ARRAY OF CHAR): Declaration;
      BEGIN
      RESULT := SubDecls.Find (Name);
      END FindLocalDecl;
            
    --------------------
    -- Returns the sum of the complexities of the
    -- methods it contains.
    --------------------    
    METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      FOR i := 0 TO Methods.Size - 1 DO
        RESULT := RESULT + Methods.Get(i).CyclomaticComplexity;
        END;
      END CyclomaticComplexity;
      
    ----------------------------
    -- Removes the class from her module
    ---------------------------- 
    METHOD Remove;
      VAR
        l: DeclList(ClassImplementation);
        i: INTEGER;
      BEGIN
      l := Module.Classes;
      i := l.Index (THIS);
      IF (i >= 0) AND (i < l.Size) THEN
        l.Delete(i);
       ELSE
        ASSERT FALSE;
        END; 
      END Remove;

    ---------------------------
    -- Removes ASSERTs and DEBUGs from all methods
    ---------------------------
    METHOD RemoveDebug;
      BEGIN                          
      FOR m IN Methods DO
        m.RemoveDebug;
        END;
      END RemoveDebug;          
      
    ------------------------------------------
    
    REDEFINE METHOD EnterInDictionary;
                
      BEGIN
      CurrentSpot.PushCurrentClass (THIS);
      ASSERT IsTypeChecked;
      
      BASE;                 
      ASSERT Reference <> VOID;
      
      Reference.SetImplPos (Id.ColNr, Id.LineNr);
      Reference.SetCyclomaticComplexity (CyclomaticComplexity);
      FOR Attr IN Attributes DO
        Attr.EnterInDictionary;
        END;
        
      IF Once THEN
        Reference.SetOnce (TRUE);
        FOR Meth IN Methods | Meth.CallBackName <> VOID DO
          Meth.Reference.SetCallBack;
          END; -- FOR
        END; -- IF
      FOR Meth IN Methods | Meth.Publish DO
        Meth.Reference.SetPublished;
        END;
      CurrentSpot.PopCurrentClass;

      END EnterInDictionary;             
                            
    -----------------------------------------------
    
    METHOD RemoveUnused; 
      VAR
        Meth: MethodImplementation;
        MethRef: MethodReference;
      BEGIN
      FOR i := Methods.Size - 1 TO 0 BY -1  DO
        Meth := Methods.Get(i);
        ASSERT Meth <> VOID;
        IF NOT Meth.Deferred AND NOT Meth.Publish THEN
          MethRef := Meth.Reference;
          IF (MethRef = VOID) THEN
            StdOut.WriteLine ("Void Meth Ref: " + Module.Id.Data + "." +
                               Id.Data + "." + Meth.Id.Data);
           ELSIF MethRef.Removed THEN
            IF MethRef.UsefulRootMethod THEN
              Meth.RemoveStatements;
             ELSE
              Meth.Remove;
              END;
            END;
          END;
        END;
      END RemoveUnused;  
      
    REDEFINE METHOD Public: BOOLEAN;
      BEGIN
      RESULT := Definition <> VOID;
      END Public;

    METHOD UsesInline: BOOLEAN;
      BEGIN
      RESULT := THERE_IS Meth IN Methods :- Meth.UsesInline;
      END UsesInline;

  END ClassImplementation;

END YaflClImplementation;
