IMPLEMENTATION MODULE YaflPredefined;

FROM YaflCfg IMPORT CurrentSpot;
FROM YaflClasses IMPORT ClassDeclaration;
FROM YaflClasses IMPORT VirtualClassDecl, ConstrainedClassDecl;
FROM YaflDeclarations IMPORT InheritsClause;
FROM YaflExpressions IMPORT Actual, ActualList;
FROM YaflParamClasses IMPORT ClassActualSet;
IMPORT Space;
FROM YaflSymbols IMPORT SymbolTable;
IMPORT LookAhead;


------------------------------------------
  CLASS PredefClass;
    INHERITS ClassDefinition(PredefClassCodeGenerator);

    VAR
      TheId: Ident;
      TheShortCName: ARRAY OF CHAR;
      TheCName: ARRAY OF CHAR;

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

    REDEFINE METHOD Id: Ident;
      BEGIN
      RESULT := TheId;
      END Id;

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

    METHOD CName: ARRAY OF CHAR;
      BEGIN
      RESULT := TheCName;
      END CName;

    REDEFINE METHOD CREATE (YaflName,
                            CName,
                            ShortCName: ARRAY OF CHAR);
      BEGIN
      BASE (0, 0);
      TheCName := CName;
      TheId.CREATE (0, 0, Space.StoreString (YaflName));
      TheShortCName := ShortCName;
      END CREATE;
      
    METHOD ShortCName: ARRAY OF CHAR;
      BEGIN
      RESULT := TheShortCName;
      END ShortCName;
      
    METHOD EnterInSymbolTable;
      BEGIN
      SymbolTable.Enter (TheId.Data, THIS);
      END EnterInSymbolTable;
      
  END PredefClass;
----------------------------------
  CLASS PredefMethod;
    INHERITS MethodDeclaration(PredefMethCodeGenerator);
    VAR
      TheId: Ident;

    REDEFINE METHOD CREATE (Name: ARRAY OF CHAR);
      BEGIN
      BASE (0, 0);
      TheId.CREATE (0, 0, Space.StoreString(Name));
      TheId.SetRef (THIS);
      END CREATE;

    REDEFINE METHOD Id: Ident;
      BEGIN
      RESULT := TheId;
      END Id;

    METHOD EnterInSymbolTable;
      BEGIN
      SymbolTable.Enter (TheId.Data, THIS);
      END EnterInSymbolTable;

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

  END PredefMethod;
----------------------------------
  CLASS PredefCreateMethod;
    INHERITS PredefMethod;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("CREATE");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
      VAR
        Ctx: Type;
      BEGIN
      IF Previous = VOID THEN
        Current.Error ("Nothing to CREATE");
       ELSE
        ------------------------------
        -- The left part must be an
        -- acceptable LValue
        ------------------------------
        IF Previous.LValue THEN
          Ctx := Previous.GetType;
          ASSERT Ctx <> VOID ;
          IF NOT Ctx.UseObjPtr THEN
            Current.Error ("CREATE applied to a predefined type");
           ELSIF Ctx.ArrayLevel > 0 THEN
            -------------------------
            -- Array creation: there
            -- must be a single integer
            -- parameter.
            -------------------------
            IF Current.Actuals = VOID THEN
              Current.Error ("Integer argument expected");
             ELSE
              VOID := Current.Actuals.CheckIntegers(1);
              END;
           ELSE
            -------------------------------------------
            -- What we are trying to CREATE is no array.
            -- First make sure that it is not a Once class.
            -------------------------------------------
            IF Ctx.SimpleType.Once THEN
              Current.Error ("Cannot CREATE a ONCE class instance");
            -------------------------------------------
            -- The object to create must be constrained
            -------------------------------------------
             ELSIF NOT Ctx.IsConstrained THEN
              Current.Error("Non constrained type object to create");
             ELSE
              WHAT Ctx.SimpleType OF
                IN VirtualClassDecl:
                  Current.Error ("Cannot CREATE an item denoted by a " +
                                 "formal class");
                  END;
               ELSE
                -- Do Nothing, just testing...
                END;
              END;
            Current.CheckParameters (Ctx.SimpleType.Create, 
                                     Ctx.ConstrainedClass);
            END;
         ELSE
          Current.Error ("Left part of CREATE is not an acceptable LValue");
          END;
        END;
      RESULT := VOID; -- Redundant, but more explicit.        
      END BuildType;
      
  END PredefCreateMethod;
