packages feed

penrose-0.1.1.0: src/Interface.hs

module Interface where
{-# OPTIONS_HADDOCK prune #-}

import           Control.Exception          (ErrorCall, try)
import qualified Data.Aeson                 as A
import qualified Data.ByteString.Lazy.Char8 as B
import           Element
import           Env
import           GenOptProblem
import qualified Optimizer
import           Plugins
import           Serializer
import           Style
import           Substance
import           Sugarer
import           System.IO.Unsafe           (unsafePerformIO)
import           Utils

-- | Given Substance, Style, and Element programs, output an initial state.
-- TODO: allow cached intermediate outputs such as ASTs to be passed in?
compileTrio ::
     String -- ^ a Substance program
  -> String -- ^ a Style program
  -> String -- ^ an Element program
  -> Either CompilerError (State, VarEnv) -- ^ an initial state and compiler context for language services
compileTrio substance style element
  -- Parsing and desugaring phase
 = do
  env <- parseElement "" element
  styProg <- parseStyle "" style env
  let subDesugared = sugarStmts substance env -- TODO: errors?
  subOut@(SubOut _ (subEnv, _) _) <- parseSubstance "" subDesugared env
  -- Plugin phase
  pluginRes <- runPlugin subOut style env
  (subOut', styVals) <-
    case pluginRes of
      Nothing -> pure $ (subOut, [])
      Just (subPlugin, styVals) -> do
        subOutPlugin <-
          parseSubstance "" (subDesugared ++ "\n" ++ subPlugin) env
        return (subOutPlugin, styVals)
  -- Compilation phase
  let optConfig = defaultOptConfig
  let styRes =
        unsafePerformIO $ -- HACK: rewrite this such that it's safe
        try (compileStyle styProg subOut' styVals optConfig) :: Either ErrorCall State
  case styRes of
    Right initState -> Right (initState, subEnv)
    Left styRTError -> Left $ StyleTypecheck $ show styRTError

getEnv ::
     String -- ^ a Substance program
  -> String -- ^ an Element program
  -> Either CompilerError VarEnv -- ^ either a compiler error or an environment of the Substance program
getEnv substance element = do
  env <- parseElement "" element
  let subDesugared = sugarStmts substance env -- TODO: errors?
  subOut@(SubOut _ (subEnv, _) _) <- parseSubstance "" subDesugared env
  Right $ subEnv

step ::
     State -- ^ the initial state
  -> Int -- ^ the number of steps n for the optimizer to take
  -> Either RuntimeError State -- ^ the resulting state after the optimizer takes n steps
  -- TODO: rewrite runtime error reporting
step initState steps = Right $ iterate Optimizer.step initState !! (steps + 1) -- `iterate` applies `id` the first time

stepUntilConvergence ::
     State -- ^ the initial state
  -> Either RuntimeError State -- ^ the converged state or optimizer errors
stepUntilConvergence state
  | optStatus (paramsr state) == EPConverged = Right state
  -- TODO: rewrite runtime error reporting
  | otherwise = stepUntilConvergence $ Optimizer.step state

resample ::
     State -- ^ the initial state
  -> Int -- ^ number of samples to choose from (> 0). If it's 1, no selection will occur
  -> Either RuntimeError State -- ^ if the number of samples requested is smaller than 1, return error, else return the resulting state
resample initState numSamples
  | numSamples >= 1 =
    let newState = resampleBest numSamples initState
        (newShapes, _, _) = evalTranslation newState
    in Right $ newState {shapesr = newShapes}
  | otherwise = Left $ RuntimeError "At least 1 sample should be requested."

--------------------------------------------------------------------------------
-- Test
subFile = "sub/tree.sub"

styFile = "sty/venn.sty"

elmFile = "set-theory-domain/setTheory.dsl"

testCompile :: IO ()
testCompile = do
  sub <- readFile subFile
  sty <- readFile styFile
  elm <- readFile elmFile
  let res = compileTrio sub sty elm
  case res of
    Right state -> B.writeFile "state.json" $ A.encode state
    Left err    -> putStrLn $ show err

testStep :: Bool -> IO ()
testStep converge
  | converge = do
    sub <- readFile subFile
    sty <- readFile styFile
    elm <- readFile elmFile
    let s = compileTrio sub sty elm
    case s of
      Right (state, _) ->
        let res = stepUntilConvergence state
        in case res of
             Right state' -> B.writeFile "state-step.json" $ A.encode state'
             Left err     -> putStrLn $ show err
      Left err -> putStrLn $ show err
  | otherwise = do
    sub <- readFile subFile
    sty <- readFile styFile
    elm <- readFile elmFile
    let s = compileTrio sub sty elm
    case s of
      Right (state, _) ->
        let res = step state 2
        in case res of
             Right state' -> B.writeFile "state-step.json" $ A.encode state'
             Left err     -> putStrLn $ show err
      Left err -> putStrLn $ show err