{-# LANGUAGE NoImplicitPrelude
           , PackageImports
           , UnicodeSyntax
  #-}

module Text.Numeral.Exp
    ( Exp(..)
    , eval
    , Side(L, R)
    ) where


-------------------------------------------------------------------------------
-- Imports
-------------------------------------------------------------------------------

import "base" Data.Bool ( Bool(False, True) )
import "base" Data.Eq   ( Eq )
import "base" Data.Ord  ( Ord )
import "base" Text.Show ( Show )
import "base-unicode-symbols" Prelude.Unicode (  )
import qualified "this" Text.Numeral.Exp.Classes as C


-------------------------------------------------------------------------------
-- Exp datatype
-------------------------------------------------------------------------------

-- | An expression that represents the structure of a numeral.
data Exp  -- | An unknown value.
         = Unknown
          -- | A literal value.
         | Lit 
           -- | Negation of an expression.
         | Neg Exp
           -- | Addition of two expressions.
         | Add Exp Exp
           -- | Multiplication of two expressions.
         | Mul Exp Exp
           -- | One expression subtracted from another expression.
         | Sub Exp Exp
           -- | A step in a scale of large values.
         | Scale   Exp
           deriving (Eq, Ord, Show)

infixl 6 `Add`
infixl 6 `Sub`
infixl 7 `Mul`

-- | Precisely the 'Unknown' constructor.
instance C.Unknown Exp where
    unknown = Unknown
    isUnknown Unknown = True
    isUnknown _       = False
-- | Precisely the 'Lit' constructor.
instance C.Lit Exp where lit = Lit
-- | Precisely the 'Neg' constructor.
instance C.Neg Exp where neg = Neg
-- | Precisely the 'Add' constructor.
instance C.Add Exp where add = Add
-- | Precisely the 'Mul' constructor.
instance C.Mul Exp where mul = Mul
-- | Precisely the 'Sub' constructor.
instance C.Sub Exp where sub = Sub
-- | Precisely the 'Scale' constructor.
instance C.Scale Exp where scale = Scale

-- | Evaluates an expression to a value.
--
-- Law: @e == eval e@
eval  (C.Unknown α, C.Lit α, C.Neg α, C.Add α, C.Mul α, C.Sub α, C.Scale α)  Exp  α
eval (Add x y)     = C.add (eval x) (eval y)
eval (Mul x y)     = C.mul (eval x) (eval y)
eval (Sub x y)     = C.sub (eval x) (eval y)
eval (Neg x)       = C.neg (eval x)
eval (Lit x)       = C.lit x
eval (Scale b o r) = C.scale b o (eval r)
eval Unknown       = C.unknown

-- prop_eval ∷ Exp → Bool
-- prop_eval e = e ≡ eval e


-------------------------------------------------------------------------------
-- Side
-------------------------------------------------------------------------------

-- | A side or direction, either 'L'eft or 'R'ight.
data Side = L -- ^ Left.
          | R -- ^ Right.
            deriving Show