---------------------------------------
  CLASS PredefCloneMethod;
    INHERITS PredefMethod;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("CLONE");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
      BEGIN
      IF Previous = VOID THEN
        Current.Error ("Nothing to CLONE");
       ELSE
        RESULT := Previous.GetType;
        IF Current.Actuals <> VOID THEN
          Current.Error ("CLONE must be used without parameters ");
          END;
        RESULT := RESULT.MatchBrackets (Current.BrExpr);
        END;
      END BuildType;
      
  END PredefCloneMethod;
---------------------------------------
  CLASS PredefKillMethod;
    INHERITS PredefMethod;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("KILL");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
      BEGIN
      IF Current.Actuals <> VOID THEN
        Current.Error ("KILL must be used without parameters ");
       ELSIF Current.BrExpr <> VOID THEN
        Error ("Not an array");
        END;
      RESULT := VOID; -- Redundant, but more explicit.        
      END BuildType;
      
  END PredefKillMethod;
---------------------------------------
  CLASS PredefSliceMethod;
    INHERITS PredefMethod;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("SLICE");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
      VAR
        Ctx: Type;
      BEGIN
      ------------------------------------
      -- SLICE must be used with
      -- an array. The previous element must exist;
      ------------------------------------
      ASSERT Previous <> VOID;
      Ctx := Previous.GetType;
      IF Ctx = VOID THEN
        Error ("Wrong context");
       ELSE
        IF Ctx.ArrayLevel > 0 THEN
          ------------------------------------------------
          -- Make sure that both parameters are integers
          -- if we use SLICE, and that there are no params
          -- if we use SIZE
          ------------------------------------------------
          RESULT := Ctx;
          IF Current.Actuals = VOID THEN
            Error ("Parameter expected");
           ELSE
            VOID := Current.Actuals.CheckIntegers(2);
            END;
         ELSE
          Error ("SLICE must be applied to an array");
          END;
        END;  
      END BuildType;
      
  END PredefSliceMethod;
---------------------------------------
  CLASS PredefSizeMethod;
    INHERITS PredefMethod;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("SIZE");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
      VAR
        Ctx: Type;
      BEGIN
      ------------------------------------
      -- SIZE must be used with
      -- an array. The previous element must exist;
      ------------------------------------
      ASSERT Previous <> VOID;
      Ctx := Previous.GetType;
      IF Ctx = VOID THEN
        Previous.Error ("Wrong context");
       ELSE
        IF Ctx.ArrayLevel > 0 THEN
          ------------------------------------------------
          -- Make sure that there are no params
          ------------------------------------------------
          RESULT := PredefItems.Integer.MakeType(0);
          IF (Current.Actuals <> VOID) OR (Current.BrExpr <> VOID) THEN
            Previous.Error ("SIZE must be used without parameter");
           ELSIF Current.Id.Data <> Id.Data THEN
            Current.Warning ("HIGH is obsolete, SIZE should be used instead");
            END;
         ELSE
          Previous.Error ("SIZE must be applied to an array");
          END;
        END;  
      END BuildType;
      
  END PredefSizeMethod;
