IMPLEMENTATION MODULE YaflSymbols;

FROM YaflParser IMPORT NonTerminal;
FROM YaflPredefined IMPORT PredefItems;
FROM YaflError IMPORT MainErrorHandler;
FROM YaflModules IMPORT DefinitionModule, ImplementationModule;
IMPORT Space;
FROM StringSpace IMPORT StringElement, StringHashSpace;
FROM Streams IMPORT StdOut;

  CLASS Symbol;
    INHERITS StringElement;

    VAR
      Info: NonTerminal;

    REDEFINE METHOD CREATE(NName: ARRAY OF CHAR;
                           IInfo: NonTerminal);
      BEGIN
      BASE(NName);
      Info := IInfo;
      END CREATE;

    METHOD GetInfo: NonTerminal;
      BEGIN
      RESULT := Info;
      END GetInfo;

    METHOD SetInfo (IInfo: NonTerminal);
      BEGIN
      Info := IInfo;
      END SetInfo;

    METHOD Dump;
      BEGIN
      StdOut.WriteLine ("    " + Text + " " + Info.WhatAmI);
      END Dump;

  END Symbol;
-------------------------------------------------------
  CLASS SymbolLayer;
    INHERITS StringHashSpace (Symbol);

    CONST
      InitialSize = 64;
   
    REDEFINE METHOD NewElement (Text: ARRAY OF CHAR): Symbol;
      BEGIN
      ASSERT FALSE;
      END NewElement;

    REDEFINE METHOD CREATE;
      BEGIN
      BASE (InitialSize);
      END CREATE;

    METHOD Dump;
      VAR
        p: ARRAY OF Symbol;
        BEGIN
      p := Row;
      IF p <> VOID THEN
        FOR i := 0 TO p.SIZE - 1 DO
          p[i].Dump;
          END;
        END;
      END Dump;

    REDEFINE METHOD StoreElement (StringEl: Symbol);
      BEGIN
      IF AllocatedSize < 2 * Stored THEN
        --DEBUG
        --  StdOut.WriteString ("Reallocating: ");
        --  StdOut.WriteInt (AllocatedSize , 8);
        --  StdOut.WriteInt (Stored, 8);
        --  StdOut.WriteInt (Collisions, 8);
        --  END;
        Resize (AllocatedSize * 2);
        --DEBUG
        --  StdOut.WriteString (" After: ");
        --  StdOut.WriteInt (AllocatedSize , 8);
        --  StdOut.WriteInt (Stored, 8);
        --  StdOut.WriteInt (Collisions, 8);
        --  StdOut.WriteLn;
        --  END;
        END;
      BASE (StringEl);
      END StoreElement;

  END SymbolLayer;
-------------------------------------------------------
  ONCE CLASS SymbolTable;
    CONST
      MaxLayer = 20;
    VAR
      Layer: ARRAY OF SymbolLayer;
      TopLayer: SymbolLayer;
      Sp: INTEGER;

    REDEFINE METHOD CREATE;
      BEGIN
      Layer.CREATE (MaxLayer);
      PushLevel;
      END CREATE;

    METHOD Zap;
      BEGIN
      Sp := 0;
      ASSERT Layer <> VOID;
      FOR i := 0 TO Layer.SIZE - 1 DO
        IF Layer[i] <> VOID THEN
          Layer[i].Purge;
          END;
        END;
      TopLayer := VOID;
      PushLevel;
      END Zap;

    METHOD PushLevel;
      BEGIN
      IF Layer[Sp] = VOID THEN
        Layer[Sp].CREATE;
       ELSE
        Layer[Sp].Purge;
        END;
      TopLayer := Layer[Sp];
      Sp := Sp + 1;
      END PushLevel;

    METHOD PopLevel;
      BEGIN
      ASSERT Sp > 0;
      Sp := Sp - 1;
      IF Sp > 0 THEN
        TopLayer := Layer[Sp-1];
       ELSE
        TopLayer := VOID;
        END;
      END PopLevel;

   METHOD LocalFindSymbol (Name: ARRAY OF CHAR): Symbol;
     VAR
       p: ARRAY OF CHAR;
     BEGIN
     FOR i := Sp-1 TO 0 BY -1 WHILE (RESULT = VOID) DO
       RESULT := Layer[i].FindStringElement (Name);
       END;
     IF RESULT = VOID THEN
       p := Space.StoreString (Name);
       IF p <> Name THEN
         RESULT := LocalFindSymbol (p);
         END;
       END;
     END LocalFindSymbol;

   METHOD Conflict (Name: ARRAY OF CHAR): BOOLEAN;
     BEGIN
     RESULT := (TopLayer.FindStringElement(Name) <> VOID);
     END Conflict;

  METHOD Enter (Name: ARRAY OF CHAR;
                Info: NonTerminal);
    VAR
      Sym: Symbol;
    BEGIN
    Sym := TopLayer.FindStringElement(Name);
    IF (Sym <> VOID) THEN
      ASSERT Sym.Text = Name;
      IF (Sym.GetInfo <> Info) THEN
        Info.Error ("Multiply defined symbol: " + Name);
        DEBUG
          Dump;
          END;
        END;
     ELSE
      Sym.CREATE (Name, Info);
      TopLayer.StoreElement (Sym);
      END;
    END Enter;

   METHOD Change (Name: ARRAY OF CHAR;
                  Info: NonTerminal);
     VAR
       Sym: Symbol;
     BEGIN
     Sym := TopLayer.FindStringElement (Name);
     IF Sym <> VOID THEN
       Sym.SetInfo (Info);
      ELSE
       Sym.CREATE (Name, Info);
       TopLayer.StoreElement(Sym);
       END;
     END Change;

  METHOD Find(Name: ARRAY OF CHAR): NonTerminal;
    VAR
      Sym: Symbol;
    BEGIN
    ASSERT Name <> VOID;
    Sym := LocalFindSymbol (Name);
    IF Sym <> VOID THEN
      RESULT := Sym.GetInfo;
      ASSERT RESULT <> VOID;
      END;
    END Find;

  METHOD Dump; 
    BEGIN
    FOR i := 0 TO Sp -1 DO
      StdOut.WriteString ("Level: ");
      StdOut.WriteInt (i, 0);
      StdOut.WriteLine (" ----------------");
      Layer[i].Dump;
      END;
    END Dump;

    METHOD Size: INTEGER;
      BEGIN
      FOR i := 0 TO Sp - 1 DO
        RESULT := RESULT + Layer[i].Stored;
        END;
      END Size;

    METHOD Check;
      BEGIN
      IF Size = 0 THEN
        Zap;
        PredefItems.Reset;
        PredefItems.EnterInSymbolTable;
        PushLevel;
        END;
      END Check;

  END SymbolTable;

END YaflSymbols;
