-----------------------------------------------------------------------------
-- |
-- Module      : Blip.Compiler.Types
-- Copyright   : (c) 2012, 2013, 2014 Bernie Pope
-- License     : BSD-style
-- Maintainer  : florbitous@gmail.com
-- Stability   : experimental
-- Portability : ghc
--
-- Type definitions which are used in multiple modules.
--
-----------------------------------------------------------------------------
module Blip.Compiler.Types 
   ( Identifier, CompileConfig (..), VarIndex, IndexedVarSet
   , ConstantID, ConstantCache, CompileState (..), BlockState (..)
   , AnnotatedCode (..), LabelMap, Dumpable (..), VarSet
   , LocalScope (..), NestedScope (..), VarInfo (..)
   , ScopeIdentifier, FrameBlockInfo (..), Context (..), ParameterTypes (..)
   ) where

import Data.Set (Set)
import Blip.Bytecode (Bytecode (..))
import Blip.Marshal (PyObject (..))
import Data.Word (Word32, Word16)
import qualified Data.Map as Map

-- The context in which a variable is used affects the bytecode
-- related to that use.
data Context
   = ModuleContext
   | ClassContext
   | FunctionContext
   deriving (Eq, Ord, Show)

-- information about how a variable is bound plus its offset into
-- the appropriate structure
data VarInfo
   = LocalVar
   | CellVar !VarIndex 
   | FreeVar !VarIndex 
   | ExplicitGlobal
   | ImplicitGlobal

type VarSet = Set Identifier

-- XXX need to handle keyword only paramters
data ParameterTypes
   = ParameterTypes
     { parameterTypes_pos :: ![Identifier]
     , parameterTypes_varPos :: !(Maybe Identifier)
     , parameterTypes_varKeyword :: !(Maybe Identifier)
     }
   deriving (Eq, Show)

data LocalScope
   = LocalScope
     { localScope_params :: !ParameterTypes
     , localScope_locals :: !VarSet
     , localScope_freeVars :: !VarSet
     , localScope_cellVars :: !VarSet
     , localScope_explicitGlobals :: !VarSet
     }
     deriving Show

-- start and end coordinates of span (row, col, row, col)
type ScopeIdentifier = (Int, Int, Int, Int)

-- mapping from source location to pair of (scope name, local scope)
newtype NestedScope =
   NestedScope (Map.Map ScopeIdentifier (String, LocalScope))
   deriving Show

data Dumpable = DumpScope | DumpAST
   deriving (Eq, Ord, Show)

data AnnotatedCode
   = AnnotatedCode 
     { annotatedCode_bytecode :: !Bytecode
     , annotatedCode_labels :: ![Word16]   -- instruction can be labelled zero or more times
     , annotatedCode_index :: !Word16 }    -- byte offset of the instruction within this sequence of bytecode
   deriving Show

type Identifier = String -- a variable name

data CompileConfig =
   CompileConfig
   { compileConfig_magic :: !Word32
   , compileConfig_dumps :: !(Set Dumpable)
   }
   deriving (Eq, Show)

type ConstantID = Word16
type ConstantCache = Map.Map PyObject ConstantID 

data CompileState = CompileState
   { state_config :: !CompileConfig
   , state_blockState :: !BlockState
   , state_filename :: !FilePath
   , state_nestedScope :: !NestedScope
   }

-- Map from Label to Instruction offset.
-- The same instruction can be labelled multiple times,
-- but each label is attached to exactly one instruction.
type LabelMap = Map.Map Word16 Word16

type VarIndex = Word16
type IndexedVarSet = Map.Map Identifier VarIndex

data BlockState = BlockState 
   { state_label :: !Word16
   , state_instructions :: ![AnnotatedCode]
   , state_labelNextInstruction :: ![Word16] -- zero or more labels for the next instruction
   , state_constants :: ![PyObject] 
   , state_constantCache :: !ConstantCache
   , state_nextConstantID :: !ConstantID
   , state_names :: ![Identifier]
   , state_nameCache :: !IndexedVarSet
   , state_nextNameID :: !VarIndex
   , state_objectName :: !String
   , state_instruction_index :: !Word16
   , state_labelMap :: !LabelMap
   , state_locals :: !VarSet
   , state_fastLocals :: !IndexedVarSet
   , state_freeVars :: !IndexedVarSet
   , state_cellVars :: !IndexedVarSet
   , state_explicitGlobals :: !VarSet
   , state_argcount :: !Word32
   , state_flags :: !Word32
   , state_frameBlockStack :: ![FrameBlockInfo]
   , state_context :: !Context
   , state_lineNumber :: !Word32
   , state_lineNumberTable :: ![(Word16, Word32)] -- mapping from bytecode offset to source line number
   , state_firstLineNumber :: !Word32
   }
   deriving (Show)

data FrameBlockInfo
   = FrameBlockLoop !Word16
   | FrameBlockExcept 
   | FrameBlockFinallyTry 
   | FrameBlockFinallyEnd
   deriving (Eq, Show)