---------------------------------------
  CLASS PredefBaseMethod;
    INHERITS PredefMethod;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("BASE");
      END CREATE;
     
    METHOD BuildCloneType (Current: DesigElement;
                           TheActuals: ActualList);
      VAR
        Cl: ClassDeclaration;
      BEGIN
      -----------------------------------------
      -- Redefining CLONE -> special handling
      -----------------------------------------
      Cl := CurrentSpot.CurrentClass;
      IF Cl.Inherits <> VOID THEN
        Cl := Cl.Inherits.Class;
        END;
      ---------------------------------------
      -- Check the parameter:
      -- There must only one, and it must be
      -- compatible with Cl.
      ---------------------------------------
      IF (TheActuals = VOID) OR (TheActuals.Size <> 1) THEN
        Current.Error ('Arity error');
       ELSE
        IF NOT TheActuals.Get(0).Expr.ExprCompatible (Cl.MakeType(0)) THEN
          Current.Error ('Type mismatch');
          END;
        END;
      END BuildCloneType;
          
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
      VAR
        PrevMeth, CurMeth: MethodDeclaration;
        Ctx: ConstrainedClassDecl;
        InheritsCl: InheritsClause;      
        TheActuals: ActualList;
      BEGIN
      TheActuals := Current.Actuals;      
      CurMeth := CurrentSpot.CurrentMethod;
      IF CurMeth.Id.Data = PredefItems.Clone.Id.Data THEN
        BuildCloneType (Current, TheActuals);
       ELSE
        PrevMeth := CurMeth.Redefined (FALSE);
        IF PrevMeth = VOID THEN
          ----------------------------------------------
          -- Can only be valid for CREATE. The default,
          -- somehow non-existing CREATE must be used
          -- parameterless.               
          -- If another method than CREATE is used,
          -- we'll rely on previous error detection,
          -- and continue without further notice.
          ----------------------------------------------
          IF CurMeth.Id.Data = PredefItems.Create.Id.Data THEN
            IF TheActuals <> VOID THEN
              TheActuals.Father.Error ("Unexpected parameters");
              END;
            END;
         ELSE
          -------------------------------------------------
          -- check parameters against the original
          -- method's definition. This unified scheme
          -- will handle both isomorphic and non-isomorphic
          -- redefinitions (such as CREATE)
          --------------------------------------------------
          InheritsCl := CurrentSpot.CurrentClass.Canonic.Inherits;
          --------------------------------------------------
          -- Except for a very unlikely error situation,
          -- the inherits clause is defined. In order to 
          -- make the whole system more robust, this error
          -- situation is tested.
          --------------------------------------------------
          IF InheritsCl <> VOID THEN
            Ctx := InheritsCl.Class.Canonic.
                              MakeConstrainedClass(InheritsCl.Actuals);
            Current.CheckParameters (PrevMeth, Ctx);     
            RESULT := PrevMeth.Return;
            IF RESULT <> VOID THEN
              RESULT := RESULT.BuildContextual (Ctx);
              END;
           ELSE
            Current.Error ("Cannot check BASE type");
            END;
          END;
        END;
      END BuildType;
      
  END PredefBaseMethod;
  
----------------------------------------------------------------  

  CLASS PredefNoChangeMethod;
    INHERITS PredefMethod;
    
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("NO_CHANGE");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
     BEGIN
     RESULT := PredefItems.Boolean.MakeType(0);
     END BuildType;
      
  END PredefNoChangeMethod;
----------------------------------------------------------------  

  CLASS PredefNoChangeHCMethod;
    INHERITS PredefMethod;
    
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("NO_CHANGE_HC");
      END CREATE;
      
    REDEFINE METHOD BuildType (Current, Previous: DesigElement): Type;
     BEGIN
     RESULT := PredefItems.Boolean.MakeType(0);
     END BuildType;
      
  END PredefNoChangeHCMethod;

---------------------------------------
  CLASS PredefDataItem;
    INHERITS SingleDataItem;

    VAR
      TheId: Ident;
             
    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "PredefDataItem";
      END WhatAmI;
      
    REDEFINE METHOD CREATE (Name: ARRAY OF CHAR;
                            ReadOnly: BOOLEAN);
      BEGIN
      TheId.CREATE (0, 0, Space.StoreString(Name));
      BASE (TheId, VOID, ReadOnly, FALSE);
      END CREATE;

    REDEFINE METHOD Id: Ident;
      BEGIN
      RESULT := TheId;
      END Id;
      
    REDEFINE METHOD Context: MethodDeclaration;
      BEGIN
      RESULT := CurrentSpot.CurrentMethod;
      END Context; 
      
    METHOD Reset;
      BEGIN     
      SetType (VOID);
      TheId.SetRef (THIS);
      END Reset;

  END PredefDataItem;
---------------------------------------
  CLASS ThisDataItem;
    INHERITS PredefDataItem;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("THIS", ReadOnly := TRUE);
      END CREATE;
      
  END ThisDataItem;
---------------------------------------
  CLASS ResultDataItem;
    INHERITS PredefDataItem;

    VAR
      TheMeth: MethodDeclaration;
    
    REDEFINE METHOD CREATE (Meth: MethodDeclaration);
      BEGIN
      BASE ("RESULT", ReadOnly := FALSE);
      TheMeth := Meth;
      END CREATE;
      
    METHOD EnclosingMethod: MethodDeclaration;
      BEGIN
      RESULT := TheMeth;
      END EnclosingMethod;

  END ResultDataItem;
