packages feed

proof-assistant-bot-0.2.0: src/Agda/Interaction/Command.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}

module Agda.Interaction.Command where

import Data.ByteString (ByteString)
import Data.HashMap.Strict (HashMap)
import GHC.Generics (Generic)
import System.FilePath

import Agda.Interaction.Command.EvalIn
import Agda.Interaction.Command.EvalTerm
import Agda.Interaction.Command.GiveMeta
import Agda.Interaction.Command.RefineMeta
import Agda.Interaction.Command.Reload
import Agda.Interaction.Command.RetryConstraints
import Agda.Interaction.Command.ShowConstraints
import Agda.Interaction.Command.ShowContext
import Agda.Interaction.Command.ShowMetas
import Agda.Interaction.Command.ShowScope
import Agda.Interaction.Command.TypeOf
import Agda.Interaction.Command.TypeIn

import Agda.Interaction.State
import Agda.TypeChecking.Monad.Base (TCEnv (..), TCM)
import Agda.Utils.FileName (AbsolutePath, filePath)

import qualified Data.ByteString.Char8 as BS8
import qualified Data.HashMap.Strict as HashMap

-- | Supported sub-commands for Agda.
data AgdaCommand
  = Reload | Help | Constraints | Context | Give | Refine | Meta | Load
  | Eval | TypeOf | TypeIn | WakeUp | Scope
  deriving (Eq, Show, Generic)

-- | Map of supported Agda sub-commands. We use it instead of parser.
supportedCommands :: HashMap ByteString AgdaCommand
supportedCommands = HashMap.fromList
  [ (,) "/reload" Reload
  , (,) "/?" Help
  , (,) "/constraints" Constraints
  , (,) "/context" Context
  , (,) "/give" Give
  , (,) "/refine" Refine
  , (,) "/meta" Meta
  , (,) "/load" Load
  , (,) "/eval" Eval
  , (,) "/typeOf" TypeOf
  , (,) "/typeIn" TypeIn
  , (,) "/wakeup" WakeUp
  , (,) "/scope" Scope
  ]

-- | Check user input to identify 'AgdaCommand'.
matchSupported :: ByteString -> Maybe AgdaCommand
matchSupported = (`HashMap.lookup` supportedCommands)

-- | Choose an action based on either 'AgdaCommand' or raw input.
chooseCommand :: AgdaState -> Either () AgdaCommand -> ByteString -> IO (TCM ByteString)
chooseCommand _state (Left _) input = pure (evalTerm input)
chooseCommand state (Right cmd) input = do
  mfilePath <- envCurrentPath <$> readTCEnv state
  case cmd of
    Constraints -> pure (showConstraints (BS8.words input))
    Context     -> pure (showContext (BS8.words input))
    Give        -> pure (giveMeta (BS8.words input))
    Refine      -> pure (refineMeta (BS8.words input))
    Scope       -> pure showScope
    Meta        -> pure (showMetas (BS8.words input))
    Load        -> case mfilePath of
      Nothing   -> pure (pure "Failed to load.")
      Just filepath -> do
        storeRequestContent filepath input
        pure $ reload (Just filepath)
    Reload      -> pure (reload mfilePath)
    WakeUp      -> pure retryConstraints
    TypeOf      -> pure (typeOf (BS8.words input))
    TypeIn      -> pure (typeIn (BS8.words input))
    Eval        -> pure (evalIn (BS8.words input))
    _           -> pure (pure tbd)
  where
    tbd = BS8.unwords
      [ "Command"
      , BS8.pack (show cmd)
      , "is not supported yet."
      ]

-- | Store raw user input in the file given by 'TCEnv'.
storeRequestContent :: AbsolutePath -> ByteString -> IO ()
storeRequestContent absFilepath content = do
  let moduleName = (BS8.pack . dropExtension . takeFileName . filePath) absFilepath
      moduleContent = BS8.unlines
        [ BS8.unwords [ "module", moduleName, "where" ]
        , ""
        , content
        ]
  BS8.writeFile (filePath absFilepath) moduleContent