packages feed

swarm-0.7.0.0: src/swarm-lang/Swarm/Language/Capability.hs

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Capabilities needed to evaluate and execute programs.  Language
-- constructs or commands require certain capabilities, and in turn
-- capabilities are provided by various devices.  A robot must have an
-- appropriate device equipped in order to make use of each language
-- construct or command.
module Swarm.Language.Capability (
  Capability (..),
  capabilityName,
  parseCapability,
  constCaps,
  constByCaps,
) where

import Control.Arrow ((&&&))
import Data.Aeson (FromJSONKey, ToJSONKey)
import Data.Char (toLower)
import Data.Data (Data)
import Data.Foldable (find)
import Data.Hashable (Hashable)
import Data.List.Extra (enumerate)
import Data.List.NonEmpty qualified as NE
import Data.Map (Map)
import Data.Maybe (mapMaybe)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Tuple (swap)
import Data.Yaml
import GHC.Generics (Generic)
import Generic.Data (FiniteEnumeration (..))
import Prettyprinter (pretty)
import Swarm.Language.Syntax.Constants (Const (..), allConst, constInfo, syntax)
import Swarm.Pretty (PrettyPrec (..))
import Swarm.Util (binTuples, failT, showEnum)
import Witch (from)
import Prelude hiding (lookup)

-- | Various capabilities which robots can have.
data Capability
  = -- | Execute the command or function.
    CExecute Const
  | -- | Be powered, i.e. execute anything at all
    CPower
  | -- | Allow a heavy robot to perform movements (e.g. move, backup and stride).
    CMoveHeavy
  | -- | Don't drown in liquid.
    CFloat
  | -- | Allow using absolute directions.
    COrient
  | -- | Store and look up definitions in an environment
    CEnv
  | -- | Interpret lambda abstractions
    CLambda
  | -- | Enable recursive definitions
    CRecursion
  | -- | Capability for working with sum types.
    CSum
  | -- | Capability for working with product types.
    CProd
  | -- | Capability for working with record types.
    CRecord
  | -- | Debug capability.
    CDebug
  | -- | Capability to handle recursive types.
    CRectype
  | -- | God-like capabilities.  For e.g. commands intended only for
    --   checking challenge mode win conditions, and not for use by
    --   players.
    CGod
  deriving (Eq, Ord, Show, Generic, Hashable, Data, FromJSONKey, ToJSONKey)
  deriving (Enum, Bounded) via (FiniteEnumeration Capability)

instance PrettyPrec Capability where
  prettyPrec _ c = pretty $ T.toLower (from (NE.tail $ showEnum c))

-- | Get the name of the capability for use in UI and YAML.
capabilityName :: Capability -> Text
capabilityName = \case
  CExecute con -> case con of
    Neg -> "neg"
    _ -> syntax $ constInfo con
  CMoveHeavy -> "move heavy robot"
  cap -> from @String . map toLower . drop 1 $ show cap

-- | Parse the capability name - inverse of 'capabilityName'.
--
-- >>> import Data.List.Extra (enumerate)
-- >>> all (\c -> Just c == parseCapability (capabilityName c)) enumerate
-- True
parseCapability :: Text -> Maybe Capability
parseCapability t = find (\c -> capabilityName c == T.toLower t) enumerate

instance ToJSON Capability where
  toJSON = String . capabilityName

instance FromJSON Capability where
  parseJSON = withText "Capability" tryRead
   where
    tryRead :: Text -> Parser Capability
    tryRead t = case parseCapability t of
      Just c -> return c
      Nothing -> failT ["Unknown capability", t]

-- | Capabilities needed to evaluate or execute a constant.
constCaps :: Const -> Maybe Capability
constCaps = \case
  -- ----------------------------------------------------------------
  -- Some built-in constants that don't require any special capability.
  AppF -> Nothing
  Base -> Nothing
  Equipped -> Nothing
  Fail -> Nothing
  Force -> Nothing
  Has -> Nothing
  Knows -> Nothing
  Noop -> Nothing
  Parent -> Nothing
  Pure -> Nothing
  Say -> Nothing -- speaking is natural to robots (unlike listening)
  Setname -> Nothing
  Undefined -> Nothing
  Use -> Nothing -- Recipes alone shall dictate whether things can be "used"
  View -> Nothing -- TODO: #17 should require equipping an antenna
  -- TODO: #495
  --   the require command will be inlined once the Issue is fixed
  --   so the capabilities of the run commands will be checked instead
  Run -> Nothing
  -- Some God-like abilities.
  As -> Just CGod
  Create -> Just CGod
  Instant -> Just CGod
  RobotNamed -> Just CGod
  RobotNumbered -> Just CGod
  Surveil -> Just CGod
  -- ----------------------------------------------------------------
  -- type-level arithmetic
  Inl -> Just CSum
  Inr -> Just CSum
  Case -> Just CSum
  -- TODO: #563 pair syntax (1,2,3...) should require CProd too
  Match -> Just CProd
  c -> Just (CExecute c)

-- | Inverts the 'constCaps' mapping.
constByCaps :: Map Capability (NE.NonEmpty Const)
constByCaps =
  binTuples $
    map swap $
      mapMaybe (sequenceA . (id &&& constCaps)) allConst