---------------------------------------
  CLASS TagDataItem;
    INHERITS PredefDataItem;
    
    REDEFINE METHOD CREATE;
      BEGIN
      BASE ("TAG", ReadOnly := TRUE);
      END CREATE;
      
  END TagDataItem;
---------------------------------------
  ONCE CLASS VoidType;
    INHERITS InstType;

    REDEFINE METHOD CREATE;
      BEGIN
      BASE (0, 0);
      END CREATE;

    REDEFINE METHOD SimpleType: ClassDeclaration;
      BEGIN
      ASSERT FALSE;
      END SimpleType;

    REDEFINE METHOD ArrayLevel: INTEGER;
      BEGIN
      ASSERT FALSE;
      END ArrayLevel;

    METHOD This: Type;
      BEGIN
      RESULT := THIS;
      END This;

    REDEFINE METHOD Compatible(Other: Type): BOOLEAN;
      BEGIN
      RESULT := Other.UseObjPtr;
      END Compatible;
      
    REDEFINE METHOD Image: ARRAY OF CHAR;
      BEGIN
      RESULT := "<<VOID>>";
      END Image;      
      
    REDEFINE METHOD UseObjPtr: BOOLEAN;
      BEGIN         
      RESULT := TRUE;
      END UseObjPtr;

  END VoidType;
---------------------------------------
  ONCE CLASS Void;
    INHERITS Literal(VoidLitCodeGenerator);

    VAR
      Name: ARRAY OF CHAR;

    METHOD EnterInSymbolTable;
      BEGIN
      Name := Space.StoreString ("VOID");
      SymbolTable.Enter (Name, THIS);
      END EnterInSymbolTable;

    METHOD Denotation: ARRAY OF CHAR;
      BEGIN
      RESULT := Name;
      END Denotation;

    REDEFINE METHOD CREATE;
      BEGIN
      BASE (0, 0, LookAhead.PlainExpressionContext);
      END CREATE;

   REDEFINE METHOD GetType: Type;
     BEGIN
     RESULT := VoidType.This;
     END GetType;

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

   REDEFINE METHOD IsDefaultValue: BOOLEAN;
     BEGIN
     RESULT := TRUE;
     END IsDefaultValue;

  END Void;
---------------------------------------
  ONCE CLASS PredefItems;
    VAR
      TheClone:    PredefCloneMethod;
      TheKill:     PredefKillMethod;
      TheCreate:   PredefCreateMethod;
      TheSize:     PredefSizeMethod;
      TheSlice:    PredefSliceMethod;
      TheBase:     PredefBaseMethod;
      TheNoChange: PredefNoChangeMethod;
      TheNoChangeHC: PredefNoChangeHCMethod;

      TheInteger,
      TheChar,
      TheReal,
      TheBoolean: PredefClass;
      
      HighString: ARRAY OF CHAR;

      TheThis: ThisDataItem;
      
      TheTag: TagDataItem;

      TheTrue,
      TheFalse: BooleanLiteral;
      

    METHOD EnterInSymbolTable;
      BEGIN
      TheInteger.EnterInSymbolTable;
      TheChar.EnterInSymbolTable;
      TheReal.EnterInSymbolTable;
      TheBoolean.EnterInSymbolTable;
      TheTrue.EnterInSymbolTable;
      TheFalse.EnterInSymbolTable;
      Void.EnterInSymbolTable;
      END EnterInSymbolTable;

    REDEFINE METHOD CREATE;
      BEGIN  
      HighString := Space.StoreString ("HIGH");
      TheClone.CREATE;
      TheKill.CREATE;
      TheCreate.CREATE;
      TheSize.CREATE;
      TheSlice.CREATE;
      TheBase.CREATE;
      TheNoChange.CREATE;
      TheNoChangeHC.CREATE;
      --
      TheInteger.CREATE ("INTEGER", "yint ", "INT");
      TheChar.CREATE ("CHAR", "ychar ", "CHAR");
      TheReal.CREATE ("REAL", "double ", "REAL");
      TheBoolean.CREATE ("BOOLEAN", "int ", "BOOL");
      --
      TheThis.CREATE;
      -- TheResult.CREATE;
      TheTag.CREATE;
      --
      TheTrue.CREATE ("TRUE", LookAhead.PlainExpressionContext, TRUE);
      TheFalse.CREATE ("FALSE", LookAhead.PlainExpressionContext, FALSE);
      END CREATE;  
      
    METHOD Reset;
      BEGIN     
      TheThis.Reset;
 --     TheResult.Reset;
      TheTag.Reset;
      END Reset;

    METHOD Clone: PredefCloneMethod;
      BEGIN
      RESULT := TheClone;
      END Clone;

    METHOD Kill: PredefKillMethod;
      BEGIN
      RESULT := TheKill;
      END Kill;
      
    METHOD Create: PredefCreateMethod;
      BEGIN
      RESULT := TheCreate;
      END Create;

    METHOD Size: PredefSizeMethod;
      BEGIN
      RESULT := TheSize;
      END Size;

    METHOD Slice: PredefSliceMethod;
      BEGIN
      RESULT := TheSlice;
      END Slice;

    METHOD Base: PredefBaseMethod;
      BEGIN
      RESULT := TheBase;
      END Base;
      
    METHOD NoChange: PredefNoChangeMethod;
      BEGIN        
      RESULT := TheNoChange;
      END NoChange;     

    METHOD NoChangeHC: PredefNoChangeHCMethod;
      BEGIN        
      RESULT := TheNoChangeHC;
      END NoChangeHC;     
      
