swarm-0.1.0.0: src/Swarm/Language/Pipeline.hs
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module : Swarm.Language.Pipeline
-- Copyright : Brent Yorgey
-- Maintainer : byorgey@gmail.com
--
-- 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',
showTypeErrorPos,
) where
import Data.Bifunctor (first)
import Data.Data (Data)
import Data.Text (Text)
import Data.Yaml as Y
import GHC.Generics (Generic)
import Swarm.Language.Context
import Swarm.Language.Elaborate
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
-- | A record containing the results of the language processing
-- pipeline. Put a 'Term' in, and get one of these out.
data ProcessedTerm
= ProcessedTerm
Term
-- ^ The elaborated term
TModule
-- ^ The type of the term (and of any embedded definitions)
Requirements
-- ^ Requirements of the term
ReqCtx
-- ^ Capability context for any definitions embedded in the term
deriving (Data, Show, Eq, Generic)
instance FromJSON ProcessedTerm where
parseJSON = withText "Term" tryProcess
where
tryProcess :: Text -> Y.Parser ProcessedTerm
tryProcess t = case processTerm t of
Left err -> fail $ "Could not parse term: " ++ from err
Right Nothing -> fail "Term was only whitespace"
Right (Just pt) -> return pt
instance ToJSON ProcessedTerm where
toJSON (ProcessedTerm t _ _ _) = String $ prettyText 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 TypeErr 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 (prettyTypeErr txt) $ traverse (processParsedTerm' ctx capCtx) mt
prettyTypeErr :: Text -> TypeErr -> Text
prettyTypeErr code te = teLoc <> prettyText te
where
teLoc = case getTypeErrLocation te of
Just (Location s e) -> (from . show . fst . fst $ getLocRange code (s, e)) <> ": "
_anyOtherLoc -> ""
showTypeErrorPos :: Text -> TypeErr -> ((Int, Int), (Int, Int), Text)
showTypeErrorPos code te = (minusOne start, minusOne end, msg)
where
minusOne (x, y) = (x - 1, y - 1)
(start, end) = case getTypeErrLocation te of
Just (Location s e) -> getLocRange code (s, e)
_anyOtherLoc -> ((1, 1), (65535, 65535)) -- unknown loc spans the whole document
msg = prettyText te
-- | Like 'processTerm'', but use a term that has already been parsed.
processParsedTerm' :: TCtx -> ReqCtx -> Syntax -> Either TypeErr ProcessedTerm
processParsedTerm' ctx capCtx t = do
ty <- inferTop ctx t
let (caps, capCtx') = requirements capCtx (sTerm t)
return $ ProcessedTerm (elaborate (sTerm t)) ty caps capCtx'