{-# LANGUAGE MonoLocalBinds #-}

-- | Organizes all the phases.
-- Allows builting and interpreting a program, without exposing any
-- particular phase information -- should maybe be 'Descript.Build.Interface'.
module Descript.Build.Build
  ( compile
  , eval
  , interpret
  ) where

import Descript.Build.Error
import Descript.Build.Read
import qualified Descript.BasicInj.Data.Value.Reg as BasicInj
import qualified Descript.BasicInj as BasicInj
import Descript.Misc
import Data.Text (Text)
import Control.Monad
import Core.Control.Monad.Trans

-- TODO In future versions, refine "output":
--   look at the reduced value. If it's a datum, output it. If it's a
--   compiled program, output it (maybe in a 'Compiled' structure).
--   Maybe if it's some other "datum"-y record types, output it.
--

-- | Parses, interprets, and compiles a source file.
compile :: (Monad u) => DFile u -> BuildResultT u Package
compile file = compileB file =<< interpret file

compileB :: (Monad u) => DFile u -> BasicInj.Value () -> BuildResultT u Package
compileB file
  = hoist . mapError BuildCompileError . BasicInj.compile (fileName file)

-- | Parses, interprets, and prints a source file.
eval :: (Monad u) => DFile u -> BuildResultT u Text
eval = fmap pprint . interpret

-- | Parses and interprets a source file.
interpret :: (Monad u) => DFile u -> BuildResultT u (BasicInj.Value ())
interpret = interpretB . sourceToProgramB <=< validateB <=< readB
  where interpretB = fmap BasicInj.interpret_
        sourceToProgramB = maybeToResultT BuildExpectedProgramError . traverse BasicInj.sourceToProgram
        validateB = mapErrorT BuildValidateError . hoist . BasicInj.validate
        readB = mapErrorT BuildParseError . readSrc