----------------------------------

    METHOD Integer: PredefClass;
      BEGIN
      RESULT := TheInteger;
      END Integer;

    METHOD Char: PredefClass;
      BEGIN
      RESULT := TheChar;
      END Char;

    METHOD ArrayOfChar: Type;
      BEGIN
      RESULT := TheChar.MakeType(ArrayLevel := 1);
      ASSERT RESULT = TheChar.MakeType (1);  -- Just to make sure that it
                                             -- is only computed once.
      END ArrayOfChar;

    METHOD Real: PredefClass;
      BEGIN
      RESULT := TheReal;
      END Real;

    METHOD Boolean: PredefClass;
      BEGIN
      RESULT := TheBoolean;
      END Boolean;
----------------------------------------
    METHOD This: ThisDataItem;
      BEGIN
      RESULT := TheThis;
      END This;

    METHOD Tag: PredefDataItem;
      BEGIN
      RESULT := TheTag;
      END Tag;
-----------------------------------------
    METHOD True: BooleanLiteral;
      BEGIN
      RESULT := TheTrue;
      END True;

    METHOD False: BooleanLiteral;
      BEGIN
      RESULT := TheFalse;
      END False;

    METHOD GetBoolean (Value: BOOLEAN): BooleanLiteral;
      BEGIN
      IF Value THEN
        RESULT := TheTrue;
       ELSE
        RESULT := TheFalse;
        END;
      END GetBoolean;

------------------------------------------
    METHOD FindPredefinedMethod (Name: ARRAY OF CHAR): PredefMethod;
      BEGIN
      IF Name = TheClone.Id.Data THEN
        RESULT := TheClone;
       ELSIF Name = TheCreate.Id.Data THEN
        RESULT := TheCreate;
       ELSIF (Name = TheSize.Id.Data) OR (Name = HighString) THEN
        RESULT := TheSize;
       ELSIF Name = TheSlice.Id.Data THEN
        RESULT := TheSlice;
       ELSIF Name = TheBase.Id.Data THEN
        RESULT := TheBase;
       ELSIF Name = TheNoChange.Id.Data THEN
        RESULT := TheNoChange;
        END;
      END FindPredefinedMethod;

    METHOD IsDiscreteScalar (TheType: Type): BOOLEAN;
      BEGIN
      IF TheType.ArrayLevel = 0 THEN
        WHAT TheType.SimpleType OF
          IN PredefClass:
            RESULT := TAG <> TheReal;
            END;
         ELSE
          RESULT := FALSE;
          END;
        END;
      END IsDiscreteScalar;

      METHOD ClearGc;
        BEGIN
        TheInteger.ClearGc;
        TheChar.ClearGc;
        TheReal.ClearGc;
        TheBoolean.ClearGc;

        TheClone.ClearGc;
        TheKill.ClearGc;
        TheCreate.ClearGc;
        TheSize.ClearGc;
        TheSlice.ClearGc;
        TheBase.ClearGc;
        TheNoChange.ClearGc;

        TheThis.ClearGc;
        TheTag.ClearGc;
        TheTrue.ClearGc;
        TheFalse.ClearGc;
        END ClearGc;

    END PredefItems;

END YaflPredefined;
