IMPLEMENTATION MODULE YaflImport;

IMPORT ModuleTable;
FROM YaflCfg     IMPORT YaflCfg;
FROM YaflClasses IMPORT ClassDeclaration;
FROM YaflSymbols IMPORT SymbolTable;
FROM YaflLex IMPORT LexicalAnalyzer;
FROM YaflNTList IMPORT NTList, DeclList;
FROM Streams IMPORT StdOut;

  CLASS ImportClause;
    INHERITS Declaration(ImportClauseCodeGenerator);

    VAR
      TheModuleName: Ident;
      TheIdentList: IdentList;
      TheDefModule: DefinitionModule;
      
    METHOD Qualified: BOOLEAN;
      BEGIN
      RESULT := (TheIdentList <> VOID) AND (TheIdentList.Size > 0);
      END Qualified;

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

    METHOD Module: DefinitionModule;
      BEGIN
      IF TheDefModule = VOID THEN
        TheDefModule := ModuleTable.LocateDefinitionModule (TheModuleName.Data);
        IF TheDefModule <> VOID THEN
          TheModuleName.SetRef (TheDefModule);
          END;
        END;
      RESULT := TheDefModule;
      END Module;
      
    METHOD ErrorFound : BOOLEAN;
      BEGIN
      RESULT := Module = VOID;
      END ErrorFound;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      IF NOT YaflCfg.Interrupted THEN
        IF Lkh.CurrentToken = LexicalAnalyzer.Import THEN
          Lkh.GetToken;
          TheModuleName := Lkh.AcceptIdent;
          Lkh.Accept (LexicalAnalyzer.SemiColon);
         ELSE
          Lkh.Accept (LexicalAnalyzer.From);
          TheModuleName := Lkh.AcceptIdent;
          Lkh.Accept (LexicalAnalyzer.Import);
          TheIdentList := Lkh.AcceptIdentList;
          IF TheIdentList <> VOID THEN
            TheIdentList.SetFather (THIS);
            END; -- IF
          Lkh.Accept (LexicalAnalyzer.SemiColon);
          END; -- IF
        SetSon (TheModuleName);
        END; -- IF 
      END Parse;

    REDEFINE METHOD Tag;
      VAR
        DefModule: DefinitionModule;
        Cl: Declaration;
        Id: Ident;
      BEGIN      
      IF NOT YaflCfg.Interrupted THEN
        DefModule := Module;
        IF DefModule = VOID THEN
          Error ("Error while importing module "+ TheModuleName.Data);
         ELSE
          IF TheModuleName.Data = GrandPa.Id.Data THEN
            Error ("Self import");
           ELSIF TheModuleName.Data <> DefModule.Id.Data THEN
            Error ("Non matching import and definition module name:" +
                   TheModuleName.Data);
            END;
          IF NOT Qualified THEN
            IF DefModule.Canonical THEN
              Cl := TheDefModule.GetClass (TheModuleName.Data);
              ASSERT Cl <> VOID;
              TheModuleName.SetRef (Cl);
             ELSE
              TheModuleName.SetRef (DefModule);
              END;
           ELSE
            FOR i := 0 TO TheIdentList.Size - 1 DO
              Id := TheIdentList.Get(i);               
              Id.SetRef (DefModule.GetClass(Id.Data));
              IF Id.GetRef = VOID THEN
                Id.Error ("Cannot find class:" + Id.Data);
                END;
              END;
            END;
          END;
        END;
      END Tag;

    METHOD EnterImportedSymbols;
      VAR
        DefModule: DefinitionModule;
        Cl: Declaration;
        Id: Ident;
      BEGIN
      DefModule := Module;
      IF DefModule <> VOID THEN
        IF NOT Qualified THEN
          IF DefModule.Canonical THEN
            Cl := TheDefModule.GetClass (TheModuleName.Data);
            ASSERT Cl <> VOID;
            SymbolTable.Enter (TheModuleName.Data, Cl);
           ELSE
            SymbolTable.Enter (TheModuleName.Data, DefModule);
            END;
         ELSE
          ASSERT TheIdentList.Size > 0;
          FOR i := 0 TO TheIdentList.Size - 1 DO
            Id := TheIdentList.Get(i);
            Cl := DefModule.GetClass (Id.Data);
            IF Cl <> VOID THEN
              SymbolTable.Enter (Id.Data, Cl);
              END;
            END;
          END;
        END;
      END EnterImportedSymbols;

    METHOD TagImportDef;
      VAR
        DefModule: DefinitionModule;
        Cl: Declaration;
        Id: Ident;
      BEGIN
      IF NOT YaflCfg.Interrupted THEN
        DefModule := ModuleTable.LocateDefinitionModule (TheModuleName.Data);
        IF DefModule <> VOID THEN
          IF NOT Qualified THEN
            IF DefModule.Canonical THEN
              ASSERT TheModuleName.Data = DefModule.UniqueClass.Id.Data;
              SymbolTable.Change (TheModuleName.Data, DefModule.UniqueClass);
             ELSE
              SymbolTable.Change (TheModuleName.Data, DefModule);
              END;
           ELSE
            FOR i := 0 TO TheIdentList.Size - 1 DO
              Id := TheIdentList.Get(i);
              Cl := DefModule.GetClass (Id.Data);
              IF Cl <> VOID THEN
                SymbolTable.Change (Id.Data, Cl);
                END;
              END;
            END;
          END;
        END; -- IF
      END TagImportDef;

    METHOD CheckTypeImportedModule;
      BEGIN
      IF Module <> VOID THEN
        Module.UniqueCheckType;
        END;
      END CheckTypeImportedModule;
      
    REDEFINE METHOD Id: Ident;
      BEGIN
      RESULT:= TheModuleName;
      END Id;

    METHOD ClassList: IdentList;
      BEGIN
      RESULT := TheIdentList;
      END ClassList;

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

    REDEFINE METHOD Enter;
      VAR
        DefModule: DefinitionModule;
        Cl: ClassDeclaration;
        Id: Ident;
      BEGIN
      DefModule := Module;
      IF DefModule <> VOID THEN
        IF NOT Qualified THEN
          ------------------------------
          -- The Canonical method returns TRUE if the Definition module
          -- it is being applied to contains a single class with the same name
          -- as the module.
          -- If it is the case, the class should be entered directly
          -- in the symbol table.
          ------------------------------
          IF DefModule.Canonical THEN
            SymbolTable.Enter (DefModule.Id.Data, DefModule.UniqueClass);
           ELSE
            SymbolTable.Enter (DefModule.Id.Data, DefModule);
            END;
         ELSE  
          FOR i := 0 TO TheIdentList.Size - 1 DO
            Id := TheIdentList.Get(i);
            Cl := DefModule.GetClass (Id.Data);
            IF Cl <> VOID THEN
              Cl.Enter;
             ELSE
              Error ('Imported class not found: ' + Id.Data);
              END;
            END;
          END;
        END;
      END Enter;

  END ImportClause;

