IMPLEMENTATION MODULE YaflLiteral;

IMPORT SYSTEM;
IMPORT Space;
IMPORT String;

FROM YaflCreator    IMPORT Creators;
FROM YaflSymbols    IMPORT SymbolTable;
FROM YaflLex        IMPORT LexicalAnalyzer;
FROM YaflPredefined IMPORT PredefItems;
FROM Streams        IMPORT StdOut;

  CLASS Literal(gc IN LiteralCodeGenerator);
    INHERITS TypedNonTerminal(gc);

    REDEFINE METHOD Tag;
      BEGIN
      SetFolded (THIS);
      END Tag;

    ----------------------------------
    -- The UsesValueStack attached to random literals
    -- returns FALSE. It will be redefined differently
    -- by the StringLiteral derived class.
    ----------------------------------
    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := FALSE;
      END UsesValueStack;

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

  END Literal;
----------------------------------------
  CLASS DiscreteLiteral(gc IN LiteralCodeGenerator);
    INHERITS Literal(gc);
    
    REDEFINE METHOD IsDefaultValue: BOOLEAN;
      BEGIN
      RESULT := Ordinal = 0;
      END IsDefaultValue;

    REDEFINE METHOD Isomorph(Other: TypedNonTerminal): BOOLEAN;
      BEGIN
      ---------------------------
      -- Since this method will only be called
      -- in isomorphic situations, the attached 
      -- typed must have been matched earlier.
      -- Accordingly, there is no need to test the
      -- type here; it is sufficient to make sure the
      -- two ordinal values match.
      ---------------------------
      WHAT Other OF
        IN DiscreteLiteral:
          RESULT := Ordinal = TAG.Ordinal;
          END;
       ELSE
        -- Don't abort, just let THIS and Other be non-isomorphic.   
        END;
      END Isomorph;
      
  END DiscreteLiteral;
----------------------------------------
  CLASS IntegerLiteral;
    INHERITS DiscreteLiteral(IntLiteralCodeGenerator);
    VAR
      Val: INTEGER;

    REDEFINE METHOD CREATE(LineNr, ColNr, Context, Value: INTEGER);
      BEGIN
      BASE(LineNr, ColNr, Context);
      Val := Value;
      END CREATE;

    METHOD Value: INTEGER;
      BEGIN
      RESULT := Val;
      END Value;

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

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

    REDEFINE METHOD BuildType: Type;
      BEGIN
      RESULT := PredefItems.Integer.MakeType(0);
      END BuildType;

    REDEFINE METHOD Fold (Op: INTEGER;
                          Other: Literal): Literal;
      VAR
        OtherInt: IntegerLiteral;
        
        METHOD NewInt(Val: INTEGER): IntegerLiteral;
          BEGIN
          RESULT := Creators.Object.CreateIntegerLiteral (LineNr, ColNr,
                                                          GetContext, Val);
          END NewInt;
          
      BEGIN
      IF Other <> VOID THEN
        WHAT Other OF
          IN IntegerLiteral:
            OtherInt := TAG;
            END;
          END;
        END;
      CASE Op OF
        LexicalAnalyzer.Plus:
          RESULT := NewInt (Val + OtherInt.Val);
          END;
        LexicalAnalyzer.Star:
          RESULT := NewInt (Val * OtherInt.Val);
          END;
        LexicalAnalyzer.Slash:
--          IF OtherInt.Val = 0 THEN
--            Error ("Division by 0");
--           ELSE
--            IntResult.CREATE (LineNr, ColNr, Val DIV OtherInt.Val);
--            END;
          END;
        LexicalAnalyzer.Mod:
          RESULT := NewInt (Val MOD OtherInt.Val);
          END;
        LexicalAnalyzer.Minus:
          IF OtherInt = VOID THEN
            RESULT := NewInt (-Val);
           ELSE             
            RESULT := NewInt (Val - OtherInt.Val);
            END;
          END;
        LexicalAnalyzer.Equal:
          RESULT := PredefItems.GetBoolean (Val = OtherInt.Val);
          END;
        LexicalAnalyzer.Smaller:
          RESULT := PredefItems.GetBoolean (Val < OtherInt.Val);
          END;
        LexicalAnalyzer.Greater:
          RESULT := PredefItems.GetBoolean (Val > OtherInt.Val);
          END;
        LexicalAnalyzer.SmallerEqual:
          RESULT := PredefItems.GetBoolean (Val <= OtherInt.Val);
          END;
        LexicalAnalyzer.GreaterEqual:
          RESULT := PredefItems.GetBoolean (Val >= OtherInt.Val);
          END;
        LexicalAnalyzer.NonEqual:
          RESULT := PredefItems.GetBoolean (Val <> OtherInt.Val);
          END;
        END;
      END Fold;

    -----------------------------
    -- The code generation of an integer
    -- literal casts the result to an yint,
    -- in case we use a prototypeless K&R
    -- compiler; and if yint do not match int's
    -- exactly.
    -----------------------------
    REDEFINE METHOD Ordinal: INTEGER;
      BEGIN
      RESULT := Val;
      END Ordinal;


  END IntegerLiteral;
