swarm-0.4: src/Swarm/Language/Pipeline.hs
{-# LANGUAGE OverloadedStrings #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Some convenient functions for putting together the whole Swarm
-- language processing pipeline: parsing, type checking, capability
-- checking, and elaboration. If you want to simply turn some raw
-- text representing a Swarm program into something useful, this is
-- probably the module you want.
module Swarm.Language.Pipeline (
ProcessedTerm (..),
processTerm,
processParsedTerm,
processTerm',
processParsedTerm',
processTermEither,
) where
import Control.Lens ((^.))
import Data.Bifunctor (first)
import Data.Data (Data)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Yaml as Y
import GHC.Generics (Generic)
import Swarm.Language.Context
import Swarm.Language.Elaborate
import Swarm.Language.Module
import Swarm.Language.Parse
import Swarm.Language.Pretty
import Swarm.Language.Requirement
import Swarm.Language.Syntax
import Swarm.Language.Typecheck
import Swarm.Language.Types
import Witch (into)
-- | A record containing the results of the language processing
-- pipeline. Put a 'Term' in, and get one of these out. A
-- 'ProcessedTerm' contains:
--
-- * The elaborated + type-annotated term, plus the types of any
-- embedded definitions ('TModule')
--
-- * The 'Requirements' of the term
--
-- * The requirements context for any definitions embedded in the
-- term ('ReqCtx')
data ProcessedTerm = ProcessedTerm TModule Requirements ReqCtx
deriving (Data, Show, Eq, Generic)
processTermEither :: Text -> Either Text ProcessedTerm
processTermEither t = case processTerm t of
Left err -> Left $ T.unwords ["Could not parse term:", err]
Right Nothing -> Left "Term was only whitespace"
Right (Just pt) -> Right pt
instance FromJSON ProcessedTerm where
parseJSON = withText "Term" $ either (fail . into @String) return . processTermEither
instance ToJSON ProcessedTerm where
toJSON (ProcessedTerm t _ _) = String $ prettyText (moduleAST t)
-- | Given a 'Text' value representing a Swarm program,
--
-- 1. Parse it (see "Swarm.Language.Parse")
-- 2. Typecheck it (see "Swarm.Language.Typecheck")
-- 3. Elaborate it (see "Swarm.Language.Elaborate")
-- 4. Check what capabilities it requires (see "Swarm.Language.Capability")
--
-- Return either the end result (or @Nothing@ if the input was only
-- whitespace) or a pretty-printed error message.
processTerm :: Text -> Either Text (Maybe ProcessedTerm)
processTerm = processTerm' empty empty
-- | Like 'processTerm', but use a term that has already been parsed.
processParsedTerm :: Syntax -> Either ContextualTypeErr ProcessedTerm
processParsedTerm = processParsedTerm' empty empty
-- | Like 'processTerm', but use explicit starting contexts.
processTerm' :: TCtx -> ReqCtx -> Text -> Either Text (Maybe ProcessedTerm)
processTerm' ctx capCtx txt = do
mt <- readTerm txt
first (prettyTypeErrText txt) $ traverse (processParsedTerm' ctx capCtx) mt
-- | Like 'processTerm'', but use a term that has already been parsed.
processParsedTerm' :: TCtx -> ReqCtx -> Syntax -> Either ContextualTypeErr ProcessedTerm
processParsedTerm' ctx capCtx t = do
m <- inferTop ctx t
let (caps, capCtx') = requirements capCtx (t ^. sTerm)
return $ ProcessedTerm (elaborateModule m) caps capCtx'
elaborateModule :: TModule -> TModule
elaborateModule (Module ast ctx) = Module (elaborate ast) ctx