----------------------------------------
  CLASS ImportList;
    INHERITS NonTerminal(DummyNTCodeGenerator);
    
    VAR
      TheList: DeclList(ImportClause);
      TheSet: ModuleSet;
    
    REDEFINE METHOD Parse(Lkh: LookAhead);
      VAR
        Imp: ImportClause;
      BEGIN
      IF NOT YaflCfg.Interrupted THEN
        TheSet := VOID;
        WHILE (Lkh.Ok) AND 
              ((Lkh.CurrentToken = Lkh.Import) OR 
               (Lkh.CurrentToken = Lkh.From)) DO
          Imp := Lkh.AcceptImportClause;
          SetSon (Imp);
          TheList.Append (Imp);
          END; -- WHILE
        END; -- IF  
      END Parse;
      
    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT := TheList.SubTree;
      END SubTree;
      
    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "ImportList";
      END WhatAmI;
      
    REDEFINE METHOD CREATE (LineNr, ColNr: INTEGER);
      BEGIN
      TheList.CREATE;
      BASE(LineNr, ColNr);
      END CREATE;
      
    METHOD EnterImportedSymbols;
      BEGIN
      FOR Ind := 0 TO TheList.Size-1 DO
        TheList.Get(Ind).EnterImportedSymbols;
        END;
      END EnterImportedSymbols;
    
    METHOD Enter;
      BEGIN
      TheList.Enter;
      END Enter;
            
    METHOD BuildSet: ModuleSet;
      BEGIN
      IF TheSet = VOID THEN
        TheSet.CREATE;
        FOR i := 0 TO TheList.Size - 1 DO
          TheSet.Add (TheList.Get(i).Module);
          END;
        END;
      RESULT := TheSet;
      END BuildSet;
      
    METHOD ErrorFound: BOOLEAN;
      BEGIN
      FOR i := 0 TO TheList.Size - 1 WHILE NOT RESULT DO
        RESULT := TheList.Get(i).ErrorFound;
        END;
      END ErrorFound;
      
    METHOD OffendingImport: ImportClause;
      BEGIN 
      FOR i := 0 TO TheList.Size -1 WHILE RESULT = VOID DO
        IF TheList.Get (i).ErrorFound THEN
          RESULT := TheList.Get (i);
          END; -- IF
        END; -- FOR  
      END OffendingImport;
        
    METHOD TagImportDef;
      BEGIN            
      IF NOT YaflCfg.Interrupted THEN
        FOR i := 0 TO TheList.Size - 1 WHILE NOT YaflCfg.Interrupted DO
          TheList.Get(i).TagImportDef;
          END; -- FOR
        END; -- IF  
      END TagImportDef;
      
    METHOD CheckTypeImportedModules;
      BEGIN
      FOR i := 0 TO TheList.Size - 1 DO
        TheList.Get(i).CheckTypeImportedModule;
        END;
      END CheckTypeImportedModules;
      
    METHOD GetList: DeclList (ImportClause);
      BEGIN
      RESULT := TheList;
      END GetList;
      
    REDEFINE METHOD Tag;
      BEGIN            
      IF NOT YaflCfg.Interrupted THEN
        TheList.UniqueTag;
        END; -- IF
      END Tag;
      
    METHOD TransitiveClosure: ModuleSet;
    
      METHOD HandleSet (Res, Adding: ModuleSet);
        VAR
          Flags: ARRAY OF BOOLEAN;
          ToAdd: ARRAY OF DefinitionModule;
          BEGIN
        ASSERT Res <> VOID;
        ASSERT Adding <> VOID;
        ToAdd := Adding.Row;
        IF ToAdd <> VOID THEN
          Flags.CREATE (ToAdd.SIZE);
          FOR i := 0 TO ToAdd.SIZE - 1 DO
            IF ToAdd[i] <> VOID THEN
              IF NOT Res.Includes(ToAdd[i]) THEN
                Res.Add(ToAdd[i]);
                Flags[i] := TRUE;
                ASSERT Res.Includes(ToAdd[i]);
                END;
              END;
            END;
          FOR i := 0 TO ToAdd.SIZE -1 DO
            IF Flags[i] THEN
              HandleSet (Res, ToAdd[i].Imported);
              END;
            END;
          END;
        END HandleSet;
  
      BEGIN
      RESULT.CREATE;
      HandleSet (RESULT, BuildSet);
      END TransitiveClosure;
     
  END ImportList;

END YaflImport;