----------------------------------------
  CLASS RealLiteral;
    INHERITS Literal(RealLiteralCodeGenerator);
    VAR
      Val: REAL;

    REDEFINE METHOD CREATE(LineNr, ColNr, Context: INTEGER;
                           Value: REAL);
      BEGIN
      BASE (LineNr, ColNr, Context);
      Val := Value;
      END CREATE;

    METHOD Value: REAL;
      BEGIN
      RESULT := Val;
      END Value;

    REDEFINE METHOD IsDefaultValue: BOOLEAN;
      BEGIN
      RESULT := Val = 0.0;
      END IsDefaultValue;

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

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

    REDEFINE METHOD BuildType: Type;
      BEGIN
      RESULT := PredefItems.Real.MakeType(0);
      END BuildType;

    REDEFINE METHOD Fold (Op: INTEGER;
                          Other: Literal): Literal;
      VAR
        OtherReal: RealLiteral;
        
        METHOD NewReal(Val: REAL): RealLiteral;
          BEGIN
          RESULT := Creators.Object.CreateRealLiteral (LineNr, ColNr,
                                                       GetContext, Val);
          END NewReal;
          
      BEGIN
      IF Other <> VOID THEN
        WHAT Other OF
          IN RealLiteral:
            OtherReal := TAG;
            END;
          END;
        END;
      CASE Op OF
        LexicalAnalyzer.Plus:
          RESULT := NewReal (Val + OtherReal.Val);
          END;
        LexicalAnalyzer.Star:
          RESULT := NewReal (Val * OtherReal.Val);
          END;
        LexicalAnalyzer.Slash:
          IF OtherReal.Val = 0.0 THEN
            Error ("Division by 0");
           ELSE
            RESULT := NewReal (Val / OtherReal.Val);
            END;
          END;
        LexicalAnalyzer.Minus:
          IF OtherReal = VOID THEN
            RESULT := NewReal (-Val);
           ELSE
            RESULT := NewReal (Val - OtherReal.Val);
            END;
          END;
        LexicalAnalyzer.Equal:
          RESULT := PredefItems.GetBoolean (Val = OtherReal.Val);
          END;
        LexicalAnalyzer.Smaller:
          RESULT := PredefItems.GetBoolean (Val < OtherReal.Val);
          END;
        LexicalAnalyzer.Greater:
          RESULT := PredefItems.GetBoolean (Val > OtherReal.Val);
          END;
        LexicalAnalyzer.SmallerEqual:
          RESULT := PredefItems.GetBoolean (Val <= OtherReal.Val);
          END;
        LexicalAnalyzer.GreaterEqual:
          RESULT := PredefItems.GetBoolean (Val >= OtherReal.Val);
          END;
        LexicalAnalyzer.NonEqual:
          RESULT := PredefItems.GetBoolean (Val <> OtherReal.Val);
          END;
        END;
      END Fold;

      
    REDEFINE METHOD Isomorph (Other: TypedNonTerminal): BOOLEAN;
      BEGIN
      -- Always FALSE !!!
      END Isomorph;

  END RealLiteral;
