IMPLEMENTATION MODULE YaflIdentifiers;

IMPORT Comparable;
FROM YaflLex          IMPORT LexicalAnalyzer;
FROM Linked           IMPORT LinkedList;
IMPORT LookAhead;
FROM YaflSymbols IMPORT SymbolTable;
FROM YaflCfg          IMPORT YaflCfg;
FROM YaflClasses      IMPORT ClassDeclaration;
FROM YaflDeclarations IMPORT SingleDataItem;
FROM YaflError        IMPORT MainErrorHandler;
FROM YaflModules      IMPORT CompilationUnit;
FROM YaflParamClasses IMPORT ClassFormal;
IMPORT String;
FROM YaflType         IMPORT Type;      
FROM YaflGCode        IMPORT DummyNTCodeGenerator;

  CLASS Ident;
    INHERITS NonTerminal(DummyNTCodeGenerator);

    VAR
      TheData: ARRAY OF CHAR;
      Ref: NonTerminal;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      IF Lkh.CurrentToken = LexicalAnalyzer.Ident THEN
        TheData := Lkh.CurrentIdent;
        Lkh.GetToken;
       ELSE
        Error ("Identifier expected");
        Lkh.Accept (LexicalAnalyzer.Ident);
        END;
      END Parse;

    REDEFINE METHOD Tag;
      VAR
        p: NonTerminal;
      BEGIN
      ASSERT TheData <> VOID;
      p := SymbolTable.Find (TheData);
      IF (p = VOID) THEN
        Error ("Undefined identifier: " + TheData);
        DEBUG
          IF YaflCfg.VerboseLevel > 0 THEN
            SymbolTable.Dump;
            ASSERT FALSE;
            END;
          END;
       ELSE
        Ref := p;
        END;
      END Tag;
            
    METHOD Data: ARRAY OF CHAR;
      BEGIN
      RESULT := TheData;
      END Data;

    METHOD SetData(Data: ARRAY OF CHAR);
      BEGIN
      TheData := Data;
      END SetData;
        
    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "Ident";
      END WhatAmI;

    METHOD SetRef (Root: NonTerminal);
      BEGIN
      Ref := Root;
      END SetRef;

    METHOD GetRef : NonTerminal;
      BEGIN
      RESULT := Ref;
      END GetRef;

    REDEFINE METHOD CREATE (LineNr, ColNr: INTEGER;
                            Data: ARRAY OF CHAR);
      BEGIN
      BASE(LineNr, ColNr);
      TheData := Data;
      END CREATE;
      
  END Ident;
----------------------------------------
  CLASS IdentList;
    INHERITS NTList(Ident);
    
    REDEFINE METHOD ShouldSwap (One, Two: Ident): BOOLEAN;
      VAR
        Code: INTEGER;
      BEGIN
      IF (One <> VOID) THEN
        IF (Two = VOID) THEN
          RESULT := TRUE;
         ELSE
          IF One.Data <> VOID THEN
            IF Two.Data = VOID THEN
              RESULT := TRUE;
             ELSE
              Code := String.Compare (One.Data, Two.Data);
              IF Code > 0 THEN
                RESULT := TRUE;
               ELSIF Code = 0 THEN
                IF One.LineNr > Two.LineNr THEN
                  RESULT := TRUE;
                 ELSIF One.LineNr = Two.LineNr THEN
                  RESULT := One.ColNr > Two.ColNr;
                  END;
                END;
              END;
            END;
          END;
        END;
      END ShouldSwap;
      
  END IdentList;
