-- {-# LANGUAGE OverloadedStrings #-}

-- | Useful constant expression converters.
module Analyzer.ConstExpressionConverters where

import Analyzer.AnalyzedAst (Expression (ExprValue), Value (..))
import Analyzer.AnalyzedType (Type (..))
import Data.Either.Extra (mapLeft)
import MaybeVoid (MaybeVoid (..))
import qualified Parser.Ast as Ast
import qualified PrimitiveValue as PV

-- | Simplifies expression to value expression if possible.
simplifyConstExpr :: Ast.Expression -> Either Err (MaybeVoid Type, Expression)
simplifyConstExpr :: Expression -> Either Err (MaybeVoid Type, Expression)
simplifyConstExpr Expression
expression = do
  PrimitiveValue Integer
constant <- Expression -> Either Err (PrimitiveValue Integer)
simplifyConstExpr' Expression
expression
  case PrimitiveValue Integer
constant of
    PV.PrimNum Integer
c -> do
      Int
c' <- Integer -> Either Err Int
convertIntegerToInt Integer
c
      forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. a -> MaybeVoid a
NonVoid Type
TInt, Value -> Expression
ExprValue forall a b. (a -> b) -> a -> b
$ Int -> Value
ValInt Int
c')
    PV.PrimBool Bool
c -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. a -> MaybeVoid a
NonVoid Type
TBool, Value -> Expression
ExprValue forall a b. (a -> b) -> a -> b
$ Bool -> Value
ValBool Bool
c)
    PV.PrimString Text
c -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. a -> MaybeVoid a
NonVoid Type
TString, Value -> Expression
ExprValue forall a b. (a -> b) -> a -> b
$ Text -> Value
ValString Text
c)

-- | Simplifies expression to int if possible.
simplifyConstIntExpr :: Ast.Expression -> Either Err Int
simplifyConstIntExpr :: Expression -> Either Err Int
simplifyConstIntExpr Expression
expression = do
  PrimitiveValue Integer
constant <- Expression -> Either Err (PrimitiveValue Integer)
simplifyConstExpr' Expression
expression
  case PrimitiveValue Integer
constant of
    PV.PrimNum Integer
c -> Integer -> Either Err Int
convertIntegerToInt Integer
c
    PrimitiveValue Integer
_ -> forall a b. a -> Either a b
Left Err
MismatchedTypes

-- | Converts integer to int if possible.
convertIntegerToInt :: Integer -> Either Err Int
convertIntegerToInt :: Integer -> Either Err Int
convertIntegerToInt Integer
integer =
  if forall a. Integral a => a -> Integer
toInteger (forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
integer :: Int) forall a. Eq a => a -> a -> Bool
== Integer
integer -- Checks for overflow
    then forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
integer
    else forall a b. a -> Either a b
Left Err
NotInIntBounds

-- | Simplifies expression to primitive value if possible.
simplifyConstExpr' :: Ast.Expression -> Either Err (PV.PrimitiveValue Integer)
simplifyConstExpr' :: Expression -> Either Err (PrimitiveValue Integer)
simplifyConstExpr' Expression
expression = case Expression
expression of
  Ast.ExprValue Value
val -> case Value
val of
    Ast.ValInt Integer
c -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall num. num -> PrimitiveValue num
PV.PrimNum Integer
c
    Ast.ValBool Bool
c -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall num. Bool -> PrimitiveValue num
PV.PrimBool Bool
c
    Ast.ValString Text
c -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall num. Text -> PrimitiveValue num
PV.PrimString Text
c
    Value
_ -> forall a b. a -> Either a b
Left Err
NotConstExpr
  Ast.ExprUnaryOp UnaryOp
unOp Expression
expr -> do
    PrimitiveValue Integer
expr' <- Expression -> Either Err (PrimitiveValue Integer)
simplifyConstExpr' Expression
expr
    forall a c b. (a -> c) -> Either a b -> Either c b
mapLeft Err -> Err
mapErr forall a b. (a -> b) -> a -> b
$ forall num.
Integral num =>
UnaryOp -> PrimitiveValue num -> Either Err (PrimitiveValue num)
PV.primitiveUnOpApplication UnaryOp
unOp PrimitiveValue Integer
expr'
  Ast.ExprBinaryOp BinaryOp
binOp Expression
lhs Expression
rhs -> do
    PrimitiveValue Integer
lhs' <- Expression -> Either Err (PrimitiveValue Integer)
simplifyConstExpr' Expression
lhs
    PrimitiveValue Integer
rhs' <- Expression -> Either Err (PrimitiveValue Integer)
simplifyConstExpr' Expression
rhs
    forall a c b. (a -> c) -> Either a b -> Either c b
mapLeft Err -> Err
mapErr forall a b. (a -> b) -> a -> b
$ forall num.
Integral num =>
BinaryOp
-> PrimitiveValue num
-> PrimitiveValue num
-> Either Err (PrimitiveValue num)
PV.primitiveBinOpApplication BinaryOp
binOp PrimitiveValue Integer
lhs' PrimitiveValue Integer
rhs'
  Expression
_ -> forall a b. a -> Either a b
Left Err
NotConstExpr

mapErr :: PV.Err -> Err
mapErr :: Err -> Err
mapErr Err
err = case Err
err of
  Err
PV.MismatchedTypes -> Err
MismatchedTypes
  Err
PV.DivisionByZero -> Err
DivisionByZero

data Err
  = MismatchedTypes
  | DivisionByZero
  | NotInIntBounds
  | NotConstExpr