{-# LANGUAGE TemplateHaskell #-}

-- | This module contains types and functions needed for representing analysis result.
module Analyzer.Result where

import Analyzer.AnalyzedAst (Identifier)
import Analyzer.AnalyzedType (Type)
import Control.Lens (At (at), LensLike', ix, makeLenses)
import Control.Monad.Except (ExceptT)
import Control.Monad.State (State)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Text (unpack)

-- * Analysis result

-- | Represents the result of analysis.
type Result a = ExceptT Err (State Env) a

-- ** Result value

-- | 'Result' value (in other words, it is stateless 'Result').
type ResultValue = Either Err

-- | Represents an unsuccessful analysis.
data Err
  = -- | No entry point for the interpreter error.
    NoMain
  | -- | Identifier not found error.
    IdentifierNotFound Identifier
  | -- | Identifier redeclaration error.
    IdentifierRedeclaration Identifier
  | -- | Mismatched types error.
    MismatchedTypes
  | -- | Constant integer expression not in `int` bounds error.
    NotInIntBounds
  | -- | Division by 0 in constant integer expression error.
    DivisionByZero
  | -- | @break@ or @continue@ statement used outside of 'ForScope' error.
    BreakOrContinueOutsideOfForScope

instance Show Err where
  show :: Err -> String
show Err
NoMain = String
"panic: analysis error: no main"
  show (IdentifierNotFound Identifier
name) = String
"panic: analysis error: identifier " forall a. [a] -> [a] -> [a]
++ Identifier -> String
unpack Identifier
name forall a. [a] -> [a] -> [a]
++ String
" not found"
  show (IdentifierRedeclaration Identifier
name) = String
"panic: analysis error: identifier " forall a. [a] -> [a] -> [a]
++ Identifier -> String
unpack Identifier
name forall a. [a] -> [a] -> [a]
++ String
" redeclared"
  show Err
MismatchedTypes = String
"panic: analysis error: mismatched types"
  show Err
NotInIntBounds = String
"panic: analysis error: not in int bounds"
  show Err
DivisionByZero = String
"panic: analysis error: integer divide by zero"
  show Err
BreakOrContinueOutsideOfForScope = String
"panic: analysis error: break or continue is not in a loop"

-- ** State

-- *** Environment

-- | Analyzer environment.
newtype Env = Env {Env -> [Scope]
_scopes :: [Scope]}
  deriving (Int -> Env -> ShowS
[Env] -> ShowS
Env -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Env] -> ShowS
$cshowList :: [Env] -> ShowS
show :: Env -> String
$cshow :: Env -> String
showsPrec :: Int -> Env -> ShowS
$cshowsPrec :: Int -> Env -> ShowS
Show)

-- | Create empty environment.
emptyEnv :: Env
emptyEnv :: Env
emptyEnv = [Scope] -> Env
Env []

-- *** Scope

-- | Scope contains identifiers mapped to their types.
data Scope = Scope
  { Scope -> ScopeType
_scopeType :: ScopeType,
    Scope -> Map Identifier Type
_vars :: Map Identifier Type
  }
  deriving (Int -> Scope -> ShowS
[Scope] -> ShowS
Scope -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Scope] -> ShowS
$cshowList :: [Scope] -> ShowS
show :: Scope -> String
$cshow :: Scope -> String
showsPrec :: Int -> Scope -> ShowS
$cshowsPrec :: Int -> Scope -> ShowS
Show)

-- | 'Scope' type.
data ScopeType = ForScope | OrdinaryScope
  deriving (Int -> ScopeType -> ShowS
[ScopeType] -> ShowS
ScopeType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ScopeType] -> ShowS
$cshowList :: [ScopeType] -> ShowS
show :: ScopeType -> String
$cshow :: ScopeType -> String
showsPrec :: Int -> ScopeType -> ShowS
$cshowsPrec :: Int -> ScopeType -> ShowS
Show, ScopeType -> ScopeType -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ScopeType -> ScopeType -> Bool
$c/= :: ScopeType -> ScopeType -> Bool
== :: ScopeType -> ScopeType -> Bool
$c== :: ScopeType -> ScopeType -> Bool
Eq)

-- | Create 'Scope' from its type and elements.
scope :: ScopeType -> [(Identifier, Type)] -> Scope
scope :: ScopeType -> [(Identifier, Type)] -> Scope
scope ScopeType
t [(Identifier, Type)]
elements = ScopeType -> Map Identifier Type -> Scope
Scope ScopeType
t (forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Identifier, Type)]
elements)

-- | Create empty 'Scope' from its type.
emptyScope :: ScopeType -> Scope
emptyScope :: ScopeType -> Scope
emptyScope ScopeType
t = ScopeType -> [(Identifier, Type)] -> Scope
scope ScopeType
t []

-- *** Optics

makeLenses ''Env
makeLenses ''Scope
makeLenses ''ScopeType

var :: Applicative f => Int -> Identifier -> LensLike' f Env (Maybe Type)
var :: forall (f :: * -> *).
Applicative f =>
Int -> Identifier -> LensLike' f Env (Maybe Type)
var Int
i Identifier
name = Iso' Env [Scope]
scopes forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Int
i forall b c a. (b -> c) -> (a -> b) -> a -> c
. Lens' Scope (Map Identifier Type)
vars forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
at Identifier
name