----------------------------------------
  CLASS QualIdent;
    INHERITS NonTerminal(DummyNTCodeGenerator);
    VAR
      TheFirstIdent, TheSecondIdent: Ident;

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

    REDEFINE METHOD Parse(Lkh: LookAhead);
      BEGIN
      TheFirstIdent := Lkh.AcceptIdent;
      SetSon (TheFirstIdent);
      IF Lkh.CurrentToken = LexicalAnalyzer.Dot THEN
        Lkh.GetToken;
        TheSecondIdent := Lkh.AcceptIdent;
        SetSon (TheSecondIdent);
        END;
      END Parse;

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

    METHOD FirstIdent: Ident;
      BEGIN
      RESULT := TheFirstIdent;
      END FirstIdent;
    
    METHOD SetIdent(Id: Ident);
      BEGIN
      TheFirstIdent := Id;
      TheSecondIdent := VOID;
      END SetIdent;
        
    METHOD LastIdent: Ident;
      BEGIN
      IF TheSecondIdent <> VOID THEN
        RESULT := TheSecondIdent;
       ELSE 
        RESULT := TheFirstIdent;
        END;
      END LastIdent;

    REDEFINE METHOD Tag;
      VAR
        Pos: INTEGER;
        Ref: NonTerminal;
        Class: ClassDeclaration;
        TheType: Type;      
      BEGIN
      ----------------------------------------
      -- Tag the first ident of the qualident.
      ----------------------------------------
      TheFirstIdent.UniqueTag;
      Ref := TheFirstIdent.GetRef;
      IF Ref = VOID THEN
        Error ("QualId error: " + TheFirstIdent.Data);
       ELSIF TheSecondIdent = VOID THEN
        WHAT Ref OF
          IN ClassDeclaration:
            END;
         ELSE
          Error ("Class identifier expected: " + TheFirstIdent.Data + "(" +
                  Ref.WhatAmI + ")");
          END;
       ELSE
        ASSERT TheSecondIdent <> VOID;
        -----------------------------------------------------------
        -- The case where Ref is void must have been catched before
        -----------------------------------------------------------
        WHAT Ref OF
          IN CompilationUnit:
            Class := TAG.GetClass (TheSecondIdent.Data);
            IF Class = VOID THEN
              Error ("Undefined class identifier: " + TheSecondIdent.Data);
             ELSE
              TheSecondIdent.SetRef (Class);
              END;
            END;
          IN SingleDataItem:
            ----------------------------------------------------
            -- The FirstIdent must be a ReadOnly SingleDataItem
            ----------------------------------------------------
            IF NOT TAG.ReadOnly THEN
              Error ("Must be a Formal parameter");
             ELSE 
              ------------------------------------------------
              -- The second Ident must refer to a ClassFormal.
              ------------------------------------------------
              TheType := TAG.GetType;
              Pos := TheType.SimpleType.GetFormalPos(TheSecondIdent.Data);
              IF Pos < 0 THEN
                TheSecondIdent.Error ("Undefined ClassFormal identifier");
               ELSE 
                --------------------------------------------
                -- If the Ident refers to a ClassFormal, its
                -- reference must be found from the Actuals
                -- attached to the previous identifier.
                --------------------------------------------
                ASSERT TheType.Actuals <> VOID;
                ASSERT Pos < TheType.Actuals.Size;
                TheSecondIdent.SetRef(TheType.Actuals.
                                      ActualList.Get(Pos).Class);
                END;    
              END;
            END;
         ELSE
          -------------------------------------
          -- If the first ident is not a module
          -- or a SingleDataItem
          -------------------------------------
          Error ("Module identifier expected (" + TheFirstIdent.Data + ")");
          END;  -- What Ref
        END;
      DEBUG
        IF Ref = VOID THEN
          Error ("Void ref");
          END;
        END;
      END Tag;
      
    METHOD GetRef: ClassDeclaration;
      VAR
        p: NonTerminal;
      BEGIN 
      p := LastIdent.GetRef;
      IF p <> VOID THEN
        WHAT p OF
          IN ClassDeclaration:
            RESULT := TAG;
            END;
         ELSE
          Error ("Class identifier expected");
          LastIdent.SetRef (VOID);
          END;
        END;
      END GetRef;
      
    METHOD Data: ARRAY OF CHAR;
      BEGIN
      IF TheSecondIdent <> VOID THEN
        RESULT := TheFirstIdent.Data + '.' + TheSecondIdent.Data;
       ELSE
        RESULT := TheFirstIdent.Data;
        END;
      END Data;

  END QualIdent;

END YaflIdentifiers;