----------------------------------------
  CLASS StringLiteral;
    INHERITS DiscreteLiteral(StringLiteralCodeGenerator);
    VAR
      Val: ARRAY OF CHAR;
      SingleChar: BOOLEAN;

    REDEFINE METHOD CREATE(LineNr, ColNr, Context: INTEGER;
                           Value: ARRAY OF CHAR);
      BEGIN
      BASE (LineNr, ColNr, Context);
      Val := Value;
      END CREATE;

    METHOD Value: ARRAY OF CHAR;
      BEGIN
      RESULT := Val;
      END Value;

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

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

    REDEFINE METHOD Fold (Op: INTEGER;
                          Other: Literal): Literal;
      VAR
        OtherString: StringLiteral;
      BEGIN
      IF Other <> VOID THEN
        WHAT Other OF
          IN StringLiteral:
            OtherString := TAG;
            END;
          END;
        END;
      IF Op = LexicalAnalyzer.Plus THEN
        RESULT := Creators.Object.CreateStringLiteral (LineNr, ColNr,
                                                       GetContext,
                                                       Val + OtherString.Val);
        END;
      END Fold;

    REDEFINE METHOD GetType: Type;
      BEGIN
      IF SingleChar THEN
        RESULT := PredefItems.Char.MakeType (0);
       ELSE
        RESULT := PredefItems.Char.MakeType (1);
        END;
      END GetType;

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

    REDEFINE METHOD ExprCompatible (TheType: Type): BOOLEAN;
      BEGIN
      IF BASE (TheType) THEN
        RESULT := TRUE;
       ELSIF TheType.Match (PredefItems.Char.MakeType(0))
              AND (Val.SIZE = 1) THEN
        RESULT := TRUE;
        SingleChar := TRUE;
        END;
      END ExprCompatible;

    REDEFINE METHOD ExprMatch (TheType: Type): BOOLEAN;
      BEGIN
      RESULT := ExprCompatible (TheType);
      END ExprMatch;

    REDEFINE METHOD Ordinal : INTEGER;
      BEGIN
      ASSERT SingleChar;
      RESULT := SYSTEM.ORD(Val[0]);
      END Ordinal;
      
    REDEFINE METHOD Isomorph (Other: TypedNonTerminal): BOOLEAN;
      BEGIN
      WHAT Other OF
        IN StringLiteral:
          RESULT := String.Equals (Val, TAG.Val);
          END;
       ELSE
        -- Don't abort, of course...
        END;
      END Isomorph;
      
    REDEFINE METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := NOT SingleChar;
      END UsesValueStack;
      
    METHOD IsSingleChar: BOOLEAN;
      BEGIN
      RESULT := SingleChar;
      END IsSingleChar;  

  END StringLiteral;

  ---------------------------------------
  CLASS BooleanLiteral;
    INHERITS DiscreteLiteral(BooleanLiteralCodeGenerator);
    VAR
      TheValue: BOOLEAN;
      TheName: ARRAY OF CHAR;

    REDEFINE METHOD GetType: Type;
      BEGIN
      RESULT := PredefItems.Boolean.MakeType (0);
      END GetType;
      
    REDEFINE METHOD CREATE (Name: ARRAY OF CHAR;
                            Context: INTEGER;
                            Value: BOOLEAN);
      BEGIN
      TheName := Space.StoreString (Name);
      BASE (0, 0, Context);
      TheValue := Value;
      END CREATE;

    REDEFINE METHOD IsDefaultValue: BOOLEAN;
      BEGIN
      RESULT := NOT TheValue;
      END IsDefaultValue;

    METHOD Value: BOOLEAN;
      BEGIN
      RESULT := TheValue;
      END Value;

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

    REDEFINE METHOD Fold (Op: INTEGER;
                          Other: Literal): Literal;
      VAR
        OtherBoolean: BooleanLiteral;
      BEGIN
      ASSERT Other=VOID IFF Op=LexicalAnalyzer.Not;
      IF Other <> VOID THEN
        WHAT Other OF
          IN BooleanLiteral:
            OtherBoolean := TAG;
            END;
          END;
        END;
      CASE Op OF
        LexicalAnalyzer.And:
          RESULT := PredefItems.GetBoolean (Value AND OtherBoolean.Value);
          END;
        LexicalAnalyzer.Or:
          RESULT := PredefItems.GetBoolean (Value OR OtherBoolean.Value);
          END;
        LexicalAnalyzer.Not:
          RESULT := PredefItems.GetBoolean (NOT Value);
          END;
        LexicalAnalyzer.Iff:
          RESULT := PredefItems.GetBoolean (Value IFF OtherBoolean.Value);
          END;
        LexicalAnalyzer.Equal,
        LexicalAnalyzer.NonEqual:
          ASSERT FALSE;
          END;
        LexicalAnalyzer.Implies:
          RESULT := PredefItems.GetBoolean (NOT Value 
                                            OR OtherBoolean.Value);  
          END;
        END;
      END Fold;

    REDEFINE METHOD Ordinal: INTEGER;
      BEGIN
      IF TheValue THEN
        RESULT := 1;
       ELSE
        RESULT := 0;
        END;
      END Ordinal;

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

  END BooleanLiteral;

END YaflLiteral;
