IMPLEMENTATION MODULE YaflNTList;

FROM YaflParser IMPORT NonTerminal;
IMPORT Space;
IMPORT HashSpace;
FROM YaflGCode IMPORT GC;

  CLASS NTList (Element IN NonTerminal);
    INHERITS ArrayList(Element);
    
    VAR
      LazyRow: ARRAY OF Element;
      TheFather: NonTerminal;
    
    REDEFINE METHOD Row: ARRAY OF Element;
      BEGIN
      IF LazyRow = VOID THEN
        LazyRow := BASE;
        END;
      RESULT := LazyRow;
      END Row;
      
    VAR
      Tagged: BOOLEAN;
      
    METHOD UniqueTag;
      BEGIN    
      IF NOT Tagged THEN
        Tagged := TRUE;
        FOR El IN THIS DO
          El.UniqueTag;    
          END;
        END;
      END UniqueTag;
      
    REDEFINE METHOD Append (El: Element);
      BEGIN      
      LazyRow := VOID;
      BASE(El);
      END Append;
      
    REDEFINE METHOD Insert (Pos: INTEGER;
                            El: Element);
      BEGIN
      LazyRow := VOID;
      BASE(Pos, El);
      END Insert;
      
    REDEFINE METHOD Delete (Pos: INTEGER);
      BEGIN
      LazyRow := VOID;
      BASE(Pos);
      END Delete;
      
    METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE (Size);
      FOR i := 0 TO Size-1 DO
        RESULT[i] := Get(i);
        END;
      END SubTree;
      
    METHOD UniqueCheckType;
      BEGIN
      FOR El IN THIS DO
        El.UniqueCheckType;
        END;
      END UniqueCheckType;
      
    METHOD GenerateCode;
      BEGIN
      FOR El IN THIS DO
        -- Well... normally we don't have to do that
        WHAT El OF
          IN NonTerminal:
            TAG.Gc.GenerateCode;
            END;
          END;              
        --Get(i).Gc.GenerateCode;
        END;
      END GenerateCode;
      
    METHOD SetFather (TheFather: NonTerminal);
      BEGIN
      FOR El IN THIS DO
        El.SetFather (TheFather);
        END;
      THIS.TheFather := TheFather;
      END SetFather;
      
    METHOD Father: NonTerminal;
      BEGIN
      RESULT := TheFather;
      END Father;
      
    METHOD DeleteElement (El: NonTerminal): BOOLEAN;
      BEGIN
      FOR i IN 0 TO Size - 1 | Get(i) = El WHILE NOT RESULT DO
        Delete (i);
        RESULT := TRUE;
        END;
      END DeleteElement;
      
    METHOD Error (Message: ARRAY OF CHAR);
      BEGIN
      TheFather.Error (Message);
      END Error;
      
    METHOD Warning (Message: ARRAY OF CHAR);
      BEGIN
      TheFather.Warning (Message);
      END Warning;

    REDEFINE METHOD Get (Pos: INTEGER): Element;
      BEGIN
      IF (Pos >= 0) AND (Pos < Size) THEN
        RESULT := BASE (Pos);
	END;
      END Get;
      
  END NTList;
-----------------------------  
  CLASS DeclList (Element IN Declaration);
    INHERITS NTList (Element);
    
    METHOD Enter;
      BEGIN
      FOR El IN THIS DO
        El.Enter;
        END;
      END Enter;
      
    METHOD Find (Name: ARRAY OF CHAR): Element;
      BEGIN   
      ASSERT Name = Space.StoreString(Name);     
      RESULT := FIRST El IN THIS :- El.Id.Data = Name;
      END Find;
      
  END DeclList;                                  
-------------------------------------------------
  CLASS MethodList (Element IN MethodDeclaration);
    INHERITS DeclList (Element);
  END MethodList;
-----------------------------------------------------
  CLASS ClassList (Element IN ClassDeclaration);
    INHERITS DeclList (Element);
  END ClassList;
