-- |
-- Module      :  $Header$
-- Copyright   :  (c) 2013-2016 Galois, Inc.
-- License     :  BSD3
-- Maintainer  :  cryptol@galois.com
-- Stability   :  provisional
-- Portability :  portable

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
module Cryptol.Prims.Syntax
  ( TFun(..), tBinOpPrec, tfunNames
  ) where

import           Cryptol.Parser.Name (PName,mkUnqual)
import           Cryptol.Utils.Ident (packIdent,packInfix)
import           Cryptol.Utils.PP
import qualified Data.Map as Map

import GHC.Generics (Generic)
import Control.DeepSeq

-- | Built-in types.
data TFun

  = TCAdd                 -- ^ @ : Num -> Num -> Num @
  | TCSub                 -- ^ @ : Num -> Num -> Num @
  | TCMul                 -- ^ @ : Num -> Num -> Num @
  | TCDiv                 -- ^ @ : Num -> Num -> Num @
  | TCMod                 -- ^ @ : Num -> Num -> Num @
  | TCExp                 -- ^ @ : Num -> Num -> Num @
  | TCWidth               -- ^ @ : Num -> Num @
  | TCMin                 -- ^ @ : Num -> Num -> Num @
  | TCMax                 -- ^ @ : Num -> Num -> Num @

  -- Computing the lengths of explicit enumerations
  | TCLenFromThen         -- ^ @ : Num -> Num -> Num -> Num@
    -- Example: @[ 1, 5 .. ] :: [lengthFromThen 1 5 b][b]@

  | TCLenFromThenTo       -- ^ @ : Num -> Num -> Num -> Num@
    -- Example: @[ 1, 5 .. 9 ] :: [lengthFromThenTo 1 5 9][b]@

    deriving (Show, Eq, Ord, Bounded, Enum, Generic, NFData)

tBinOpPrec :: Map.Map TFun (Assoc,Int)
tBinOpPrec  = mkMap t_table
  where
  mkMap t = Map.fromList [ (op,(a,n)) | ((a,ops),n) <- zip t [1..] , op <- ops ]

  -- lowest to highest
  t_table =
    [ (LeftAssoc,   [ TCAdd, TCSub ])
    , (LeftAssoc,   [ TCMul, TCDiv, TCMod ])
    , (RightAssoc,  [ TCExp ])
    ]

-- | Type functions, with their arity and function constructor.
tfunNames :: Map.Map PName (Int,TFun)
tfunNames  = Map.fromList
  [ tinfix  "+"                2 TCAdd
  , tinfix  "-"                2 TCSub
  , tinfix  "*"                2 TCMul
  , tinfix  "/"                2 TCDiv
  , tinfix  "%"                2 TCMod
  , tinfix  "^^"               2 TCExp
  , tprefix "width"            1 TCWidth
  , tprefix "min"              2 TCMin
  , tprefix "max"              2 TCMax
  , tprefix "lengthFromThen"   3 TCLenFromThen
  , tprefix "lengthFromThenTo" 3 TCLenFromThenTo
  ]
  where

  tprefix n a p = (mkUnqual (packIdent n), (a,p))
  tinfix  n a p = (mkUnqual (packInfix n), (a,p))

instance PPName TFun where
  ppNameFixity f = Map.lookup f tBinOpPrec

  ppPrefixName TCAdd = text "(+)"
  ppPrefixName TCSub = text "(-)"
  ppPrefixName TCMul = text "(*)"
  ppPrefixName TCDiv = text "(/)"
  ppPrefixName TCMod = text "(%)"
  ppPrefixName TCExp = text "(^^)"
  ppPrefixName f     = pp f

  ppInfixName TCAdd = text "+"
  ppInfixName TCSub = text "-"
  ppInfixName TCMul = text "*"
  ppInfixName TCDiv = text "/"
  ppInfixName TCMod = text "%"
  ppInfixName TCExp = text "^^"
  ppInfixName f     = error $ "Not a prefix type function: " ++ show (pp f)

instance PP TFun where
  ppPrec _ tcon =
    case tcon of
      TCAdd             -> text "+"
      TCSub             -> text "-"
      TCMul             -> text "*"
      TCDiv             -> text "/"
      TCMod             -> text "%"
      TCExp             -> text "^^"
      TCWidth           -> text "width"
      TCMin             -> text "min"
      TCMax             -> text "max"

      TCLenFromThen     -> text "lengthFromThen"
      TCLenFromThenTo   -> text "lengthFromThenTo"