-------------------------------------------------
  CLASS MultiDeclList;
    INHERITS NonTerminal(DummyNTCodeGenerator);
    
    VAR
      Lists  : ARRAY OF DeclList;
      ListCount : INTEGER;    -- Actual number of lists
      GlobalList: DeclList(Declaration);
      
    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "MultiDeclList";
      END WhatAmI;
      
    REDEFINE METHOD CheckType;
      BEGIN
      GlobalList.UniqueCheckType;
      END CheckType;
  
    REDEFINE METHOD CREATE;
      CONST
        Size = 6;
      BEGIN      
      Lists.CREATE(Size);
      GlobalList.CREATE; 
      BASE(0,0);
      END CREATE;
    
    METHOD AppendList (l : DeclList);
      BEGIN          
      ASSERT ListCount < Lists.SIZE;
      Lists[ListCount] := l;
      ListCount := ListCount + 1;                        
      END AppendList;
      
    METHOD Find (Id : ARRAY OF CHAR) : Declaration;
      BEGIN    
      FOR DList IN Lists WHILE RESULT = VOID DO
        RESULT := FIRST El IN DList :- El.Id.Data = Id;
        END;      
      END Find;
                        
    METHOD AppendInList (List : DeclList; 
                         Decl : Declaration): BOOLEAN;
      BEGIN
      WHAT Decl OF
        IN List.Element:
          List.Append(TAG);
          RESULT := TRUE;
          END;
       ELSE
         -- That's not the good list, don't abort
        END;      
      END AppendInList;      
      
    METHOD Append (Decl : Declaration);
      VAR 
        Done : BOOLEAN;
      BEGIN                     
      ASSERT Decl <> VOID;
      GlobalList.Append (Decl);
      Decl.SetFather (THIS);
      FOR i := 0 TO ListCount - 1 WHILE NOT Done DO
        Done := AppendInList (Lists[i], Decl);        
        END;        
      IF NOT Done THEN
        Decl.Error ("Unexpected declaration");
        END;
      END Append;
      
    METHOD AppendFromList (l: DeclList);
      BEGIN
      FOR Decl IN l DO
        Append (Decl);
        END;
      END AppendFromList;
      
    METHOD Size : INTEGER;
      BEGIN                
      RESULT := GlobalList.Size;
      END Size;
      
    REDEFINE METHOD SubTree : ARRAY OF NonTerminal;
      BEGIN 
      RESULT.CREATE(Size);
      FOR i := 0 TO GlobalList.Size - 1 DO
        RESULT[i] := GlobalList.Get(i);
        END;
      END SubTree;
      
      REDEFINE METHOD Tag;
        BEGIN         
        GlobalList.UniqueTag;
        BASE;
        END Tag;
        
      METHOD Enter;
        BEGIN
        GlobalList.Enter;
        END Enter;
        
      METHOD DeleteElement (Decl: Declaration);
        VAR
          Found: BOOLEAN;
        BEGIN         
        IF GlobalList.DeleteElement (Decl) THEN
          FOR i := 0 TO ListCount - 1 WHILE NOT Found DO
            Found := Lists[i].DeleteElement (Decl);
            END;   
          ASSERT Found;
          Decl.MarkAsDeleted;
          END;
        END DeleteElement;  
        
    METHOD MarkAsDeleted;
      BEGIN             
      FOR i := 0 TO GlobalList.Size - 1 DO
        GlobalList.Get(i).MarkAsDeleted;
        END;
      END MarkAsDeleted;
        
    METHOD GrabDeclarations (TheList: AbstractList);
      BEGIN
      ASSERT TheList <> VOID;
      FOR i := 0 TO GlobalList.Size - 1 DO
        WHAT GlobalList.Get(i) OF
          IN TheList.Element:
            TheList.Append (TAG);
            END;
          END;
        END;
      END GrabDeclarations;
      
    METHOD EntireList: DeclList;
      BEGIN
      RESULT := GlobalList;
      END EntireList;
        
  END MultiDeclList;
----------------------------------
  CLASS NTSet(Element IN NonTerminal);
    VAR
      Table: HashSpace(Element);
    CONST
      InitialSize = 16;
  
    REDEFINE METHOD CREATE;
      BEGIN
      Table.CREATE (InitialSize);
      END CREATE;
    
    METHOD Add (El: Element);
      BEGIN    
      IF El <> VOID THEN
        IF NOT Includes (El) THEN
          IF (Table.AllocatedSize - Table.Stored < Table.AllocatedSize / 3) THEN
            Table.Resize (Table.AllocatedSize + Table.AllocatedSize / 2);
            END;
          ASSERT Table.AllocatedSize - Table.Stored >= Table.AllocatedSize / 3;
          VOID := Table.Store(El);
          END;
        ASSERT Includes (El);
        END;
      END Add;
      
    METHOD Includes (El: Element): BOOLEAN;
      VAR
        Candidate: Element;  
      BEGIN
      ASSERT El <> VOID;
      Candidate := Table.Find (El.HashValue);
      WHILE (Candidate <> VOID) AND (Candidate <> El) DO
        Candidate := Table.Next;
        END;
      RESULT := Candidate <> VOID;
      ASSERT RESULT IMPLIES Candidate = El;
      END Includes;
      
    METHOD AllocatedSize: INTEGER;
      BEGIN
      RESULT := Table.AllocatedSize;
      END AllocatedSize;
      
    METHOD Used: INTEGER;
      BEGIN             
      RESULT := Table.Stored;
      END Used;
      
    METHOD Row: ARRAY OF Element;
      BEGIN
      RESULT := Table.Row;
      ASSERT RESULT.SIZE = Table.Stored;
      END Row;
      
    REDEFINE METHOD CLONE(Other: NTSet(Element));
      BEGIN     
      Other.Table := Table.CLONE;
      END CLONE;
      
    METHOD DoUnion (Other: NTSet(Element));
      VAR
        p: ARRAY OF Element;
        BEGIN                 
      p := Other.Row;
      FOR i := 0 TO p.SIZE - 1 DO
        Add(p[i]);
        END;
      END DoUnion;
      
    METHOD Union (Other: NTSet(Element)): NTSet(Element);
      BEGIN     
      RESULT := THIS.CLONE;
      RESULT.DoUnion (Other);
      END Union;
      
  END NTSet;      

  
END YaflNTList;
