diff --git a/.gitignore b/.gitignore index 9fc5c7d4..5ba698fe 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,10 @@ Thumbs.db *.purs.bak /tests/oa -/tests/sources.txt \ No newline at end of file +/tests/sources.txt + + +# lsp vscode client + +/editors/code/node_modules +/editors/code/out diff --git a/Cargo.toml b/Cargo.toml index 54200bf7..6b43e37d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ swc_common = "18.0.1" ntest_timeout = "0.9.5" rayon = "1.10" mimalloc = { version = "0.1", default-features = false } +tower-lsp = "0.20" +tokio = { version = "1", features = ["full"] } +serde_json = "1" [build-dependencies] lalrpop = "0.22" diff --git a/claude-help/original-compiler/src/Control/Monad/Logger.hs b/claude-help/original-compiler/src/Control/Monad/Logger.hs deleted file mode 100644 index a3ed57b0..00000000 --- a/claude-help/original-compiler/src/Control/Monad/Logger.hs +++ /dev/null @@ -1,56 +0,0 @@ --- | --- A replacement for WriterT IO which uses mutable references. --- -module Control.Monad.Logger where - -import Prelude - -import Control.Monad (ap) -import Control.Monad.Base (MonadBase(..)) -import Control.Monad.IO.Class (MonadIO(..)) -import Control.Monad.Trans.Control (MonadBaseControl(..)) -import Control.Monad.Writer.Class (MonadWriter(..)) - -import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef) - --- | A replacement for WriterT IO which uses mutable references. -newtype Logger w a = Logger { runLogger :: IORef w -> IO a } - --- | Run a Logger computation, starting with an empty log. -runLogger' :: (Monoid w) => Logger w a -> IO (a, w) -runLogger' l = do - r <- newIORef mempty - a <- runLogger l r - w <- readIORef r - return (a, w) - -instance Functor (Logger w) where - fmap f (Logger l) = Logger $ \r -> fmap f (l r) - -instance (Monoid w) => Applicative (Logger w) where - pure = Logger . const . pure - (<*>) = ap - -instance (Monoid w) => Monad (Logger w) where - return = pure - Logger l >>= f = Logger $ \r -> l r >>= \a -> runLogger (f a) r - -instance (Monoid w) => MonadIO (Logger w) where - liftIO = Logger . const - -instance (Monoid w) => MonadWriter w (Logger w) where - tell w = Logger $ \r -> atomicModifyIORef' r $ \w' -> (mappend w' w, ()) - listen l = Logger $ \r -> do - (a, w) <- liftIO (runLogger' l) - atomicModifyIORef' r $ \w' -> (mappend w' w, (a, w)) - pass l = Logger $ \r -> do - ((a, f), w) <- liftIO (runLogger' l) - atomicModifyIORef' r $ \w' -> (mappend w' (f w), a) - -instance (Monoid w) => MonadBase IO (Logger w) where - liftBase = liftIO - -instance (Monoid w) => MonadBaseControl IO (Logger w) where - type StM (Logger w) a = a - liftBaseWith f = Logger $ \r -> liftBaseWith $ \q -> f (q . flip runLogger r) - restoreM = return diff --git a/claude-help/original-compiler/src/Control/Monad/Supply.hs b/claude-help/original-compiler/src/Control/Monad/Supply.hs deleted file mode 100644 index 1aa6c3df..00000000 --- a/claude-help/original-compiler/src/Control/Monad/Supply.hs +++ /dev/null @@ -1,38 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} --- | --- Fresh variable supply --- -module Control.Monad.Supply where - -import Prelude - -import Control.Applicative (Alternative) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Reader (MonadReader, MonadTrans) -import Control.Monad (MonadPlus) -import Control.Monad.State (MonadState(..)) -import Control.Monad.State.Strict (StateT(..)) -import Control.Monad.Writer (MonadWriter) -import Data.Int (Int64) - -import Data.Functor.Identity (Identity(..)) -import Control.Monad.IO.Class (MonadIO) -import Control.Monad.Trans (lift) - -newtype SupplyT m a = SupplyT { unSupplyT :: StateT Int64 m a } - deriving (Functor, Applicative, Monad, MonadTrans, MonadError e, MonadWriter w, MonadReader r, Alternative, MonadPlus, MonadIO) - -runSupplyT :: Int64 -> SupplyT m a -> m (a, Int64) -runSupplyT n = flip runStateT n . unSupplyT - -evalSupplyT :: (Functor m) => Int64 -> SupplyT m a -> m a -evalSupplyT n = fmap fst . runSupplyT n - -type Supply = SupplyT Identity - -runSupply :: Int64 -> Supply a -> (a, Int64) -runSupply n = runIdentity . runSupplyT n - -instance MonadState s m => MonadState s (SupplyT m) where - get = lift get - put = lift . put diff --git a/claude-help/original-compiler/src/Control/Monad/Supply/Class.hs b/claude-help/original-compiler/src/Control/Monad/Supply/Class.hs deleted file mode 100644 index 8acdc363..00000000 --- a/claude-help/original-compiler/src/Control/Monad/Supply/Class.hs +++ /dev/null @@ -1,45 +0,0 @@ -{-# LANGUAGE TypeOperators #-} - --- | --- A class for monads supporting a supply of fresh names --- - -module Control.Monad.Supply.Class where - -import Prelude - -import Control.Monad.RWS (MonadState(..), MonadTrans(..), RWST) -import Control.Monad.State (StateT) -import Control.Monad.Supply (SupplyT(..)) -import Control.Monad.Writer (WriterT) -import Data.Text (Text, pack) -import Data.Int (Int64) - -class Monad m => MonadSupply m where - fresh :: m Int64 - peek :: m Int64 - consumeUpTo :: Int64 -> m () - default fresh :: (MonadTrans t, MonadSupply n, m ~ t n) => m Int64 - fresh = lift fresh - default peek :: (MonadTrans t, MonadSupply n, m ~ t n) => m Int64 - peek = lift peek - default consumeUpTo :: (MonadTrans t, MonadSupply n, m ~ t n) => Int64 -> m () - consumeUpTo n = lift (consumeUpTo n) - -instance Monad m => MonadSupply (SupplyT m) where - fresh = SupplyT $ do - n <- get - put (n + 1) - return n - peek = SupplyT get - consumeUpTo n = SupplyT $ do - m <- get - put $ max n m - -instance MonadSupply m => MonadSupply (StateT s m) -instance (Monoid w, MonadSupply m) => MonadSupply (WriterT w m) -instance (Monoid w, MonadSupply m) => MonadSupply (RWST r w s m) - -freshName :: MonadSupply m => m Text -freshName = fmap (("$" <> ) . pack . show) fresh - diff --git a/claude-help/original-compiler/src/Control/PatternArrows.hs b/claude-help/original-compiler/src/Control/PatternArrows.hs deleted file mode 100644 index b01d1ccc..00000000 --- a/claude-help/original-compiler/src/Control/PatternArrows.hs +++ /dev/null @@ -1,118 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Control.PatternArrows --- Copyright : (c) Phil Freeman 2013 --- License : MIT --- --- Maintainer : Phil Freeman --- Stability : experimental --- Portability : --- --- | --- Arrows for Pretty Printing --- ------------------------------------------------------------------------------ - -{-# LANGUAGE GeneralizedNewtypeDeriving, GADTs #-} - -module Control.PatternArrows where - -import Prelude - -import Control.Arrow ((***), (<+>)) -import Control.Arrow qualified as A -import Control.Category ((>>>)) -import Control.Category qualified as C -import Control.Monad.State -import Control.Monad.Fix (fix) - --- | --- A first-order pattern match --- --- A pattern is a Kleisli arrow for the @StateT Maybe@ monad. That is, patterns can fail, and can carry user-defined state. --- -newtype Pattern u a b = Pattern { runPattern :: A.Kleisli (StateT u Maybe) a b } deriving (A.Arrow, A.ArrowZero, A.ArrowPlus) - -instance C.Category (Pattern u) where - id = Pattern C.id - Pattern p1 . Pattern p2 = Pattern (p1 C.. p2) - -instance Functor (Pattern u a) where - fmap f (Pattern p) = Pattern $ A.Kleisli $ fmap f . A.runKleisli p - --- | --- Run a pattern with an input and initial user state --- --- Returns Nothing if the pattern fails to match --- -pattern_ :: Pattern u a b -> u -> a -> Maybe b -pattern_ p u = flip evalStateT u . A.runKleisli (runPattern p) - --- | --- Construct a pattern from a function --- -mkPattern :: (a -> Maybe b) -> Pattern u a b -mkPattern f = Pattern $ A.Kleisli (lift . f) - --- | --- Construct a pattern from a stateful function --- -mkPattern' :: (a -> StateT u Maybe b) -> Pattern u a b -mkPattern' = Pattern . A.Kleisli - --- | --- Construct a pattern which recursively matches on the left-hand-side --- -chainl :: Pattern u a (a, a) -> (r -> r -> r) -> Pattern u a r -> Pattern u a r -chainl g f p = fix $ \c -> g >>> ((c <+> p) *** p) >>> A.arr (uncurry f) - --- | --- Construct a pattern which recursively matches on the right-hand side --- -chainr :: Pattern u a (a, a) -> (r -> r -> r) -> Pattern u a r -> Pattern u a r -chainr g f p = fix $ \c -> g >>> (p *** (c <+> p)) >>> A.arr (uncurry f) - --- | --- Construct a pattern which recursively matches on one-side of a tuple --- -wrap :: Pattern u a (s, a) -> (s -> r -> r) -> Pattern u a r -> Pattern u a r -wrap g f p = fix $ \c -> g >>> (C.id *** (c <+> p)) >>> A.arr (uncurry f) - --- | --- Construct a pattern which matches a part of a tuple --- -split :: Pattern u a (s, t) -> (s -> t -> r) -> Pattern u a r -split s f = s >>> A.arr (uncurry f) - --- | --- A table of operators --- -data OperatorTable u a r = OperatorTable { runOperatorTable :: [ [Operator u a r] ] } - --- | --- An operator: --- --- [@AssocL@] A left-associative operator --- --- [@AssocR@] A right-associative operator --- --- [@Wrap@] A prefix-like or postfix-like operator --- --- [@Split@] A prefix-like or postfix-like operator which does not recurse into its operand --- -data Operator u a r where - AssocL :: Pattern u a (a, a) -> (r -> r -> r) -> Operator u a r - AssocR :: Pattern u a (a, a) -> (r -> r -> r) -> Operator u a r - Wrap :: Pattern u a (s, a) -> (s -> r -> r) -> Operator u a r - Split :: Pattern u a (s, t) -> (s -> t -> r) -> Operator u a r - --- | --- Build a pretty printer from an operator table and an indecomposable pattern --- -buildPrettyPrinter :: OperatorTable u a r -> Pattern u a r -> Pattern u a r -buildPrettyPrinter table p = foldl (\p' ops -> foldl1 (<+>) (flip map ops $ \case - AssocL pat g -> chainl pat g p' - AssocR pat g -> chainr pat g p' - Wrap pat g -> wrap pat g p' - Split pat g -> split pat g - ) <+> p') p $ runOperatorTable table diff --git a/claude-help/original-compiler/src/Data/InternedName.hs b/claude-help/original-compiler/src/Data/InternedName.hs deleted file mode 100644 index 21236e4c..00000000 --- a/claude-help/original-compiler/src/Data/InternedName.hs +++ /dev/null @@ -1,33 +0,0 @@ -{-# LANGUAGE DerivingStrategies #-} -module Data.InternedName where - -import Prelude -import Data.Aeson (FromJSON(..), ToJSON(..)) -import Data.Interned (intern, unintern) -import Data.Interned.Text (InternedText) -import Control.DeepSeq (NFData(..)) -import Codec.Serialise (Serialise(..), encode, decode) -import Data.Text as T - -newtype InternedName = InternedName InternedText - deriving stock (Show) - deriving newtype (Eq, Ord) - -internName :: String -> InternedName -internName name = InternedName $ intern $ T.pack name - -uninternName :: InternedName -> String -uninternName (InternedName name) = T.unpack $ unintern name - -instance FromJSON InternedName where - parseJSON = fmap internName . parseJSON - -instance ToJSON InternedName where - toJSON = toJSON . uninternName - -instance Serialise InternedName where - encode = encode . uninternName - decode = fmap internName decode - -instance NFData InternedName where - rnf (InternedName _) = () diff --git a/claude-help/original-compiler/src/Data/Text/PureScript.hs b/claude-help/original-compiler/src/Data/Text/PureScript.hs deleted file mode 100644 index 65751bff..00000000 --- a/claude-help/original-compiler/src/Data/Text/PureScript.hs +++ /dev/null @@ -1,23 +0,0 @@ --- | --- This module contains internal extensions to Data.Text. --- -module Data.Text.PureScript (spanUpTo) where - -import Prelude - -import Data.Text.Internal (Text(..), text) -import Data.Text.Unsafe (Iter(..), iter) - --- | /O(n)/ 'spanUpTo', applied to a number @n@, predicate @p@, and text @t@, --- returns a pair whose first element is the longest prefix (possibly empty) of --- @t@ of length less than or equal to @n@ of elements that satisfy @p@, and --- whose second is the remainder of the text. -{-# INLINE spanUpTo #-} -spanUpTo :: Int -> (Char -> Bool) -> Text -> (Text, Text) -spanUpTo n p t@(Text arr off len) = (hd, tl) - where hd = text arr off k - tl = text arr (off + k) (len - k) - !k = loop n 0 - loop !n' !i | n' > 0 && i < len && p c = loop (n' - 1) (i + d) - | otherwise = i - where Iter c d = iter t i diff --git a/claude-help/original-compiler/src/Language/PureScript.hs b/claude-help/original-compiler/src/Language/PureScript.hs deleted file mode 100644 index f2309f35..00000000 --- a/claude-help/original-compiler/src/Language/PureScript.hs +++ /dev/null @@ -1,36 +0,0 @@ --- | --- The main compiler module --- -module Language.PureScript - ( module P - , version - ) where - - -import Control.Monad.Supply as P - -import Data.Version (Version) - -import Language.PureScript.AST as P -import Language.PureScript.Comments as P -import Language.PureScript.Crash as P -import Language.PureScript.Environment as P -import Language.PureScript.Errors as P hiding (indent) -import Language.PureScript.Externs as P -import Language.PureScript.Graph as P -import Language.PureScript.Linter as P -import Language.PureScript.Make as P -import Language.PureScript.ModuleDependencies as P -import Language.PureScript.Names as P -import Language.PureScript.Options as P -import Language.PureScript.Pretty as P -import Language.PureScript.Renamer as P -import Language.PureScript.Roles as P -import Language.PureScript.Sugar as P -import Language.PureScript.TypeChecker as P -import Language.PureScript.Types as P - -import Paths_purescript qualified as Paths - -version :: Version -version = Paths.version diff --git a/claude-help/original-compiler/src/Language/PureScript/AST.hs b/claude-help/original-compiler/src/Language/PureScript/AST.hs deleted file mode 100644 index fe82e272..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST.hs +++ /dev/null @@ -1,14 +0,0 @@ --- | --- The initial PureScript AST --- -module Language.PureScript.AST ( - module AST -) where - -import Language.PureScript.AST.Binders as AST -import Language.PureScript.AST.Declarations as AST -import Language.PureScript.AST.Exported as AST -import Language.PureScript.AST.Literals as AST -import Language.PureScript.AST.Operators as AST -import Language.PureScript.AST.SourcePos as AST -import Language.PureScript.AST.Traversals as AST diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Binders.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Binders.hs deleted file mode 100644 index 1f427755..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Binders.hs +++ /dev/null @@ -1,162 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} --- | --- Case binders --- -module Language.PureScript.AST.Binders where - -import Prelude - -import Control.DeepSeq (NFData) -import GHC.Generics (Generic) -import Language.PureScript.AST.SourcePos (SourceSpan) -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.Names (Ident, OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified) -import Language.PureScript.Comments (Comment) -import Language.PureScript.Types (SourceType) - --- | --- Data type for binders --- -data Binder - -- | - -- Wildcard binder - -- - = NullBinder - -- | - -- A binder which matches a literal - -- - | LiteralBinder SourceSpan (Literal Binder) - -- | - -- A binder which binds an identifier - -- - | VarBinder SourceSpan Ident - -- | - -- A binder which matches a data constructor - -- - | ConstructorBinder SourceSpan (Qualified (ProperName 'ConstructorName)) [Binder] - -- | - -- A operator alias binder. During the rebracketing phase of desugaring, - -- this data constructor will be removed. - -- - | OpBinder SourceSpan (Qualified (OpName 'ValueOpName)) - -- | - -- Binary operator application. During the rebracketing phase of desugaring, - -- this data constructor will be removed. - -- - | BinaryNoParensBinder Binder Binder Binder - -- | - -- Explicit parentheses. During the rebracketing phase of desugaring, this - -- data constructor will be removed. - -- - -- Note: although it seems this constructor is not used, it _is_ useful, - -- since it prevents certain traversals from matching. - -- - | ParensInBinder Binder - -- | - -- A binder which binds its input to an identifier - -- - | NamedBinder SourceSpan Ident Binder - -- | - -- A binder with source position information - -- - | PositionedBinder SourceSpan [Comment] Binder - -- | - -- A binder with a type annotation - -- - | TypedBinder SourceType Binder - deriving (Show, Generic, NFData) - --- Manual Eq and Ord instances for `Binder` were added on 2018-03-05. Comparing --- the `SourceSpan` values embedded in some of the data constructors of `Binder` --- was expensive. This made exhaustiveness checking observably slow for code --- such as the `explode` function in `test/purs/passing/LargeSumTypes.purs`. --- Custom instances were written to skip comparing the `SourceSpan` values. Only --- the `Ord` instance was needed for the speed-up, but I did not want the `Eq` --- to have mismatched behavior. -instance Eq Binder where - NullBinder == NullBinder = - True - (LiteralBinder _ lb) == (LiteralBinder _ lb') = - lb == lb' - (VarBinder _ ident) == (VarBinder _ ident') = - ident == ident' - (ConstructorBinder _ qpc bs) == (ConstructorBinder _ qpc' bs') = - qpc == qpc' && bs == bs' - (OpBinder _ qov) == (OpBinder _ qov') = - qov == qov' - (BinaryNoParensBinder b1 b2 b3) == (BinaryNoParensBinder b1' b2' b3') = - b1 == b1' && b2 == b2' && b3 == b3' - (ParensInBinder b) == (ParensInBinder b') = - b == b' - (NamedBinder _ ident b) == (NamedBinder _ ident' b') = - ident == ident' && b == b' - (PositionedBinder _ comments b) == (PositionedBinder _ comments' b') = - comments == comments' && b == b' - (TypedBinder ty b) == (TypedBinder ty' b') = - ty == ty' && b == b' - _ == _ = False - -instance Ord Binder where - compare NullBinder NullBinder = EQ - compare (LiteralBinder _ lb) (LiteralBinder _ lb') = - compare lb lb' - compare (VarBinder _ ident) (VarBinder _ ident') = - compare ident ident' - compare (ConstructorBinder _ qpc bs) (ConstructorBinder _ qpc' bs') = - compare qpc qpc' <> compare bs bs' - compare (OpBinder _ qov) (OpBinder _ qov') = - compare qov qov' - compare (BinaryNoParensBinder b1 b2 b3) (BinaryNoParensBinder b1' b2' b3') = - compare b1 b1' <> compare b2 b2' <> compare b3 b3' - compare (ParensInBinder b) (ParensInBinder b') = - compare b b' - compare (NamedBinder _ ident b) (NamedBinder _ ident' b') = - compare ident ident' <> compare b b' - compare (PositionedBinder _ comments b) (PositionedBinder _ comments' b') = - compare comments comments' <> compare b b' - compare (TypedBinder ty b) (TypedBinder ty' b') = - compare ty ty' <> compare b b' - compare binder binder' = - compare (orderOf binder) (orderOf binder') - where - orderOf :: Binder -> Int - orderOf NullBinder = 0 - orderOf LiteralBinder{} = 1 - orderOf VarBinder{} = 2 - orderOf ConstructorBinder{} = 3 - orderOf OpBinder{} = 4 - orderOf BinaryNoParensBinder{} = 5 - orderOf ParensInBinder{} = 6 - orderOf NamedBinder{} = 7 - orderOf PositionedBinder{} = 8 - orderOf TypedBinder{} = 9 - --- | --- Collect all names introduced in binders in an expression --- -binderNames :: Binder -> [Ident] -binderNames = map snd . binderNamesWithSpans - -binderNamesWithSpans :: Binder -> [(SourceSpan, Ident)] -binderNamesWithSpans = go [] - where - go ns (LiteralBinder _ b) = lit ns b - go ns (VarBinder ss name) = (ss, name) : ns - go ns (ConstructorBinder _ _ bs) = foldl go ns bs - go ns (BinaryNoParensBinder b1 b2 b3) = foldl go ns [b1, b2, b3] - go ns (ParensInBinder b) = go ns b - go ns (NamedBinder ss name b) = go ((ss, name) : ns) b - go ns (PositionedBinder _ _ b) = go ns b - go ns (TypedBinder _ b) = go ns b - go ns _ = ns - lit ns (ObjectLiteral bs) = foldl go ns (map snd bs) - lit ns (ArrayLiteral bs) = foldl go ns bs - lit ns _ = ns - - -isIrrefutable :: Binder -> Bool -isIrrefutable NullBinder = True -isIrrefutable (VarBinder _ _) = True -isIrrefutable (PositionedBinder _ _ b) = isIrrefutable b -isIrrefutable (TypedBinder _ b) = isIrrefutable b -isIrrefutable _ = False diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Declarations.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Declarations.hs deleted file mode 100644 index 7184cbb8..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Declarations.hs +++ /dev/null @@ -1,868 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE TemplateHaskell #-} - --- | --- Data types for modules and declarations --- -module Language.PureScript.AST.Declarations where - -import Prelude -import Protolude.Exceptions (hush) - -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Data.Functor.Identity (Identity(..)) - -import Data.Aeson.TH (Options(..), SumEncoding(..), defaultOptions, deriveJSON) -import Data.Map qualified as M -import Data.Text (Text) -import Data.List.NonEmpty qualified as NEL -import GHC.Generics (Generic) - -import Language.PureScript.AST.Binders (Binder) -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.AST.Operators (Fixity) -import Language.PureScript.AST.SourcePos (SourceAnn, SourceSpan) -import Language.PureScript.AST.Declarations.ChainId (ChainId) -import Language.PureScript.Types (SourceConstraint, SourceType) -import Language.PureScript.PSString (PSString) -import Language.PureScript.Label (Label) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName(..), Name(..), OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), toMaybeModuleName) -import Language.PureScript.Roles (Role) -import Language.PureScript.TypeClassDictionaries (NamedDict) -import Language.PureScript.Comments (Comment) -import Language.PureScript.Environment (DataDeclType, Environment, FunctionalDependency, NameKind) -import Language.PureScript.Constants.Prim qualified as C - --- | A map of locally-bound names in scope. -type Context = [(Ident, SourceType)] - --- | Holds the data necessary to do type directed search for typed holes -data TypeSearch - = TSBefore Environment - -- ^ An Environment captured for later consumption by type directed search - | TSAfter - -- ^ Results of applying type directed search to the previously captured - -- Environment - { tsAfterIdentifiers :: [(Qualified Text, SourceType)] - -- ^ The identifiers that fully satisfy the subsumption check - , tsAfterRecordFields :: Maybe [(Label, SourceType)] - -- ^ Record fields that are available on the first argument to the typed - -- hole - } - deriving (Show, Generic, NFData) - -onTypeSearchTypes :: (SourceType -> SourceType) -> TypeSearch -> TypeSearch -onTypeSearchTypes f = runIdentity . onTypeSearchTypesM (Identity . f) - -onTypeSearchTypesM :: (Applicative m) => (SourceType -> m SourceType) -> TypeSearch -> m TypeSearch -onTypeSearchTypesM f (TSAfter i r) = TSAfter <$> traverse (traverse f) i <*> traverse (traverse (traverse f)) r -onTypeSearchTypesM _ (TSBefore env) = pure (TSBefore env) - --- | Error message hints, providing more detailed information about failure. -data ErrorMessageHint - = ErrorUnifyingTypes SourceType SourceType - | ErrorInExpression Expr - | ErrorInModule ModuleName - | ErrorInInstance (Qualified (ProperName 'ClassName)) [SourceType] - | ErrorInSubsumption SourceType SourceType - | ErrorInRowLabel Label - | ErrorCheckingAccessor Expr PSString - | ErrorCheckingType Expr SourceType - | ErrorCheckingKind SourceType SourceType - | ErrorCheckingGuard - | ErrorInferringType Expr - | ErrorInferringKind SourceType - | ErrorInApplication Expr SourceType Expr - | ErrorInDataConstructor (ProperName 'ConstructorName) - | ErrorInTypeConstructor (ProperName 'TypeName) - | ErrorInBindingGroup (NEL.NonEmpty Ident) - | ErrorInDataBindingGroup [ProperName 'TypeName] - | ErrorInTypeSynonym (ProperName 'TypeName) - | ErrorInValueDeclaration Ident - | ErrorInTypeDeclaration Ident - | ErrorInTypeClassDeclaration (ProperName 'ClassName) - | ErrorInKindDeclaration (ProperName 'TypeName) - | ErrorInRoleDeclaration (ProperName 'TypeName) - | ErrorInForeignImport Ident - | ErrorInForeignImportData (ProperName 'TypeName) - | ErrorSolvingConstraint SourceConstraint - | MissingConstructorImportForCoercible (Qualified (ProperName 'ConstructorName)) - | PositionedError (NEL.NonEmpty SourceSpan) - | RelatedPositions (NEL.NonEmpty SourceSpan) - deriving (Show, Generic, NFData) - --- | Categories of hints -data HintCategory - = ExprHint - | KindHint - | CheckHint - | PositionHint - | SolverHint - | DeclarationHint - | OtherHint - deriving (Show, Eq) - --- | --- In constraint solving, indicates whether there were `TypeUnknown`s that prevented --- an instance from being found, and whether VTAs are required --- due to type class members not referencing all the type class --- head's type variables. -data UnknownsHint - = NoUnknowns - | Unknowns - | UnknownsWithVtaRequiringArgs (NEL.NonEmpty (Qualified Ident, [[Text]])) - deriving (Show, Generic, NFData) - --- | --- A module declaration, consisting of comments about the module, a module name, --- a list of declarations, and a list of the declarations that are --- explicitly exported. If the export list is Nothing, everything is exported. --- -data Module = Module SourceSpan [Comment] ModuleName [Declaration] (Maybe [DeclarationRef]) - deriving (Show) - --- | Return a module's name. -getModuleName :: Module -> ModuleName -getModuleName (Module _ _ name _ _) = name - --- | Return a module's source span. -getModuleSourceSpan :: Module -> SourceSpan -getModuleSourceSpan (Module ss _ _ _ _) = ss - --- | Return a module's declarations. -getModuleDeclarations :: Module -> [Declaration] -getModuleDeclarations (Module _ _ _ declarations _) = declarations - --- | --- Add an import declaration for a module if it does not already explicitly import it. --- --- Will not import an unqualified module if that module has already been imported qualified. --- (See #2197) --- -addDefaultImport :: Qualified ModuleName -> Module -> Module -addDefaultImport (Qualified toImportAs toImport) m@(Module ss coms mn decls exps) = - if isExistingImport `any` decls || mn == toImport then m - else Module ss coms mn (ImportDeclaration (ss, []) toImport Implicit toImportAs' : decls) exps - where - toImportAs' = toMaybeModuleName toImportAs - - isExistingImport (ImportDeclaration _ mn' _ as') - | mn' == toImport = - case toImportAs' of - Nothing -> True - _ -> as' == toImportAs' - isExistingImport _ = False - --- | Adds import declarations to a module for an implicit Prim import and Prim --- | qualified as Prim, as necessary. -importPrim :: Module -> Module -importPrim = - let - primModName = C.M_Prim - in - addDefaultImport (Qualified (ByModuleName primModName) primModName) - . addDefaultImport (Qualified ByNullSourcePos primModName) - -data NameSource = UserNamed | CompilerNamed - deriving (Eq, Show, Generic, NFData, Serialise) - --- | --- An item in a list of explicit imports or exports --- -data DeclarationRef - -- | - -- A type class - -- - = TypeClassRef SourceSpan (ProperName 'ClassName) - -- | - -- A type operator - -- - | TypeOpRef SourceSpan (OpName 'TypeOpName) - -- | - -- A type constructor with data constructors - -- - | TypeRef SourceSpan (ProperName 'TypeName) (Maybe [ProperName 'ConstructorName]) - -- | - -- A value - -- - | ValueRef SourceSpan Ident - -- | - -- A value-level operator - -- - | ValueOpRef SourceSpan (OpName 'ValueOpName) - -- | - -- A type class instance, created during typeclass desugaring - -- - | TypeInstanceRef SourceSpan Ident NameSource - -- | - -- A module, in its entirety - -- - | ModuleRef SourceSpan ModuleName - -- | - -- A value re-exported from another module. These will be inserted during - -- elaboration in name desugaring. - -- - | ReExportRef SourceSpan ExportSource DeclarationRef - deriving (Show, Generic, NFData, Serialise) - -instance Eq DeclarationRef where - (TypeClassRef _ name) == (TypeClassRef _ name') = name == name' - (TypeOpRef _ name) == (TypeOpRef _ name') = name == name' - (TypeRef _ name dctors) == (TypeRef _ name' dctors') = name == name' && dctors == dctors' - (ValueRef _ name) == (ValueRef _ name') = name == name' - (ValueOpRef _ name) == (ValueOpRef _ name') = name == name' - (TypeInstanceRef _ name _) == (TypeInstanceRef _ name' _) = name == name' - (ModuleRef _ name) == (ModuleRef _ name') = name == name' - (ReExportRef _ mn ref) == (ReExportRef _ mn' ref') = mn == mn' && ref == ref' - _ == _ = False - -instance Ord DeclarationRef where - TypeClassRef _ name `compare` TypeClassRef _ name' = compare name name' - TypeOpRef _ name `compare` TypeOpRef _ name' = compare name name' - TypeRef _ name dctors `compare` TypeRef _ name' dctors' = compare name name' <> compare dctors dctors' - ValueRef _ name `compare` ValueRef _ name' = compare name name' - ValueOpRef _ name `compare` ValueOpRef _ name' = compare name name' - TypeInstanceRef _ name _ `compare` TypeInstanceRef _ name' _ = compare name name' - ModuleRef _ name `compare` ModuleRef _ name' = compare name name' - ReExportRef _ mn ref `compare` ReExportRef _ mn' ref' = compare mn mn' <> compare ref ref' - compare ref ref' = - compare (orderOf ref) (orderOf ref') - where - orderOf :: DeclarationRef -> Int - orderOf TypeClassRef{} = 0 - orderOf TypeOpRef{} = 1 - orderOf TypeRef{} = 2 - orderOf ValueRef{} = 3 - orderOf ValueOpRef{} = 4 - orderOf TypeInstanceRef{} = 5 - orderOf ModuleRef{} = 6 - orderOf ReExportRef{} = 7 - -data ExportSource = - ExportSource - { exportSourceImportedFrom :: Maybe ModuleName - , exportSourceDefinedIn :: ModuleName - } - deriving (Eq, Ord, Show, Generic, NFData, Serialise) - -declRefSourceSpan :: DeclarationRef -> SourceSpan -declRefSourceSpan (TypeRef ss _ _) = ss -declRefSourceSpan (TypeOpRef ss _) = ss -declRefSourceSpan (ValueRef ss _) = ss -declRefSourceSpan (ValueOpRef ss _) = ss -declRefSourceSpan (TypeClassRef ss _) = ss -declRefSourceSpan (TypeInstanceRef ss _ _) = ss -declRefSourceSpan (ModuleRef ss _) = ss -declRefSourceSpan (ReExportRef ss _ _) = ss - -declRefName :: DeclarationRef -> Name -declRefName (TypeRef _ n _) = TyName n -declRefName (TypeOpRef _ n) = TyOpName n -declRefName (ValueRef _ n) = IdentName n -declRefName (ValueOpRef _ n) = ValOpName n -declRefName (TypeClassRef _ n) = TyClassName n -declRefName (TypeInstanceRef _ n _) = IdentName n -declRefName (ModuleRef _ n) = ModName n -declRefName (ReExportRef _ _ ref) = declRefName ref - -getTypeRef :: DeclarationRef -> Maybe (ProperName 'TypeName, Maybe [ProperName 'ConstructorName]) -getTypeRef (TypeRef _ name dctors) = Just (name, dctors) -getTypeRef _ = Nothing - -getTypeOpRef :: DeclarationRef -> Maybe (OpName 'TypeOpName) -getTypeOpRef (TypeOpRef _ op) = Just op -getTypeOpRef _ = Nothing - -getValueRef :: DeclarationRef -> Maybe Ident -getValueRef (ValueRef _ name) = Just name -getValueRef _ = Nothing - -getValueOpRef :: DeclarationRef -> Maybe (OpName 'ValueOpName) -getValueOpRef (ValueOpRef _ op) = Just op -getValueOpRef _ = Nothing - -getTypeClassRef :: DeclarationRef -> Maybe (ProperName 'ClassName) -getTypeClassRef (TypeClassRef _ name) = Just name -getTypeClassRef _ = Nothing - -isModuleRef :: DeclarationRef -> Bool -isModuleRef ModuleRef{} = True -isModuleRef _ = False - --- | --- The data type which specifies type of import declaration --- -data ImportDeclarationType - -- | - -- An import with no explicit list: `import M`. - -- - = Implicit - -- | - -- An import with an explicit list of references to import: `import M (foo)` - -- - | Explicit [DeclarationRef] - -- | - -- An import with a list of references to hide: `import M hiding (foo)` - -- - | Hiding [DeclarationRef] - deriving (Eq, Show, Generic, Serialise, NFData) - -isExplicit :: ImportDeclarationType -> Bool -isExplicit (Explicit _) = True -isExplicit _ = False - --- | A role declaration assigns a list of roles to a type constructor's --- parameters, e.g.: --- --- @type role T representational phantom@ --- --- In this example, @T@ is the identifier and @[representational, phantom]@ is --- the list of roles (@T@ presumably having two parameters). -data RoleDeclarationData = RoleDeclarationData - { rdeclSourceAnn :: !SourceAnn - , rdeclIdent :: !(ProperName 'TypeName) - , rdeclRoles :: ![Role] - } deriving (Show, Eq, Generic, NFData) - --- | A type declaration assigns a type to an identifier, eg: --- --- @identity :: forall a. a -> a@ --- --- In this example @identity@ is the identifier and @forall a. a -> a@ the type. -data TypeDeclarationData = TypeDeclarationData - { tydeclSourceAnn :: !SourceAnn - , tydeclIdent :: !Ident - , tydeclType :: !SourceType - } deriving (Show, Eq, Generic, NFData) - -getTypeDeclaration :: Declaration -> Maybe TypeDeclarationData -getTypeDeclaration (TypeDeclaration d) = Just d -getTypeDeclaration _ = Nothing - -unwrapTypeDeclaration :: TypeDeclarationData -> (Ident, SourceType) -unwrapTypeDeclaration td = (tydeclIdent td, tydeclType td) - --- | A value declaration assigns a name and potential binders, to an expression (or multiple guarded expressions). --- --- @double x = x + x@ --- --- In this example @double@ is the identifier, @x@ is a binder and @x + x@ is the expression. -data ValueDeclarationData a = ValueDeclarationData - { valdeclSourceAnn :: !SourceAnn - , valdeclIdent :: !Ident - -- ^ The declared value's name - , valdeclName :: !NameKind - -- ^ Whether or not this value is exported/visible - , valdeclBinders :: ![Binder] - , valdeclExpression :: !a - } deriving (Show, Functor, Generic, NFData, Foldable, Traversable) - -getValueDeclaration :: Declaration -> Maybe (ValueDeclarationData [GuardedExpr]) -getValueDeclaration (ValueDeclaration d) = Just d -getValueDeclaration _ = Nothing - -pattern ValueDecl :: SourceAnn -> Ident -> NameKind -> [Binder] -> [GuardedExpr] -> Declaration -pattern ValueDecl sann ident name binders expr - = ValueDeclaration (ValueDeclarationData sann ident name binders expr) - -data DataConstructorDeclaration = DataConstructorDeclaration - { dataCtorAnn :: !SourceAnn - , dataCtorName :: !(ProperName 'ConstructorName) - , dataCtorFields :: ![(Ident, SourceType)] - } deriving (Show, Eq, Generic, NFData) - -mapDataCtorFields :: ([(Ident, SourceType)] -> [(Ident, SourceType)]) -> DataConstructorDeclaration -> DataConstructorDeclaration -mapDataCtorFields f DataConstructorDeclaration{..} = DataConstructorDeclaration { dataCtorFields = f dataCtorFields, .. } - -traverseDataCtorFields :: Monad m => ([(Ident, SourceType)] -> m [(Ident, SourceType)]) -> DataConstructorDeclaration -> m DataConstructorDeclaration -traverseDataCtorFields f DataConstructorDeclaration{..} = DataConstructorDeclaration dataCtorAnn dataCtorName <$> f dataCtorFields - --- | --- The data type of declarations --- -data Declaration - -- | - -- A data type declaration (data or newtype, name, arguments, data constructors) - -- - = DataDeclaration SourceAnn DataDeclType (ProperName 'TypeName) [(Text, Maybe SourceType)] [DataConstructorDeclaration] - -- | - -- A minimal mutually recursive set of data type declarations - -- - | DataBindingGroupDeclaration (NEL.NonEmpty Declaration) - -- | - -- A type synonym declaration (name, arguments, type) - -- - | TypeSynonymDeclaration SourceAnn (ProperName 'TypeName) [(Text, Maybe SourceType)] SourceType - -- | - -- A kind signature declaration - -- - | KindDeclaration SourceAnn KindSignatureFor (ProperName 'TypeName) SourceType - -- | - -- A role declaration (name, roles) - -- - | RoleDeclaration {-# UNPACK #-} !RoleDeclarationData - -- | - -- A type declaration for a value (name, ty) - -- - | TypeDeclaration {-# UNPACK #-} !TypeDeclarationData - -- | - -- A value declaration (name, top-level binders, optional guard, value) - -- - | ValueDeclaration {-# UNPACK #-} !(ValueDeclarationData [GuardedExpr]) - -- | - -- A declaration paired with pattern matching in let-in expression (binder, optional guard, value) - | BoundValueDeclaration SourceAnn Binder Expr - -- | - -- A minimal mutually recursive set of value declarations - -- - | BindingGroupDeclaration (NEL.NonEmpty ((SourceAnn, Ident), NameKind, Expr)) - -- | - -- A foreign import declaration (name, type) - -- - | ExternDeclaration SourceAnn Ident SourceType - -- | - -- A data type foreign import (name, kind) - -- - | ExternDataDeclaration SourceAnn (ProperName 'TypeName) SourceType - -- | - -- A fixity declaration - -- - | FixityDeclaration SourceAnn (Either ValueFixity TypeFixity) - -- | - -- A module import (module name, qualified/unqualified/hiding, optional "qualified as" name) - -- - | ImportDeclaration SourceAnn ModuleName ImportDeclarationType (Maybe ModuleName) - -- | - -- A type class declaration (name, argument, implies, member declarations) - -- - | TypeClassDeclaration SourceAnn (ProperName 'ClassName) [(Text, Maybe SourceType)] [SourceConstraint] [FunctionalDependency] [Declaration] - -- | - -- A type instance declaration (instance chain, chain index, name, - -- dependencies, class name, instance types, member declarations) - -- - -- The first @SourceAnn@ serves as the annotation for the entire - -- declaration, while the second @SourceAnn@ serves as the - -- annotation for the type class and its arguments. - | TypeInstanceDeclaration SourceAnn SourceAnn ChainId Integer (Either Text Ident) [SourceConstraint] (Qualified (ProperName 'ClassName)) [SourceType] TypeInstanceBody - deriving (Show, Generic, NFData) - -data ValueFixity = ValueFixity Fixity (Qualified (Either Ident (ProperName 'ConstructorName))) (OpName 'ValueOpName) - deriving (Eq, Ord, Show, Generic, NFData) - -data TypeFixity = TypeFixity Fixity (Qualified (ProperName 'TypeName)) (OpName 'TypeOpName) - deriving (Eq, Ord, Show, Generic, NFData) - -pattern ValueFixityDeclaration :: SourceAnn -> Fixity -> Qualified (Either Ident (ProperName 'ConstructorName)) -> OpName 'ValueOpName -> Declaration -pattern ValueFixityDeclaration sa fixity name op = FixityDeclaration sa (Left (ValueFixity fixity name op)) - -pattern TypeFixityDeclaration :: SourceAnn -> Fixity -> Qualified (ProperName 'TypeName) -> OpName 'TypeOpName -> Declaration -pattern TypeFixityDeclaration sa fixity name op = FixityDeclaration sa (Right (TypeFixity fixity name op)) - -data InstanceDerivationStrategy - = KnownClassStrategy - | NewtypeStrategy - deriving (Show, Generic, NFData) - --- | The members of a type class instance declaration -data TypeInstanceBody - = DerivedInstance - -- ^ This is a derived instance - | NewtypeInstance - -- ^ This is an instance derived from a newtype - | ExplicitInstance [Declaration] - -- ^ This is a regular (explicit) instance - deriving (Show, Generic, NFData) - -mapTypeInstanceBody :: ([Declaration] -> [Declaration]) -> TypeInstanceBody -> TypeInstanceBody -mapTypeInstanceBody f = runIdentity . traverseTypeInstanceBody (Identity . f) - --- | A traversal for TypeInstanceBody -traverseTypeInstanceBody :: (Applicative f) => ([Declaration] -> f [Declaration]) -> TypeInstanceBody -> f TypeInstanceBody -traverseTypeInstanceBody f (ExplicitInstance ds) = ExplicitInstance <$> f ds -traverseTypeInstanceBody _ other = pure other - --- | What sort of declaration the kind signature applies to. -data KindSignatureFor - = DataSig - | NewtypeSig - | TypeSynonymSig - | ClassSig - deriving (Eq, Ord, Show, Generic, NFData) - -declSourceAnn :: Declaration -> SourceAnn -declSourceAnn (DataDeclaration sa _ _ _ _) = sa -declSourceAnn (DataBindingGroupDeclaration ds) = declSourceAnn (NEL.head ds) -declSourceAnn (TypeSynonymDeclaration sa _ _ _) = sa -declSourceAnn (KindDeclaration sa _ _ _) = sa -declSourceAnn (RoleDeclaration rd) = rdeclSourceAnn rd -declSourceAnn (TypeDeclaration td) = tydeclSourceAnn td -declSourceAnn (ValueDeclaration vd) = valdeclSourceAnn vd -declSourceAnn (BoundValueDeclaration sa _ _) = sa -declSourceAnn (BindingGroupDeclaration ds) = let ((sa, _), _, _) = NEL.head ds in sa -declSourceAnn (ExternDeclaration sa _ _) = sa -declSourceAnn (ExternDataDeclaration sa _ _) = sa -declSourceAnn (FixityDeclaration sa _) = sa -declSourceAnn (ImportDeclaration sa _ _ _) = sa -declSourceAnn (TypeClassDeclaration sa _ _ _ _ _) = sa -declSourceAnn (TypeInstanceDeclaration sa _ _ _ _ _ _ _ _) = sa - -declSourceSpan :: Declaration -> SourceSpan -declSourceSpan = fst . declSourceAnn - --- Note: Kind Declarations' names can refer to either a `TyClassName` --- or a `TypeName`. Use a helper function for handling `KindDeclaration`s --- specifically in the context in which it is needed. -declName :: Declaration -> Maybe Name -declName (DataDeclaration _ _ n _ _) = Just (TyName n) -declName (TypeSynonymDeclaration _ n _ _) = Just (TyName n) -declName (ValueDeclaration vd) = Just (IdentName (valdeclIdent vd)) -declName (ExternDeclaration _ n _) = Just (IdentName n) -declName (ExternDataDeclaration _ n _) = Just (TyName n) -declName (FixityDeclaration _ (Left (ValueFixity _ _ n))) = Just (ValOpName n) -declName (FixityDeclaration _ (Right (TypeFixity _ _ n))) = Just (TyOpName n) -declName (TypeClassDeclaration _ n _ _ _ _) = Just (TyClassName n) -declName (TypeInstanceDeclaration _ _ _ _ n _ _ _ _) = IdentName <$> hush n -declName (RoleDeclaration RoleDeclarationData{..}) = Just (TyName rdeclIdent) -declName ImportDeclaration{} = Nothing -declName BindingGroupDeclaration{} = Nothing -declName DataBindingGroupDeclaration{} = Nothing -declName BoundValueDeclaration{} = Nothing -declName KindDeclaration{} = Nothing -declName TypeDeclaration{} = Nothing - --- | --- Test if a declaration is a value declaration --- -isValueDecl :: Declaration -> Bool -isValueDecl ValueDeclaration{} = True -isValueDecl _ = False - --- | --- Test if a declaration is a data type declaration --- -isDataDecl :: Declaration -> Bool -isDataDecl DataDeclaration{} = True -isDataDecl _ = False - --- | --- Test if a declaration is a type synonym declaration --- -isTypeSynonymDecl :: Declaration -> Bool -isTypeSynonymDecl TypeSynonymDeclaration{} = True -isTypeSynonymDecl _ = False - --- | --- Test if a declaration is a module import --- -isImportDecl :: Declaration -> Bool -isImportDecl ImportDeclaration{} = True -isImportDecl _ = False - --- | --- Test if a declaration is a role declaration --- -isRoleDecl :: Declaration -> Bool -isRoleDecl RoleDeclaration{} = True -isRoleDecl _ = False - --- | --- Test if a declaration is a data type foreign import --- -isExternDataDecl :: Declaration -> Bool -isExternDataDecl ExternDataDeclaration{} = True -isExternDataDecl _ = False - --- | --- Test if a declaration is a fixity declaration --- -isFixityDecl :: Declaration -> Bool -isFixityDecl FixityDeclaration{} = True -isFixityDecl _ = False - -getFixityDecl :: Declaration -> Maybe (Either ValueFixity TypeFixity) -getFixityDecl (FixityDeclaration _ fixity) = Just fixity -getFixityDecl _ = Nothing - --- | --- Test if a declaration is a foreign import --- -isExternDecl :: Declaration -> Bool -isExternDecl ExternDeclaration{} = True -isExternDecl _ = False - --- | --- Test if a declaration is a type class instance declaration --- -isTypeClassInstanceDecl :: Declaration -> Bool -isTypeClassInstanceDecl TypeInstanceDeclaration{} = True -isTypeClassInstanceDecl _ = False - --- | --- Test if a declaration is a type class declaration --- -isTypeClassDecl :: Declaration -> Bool -isTypeClassDecl TypeClassDeclaration{} = True -isTypeClassDecl _ = False - --- | --- Test if a declaration is a kind signature declaration. --- -isKindDecl :: Declaration -> Bool -isKindDecl KindDeclaration{} = True -isKindDecl _ = False - --- | --- Recursively flatten data binding groups in the list of declarations -flattenDecls :: [Declaration] -> [Declaration] -flattenDecls = concatMap flattenOne - where flattenOne :: Declaration -> [Declaration] - flattenOne (DataBindingGroupDeclaration decls) = concatMap flattenOne decls - flattenOne d = [d] - --- | --- A guard is just a boolean-valued expression that appears alongside a set of binders --- -data Guard = ConditionGuard Expr - | PatternGuard Binder Expr - deriving (Show, Generic, NFData) - --- | --- The right hand side of a binder in value declarations --- and case expressions. -data GuardedExpr = GuardedExpr [Guard] Expr - deriving (Show, Generic, NFData) - -pattern MkUnguarded :: Expr -> GuardedExpr -pattern MkUnguarded e = GuardedExpr [] e - --- | --- Data type for expressions and terms --- -data Expr - -- | - -- A literal value - -- - = Literal SourceSpan (Literal Expr) - -- | - -- A prefix -, will be desugared - -- - | UnaryMinus SourceSpan Expr - -- | - -- Binary operator application. During the rebracketing phase of desugaring, this data constructor - -- will be removed. - -- - | BinaryNoParens Expr Expr Expr - -- | - -- Explicit parentheses. During the rebracketing phase of desugaring, this data constructor - -- will be removed. - -- - -- Note: although it seems this constructor is not used, it _is_ useful, since it prevents - -- certain traversals from matching. - -- - | Parens Expr - -- | - -- An record property accessor expression (e.g. `obj.x` or `_.x`). - -- Anonymous arguments will be removed during desugaring and expanded - -- into a lambda that reads a property from a record. - -- - | Accessor PSString Expr - -- | - -- Partial record update - -- - | ObjectUpdate Expr [(PSString, Expr)] - -- | - -- Object updates with nested support: `x { foo { bar = e } }` - -- Replaced during desugaring into a `Let` and nested `ObjectUpdate`s - -- - | ObjectUpdateNested Expr (PathTree Expr) - -- | - -- Function introduction - -- - | Abs Binder Expr - -- | - -- Function application - -- - | App Expr Expr - -- | - -- A type application (e.g. `f @Int`) - -- - | VisibleTypeApp Expr SourceType - -- | - -- Hint that an expression is unused. - -- This is used to ignore type class dictionaries that are necessarily empty. - -- The inner expression lets us solve subgoals before eliminating the whole expression. - -- The code gen will render this as `undefined`, regardless of what the inner expression is. - | Unused Expr - -- | - -- Variable - -- - | Var SourceSpan (Qualified Ident) - -- | - -- An operator. This will be desugared into a function during the "operators" - -- phase of desugaring. - -- - | Op SourceSpan (Qualified (OpName 'ValueOpName)) - -- | - -- Conditional (if-then-else expression) - -- - | IfThenElse Expr Expr Expr - -- | - -- A data constructor - -- - | Constructor SourceSpan (Qualified (ProperName 'ConstructorName)) - -- | - -- A case expression. During the case expansion phase of desugaring, top-level binders will get - -- desugared into case expressions, hence the need for guards and multiple binders per branch here. - -- - | Case [Expr] [CaseAlternative] - -- | - -- A value with a type annotation - -- - | TypedValue Bool Expr SourceType - -- | - -- A let binding - -- - | Let WhereProvenance [Declaration] Expr - -- | - -- A do-notation block - -- - | Do (Maybe ModuleName) [DoNotationElement] - -- | - -- An ado-notation block - -- - | Ado (Maybe ModuleName) [DoNotationElement] Expr - -- | - -- A placeholder for a type class dictionary to be inserted later. At the end of type checking, these - -- placeholders will be replaced with actual expressions representing type classes dictionaries which - -- can be evaluated at runtime. The constructor arguments represent (in order): whether or not to look - -- at superclass implementations when searching for a dictionary, the type class name and - -- instance type, and the type class dictionaries in scope. - -- - | TypeClassDictionary SourceConstraint - (M.Map QualifiedBy (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)))) - [ErrorMessageHint] - -- | - -- A placeholder for a superclass dictionary to be turned into a TypeClassDictionary during typechecking - -- - | DeferredDictionary (Qualified (ProperName 'ClassName)) [SourceType] - -- | - -- A placeholder for a type class instance to be derived during typechecking - -- - | DerivedInstancePlaceholder (Qualified (ProperName 'ClassName)) InstanceDerivationStrategy - -- | - -- A placeholder for an anonymous function argument - -- - | AnonymousArgument - -- | - -- A typed hole that will be turned into a hint/error during typechecking - -- - | Hole Text - -- | - -- A value with source position information - -- - | PositionedValue SourceSpan [Comment] Expr - deriving (Show, Generic, NFData) - --- | --- Metadata that tells where a let binding originated --- -data WhereProvenance - -- | - -- The let binding was originally a where clause - -- - = FromWhere - -- | - -- The let binding was always a let binding - -- - | FromLet - deriving (Show, Generic, NFData) - --- | --- An alternative in a case statement --- -data CaseAlternative = CaseAlternative - { -- | - -- A collection of binders with which to match the inputs - -- - caseAlternativeBinders :: [Binder] - -- | - -- The result expression or a collect of guarded expressions - -- - , caseAlternativeResult :: [GuardedExpr] - } deriving (Show, Generic, NFData) - --- | --- A statement in a do-notation block --- -data DoNotationElement - -- | - -- A monadic value without a binder - -- - = DoNotationValue Expr - -- | - -- A monadic value with a binder - -- - | DoNotationBind Binder Expr - -- | - -- A let statement, i.e. a pure value with a binder - -- - | DoNotationLet [Declaration] - -- | - -- A do notation element with source position information - -- - | PositionedDoNotationElement SourceSpan [Comment] DoNotationElement - deriving (Show, Generic, NFData) - - --- For a record update such as: --- --- x { foo = 0 --- , bar { baz = 1 --- , qux = 2 } } --- --- We represent the updates as the `PathTree`: --- --- [ ("foo", Leaf 3) --- , ("bar", Branch [ ("baz", Leaf 1) --- , ("qux", Leaf 2) ]) ] --- --- Which we then convert to an expression representing the following: --- --- let x' = x --- in x' { foo = 0 --- , bar = x'.bar { baz = 1 --- , qux = 2 } } --- --- The `let` here is required to prevent re-evaluating the object expression `x`. --- However we don't generate this when using an anonymous argument for the object. --- - -newtype PathTree t = PathTree (AssocList PSString (PathNode t)) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable) - deriving newtype NFData - -data PathNode t = Leaf t | Branch (PathTree t) - deriving (Show, Eq, Ord, Generic, NFData, Functor, Foldable, Traversable) - -newtype AssocList k t = AssocList { runAssocList :: [(k, t)] } - deriving (Show, Eq, Ord, Foldable, Functor, Traversable) - deriving newtype NFData - -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''NameSource) -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ExportSource) -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''DeclarationRef) -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ImportDeclarationType) - -isTrueExpr :: Expr -> Bool -isTrueExpr (Literal _ (BooleanLiteral True)) = True -isTrueExpr (Var _ (Qualified (ByModuleName (ModuleName "Prelude")) (Ident "otherwise"))) = True -isTrueExpr (Var _ (Qualified (ByModuleName (ModuleName "Data.Boolean")) (Ident "otherwise"))) = True -isTrueExpr (TypedValue _ e _) = isTrueExpr e -isTrueExpr (PositionedValue _ _ e) = isTrueExpr e -isTrueExpr _ = False - -isAnonymousArgument :: Expr -> Bool -isAnonymousArgument AnonymousArgument = True -isAnonymousArgument (PositionedValue _ _ e) = isAnonymousArgument e -isAnonymousArgument _ = False diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Declarations/ChainId.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Declarations/ChainId.hs deleted file mode 100644 index e55ebdb7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Declarations/ChainId.hs +++ /dev/null @@ -1,22 +0,0 @@ -module Language.PureScript.AST.Declarations.ChainId - ( ChainId(..) - , mkChainId - ) where - -import Prelude -import Language.PureScript.AST.SourcePos qualified as Pos -import Data.InternedName (InternedName, internName) -import Control.DeepSeq (NFData) -import Codec.Serialise (Serialise) -import Data.Aeson (ToJSON, FromJSON) - --- | --- For a given instance chain, stores the chain's file name and --- the starting source pos of the first instance in the chain. --- This data is used to determine which instances are part of --- the same instance chain. -newtype ChainId = ChainId (InternedName, Pos.SourcePos) - deriving (Eq, Ord, Show, NFData, Serialise, ToJSON, FromJSON) - -mkChainId :: String -> Pos.SourcePos -> ChainId -mkChainId fileName startingSourcePos = ChainId (internName fileName, startingSourcePos) diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Exported.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Exported.hs deleted file mode 100644 index 8ca960bb..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Exported.hs +++ /dev/null @@ -1,173 +0,0 @@ -module Language.PureScript.AST.Exported - ( exportedDeclarations - , isExported - ) where - -import Prelude -import Protolude (sortOn) - -import Control.Category ((>>>)) -import Control.Applicative ((<|>)) - -import Data.Maybe (mapMaybe) -import Data.Map qualified as M - -import Language.PureScript.AST.Declarations (DataConstructorDeclaration(..), Declaration(..), DeclarationRef(..), Module(..), declName, declRefName, flattenDecls) -import Language.PureScript.Types (Constraint(..), Type(..), everythingOnTypes) -import Language.PureScript.Names (ModuleName, Name(..), ProperName, ProperNameType(..), Qualified, coerceProperName, disqualify, isQualified, isQualifiedWith) - --- | --- Return a list of all declarations which are exported from a module. --- This function descends into data declarations to filter out unexported --- data constructors, and also filters out type instance declarations if --- they refer to classes or types which are not themselves exported. --- --- Note that this function assumes that the module has already had its imports --- desugared using 'Language.PureScript.Sugar.Names.desugarImports'. It will --- produce incorrect results if this is not the case - for example, type class --- instances will be incorrectly removed in some cases. --- --- The returned declarations are in the same order as they appear in the export --- list, unless there is no export list, in which case they appear in the same --- order as they do in the source file. --- --- Kind signatures declarations are also exported if their associated --- declaration is exported. -exportedDeclarations :: Module -> [Declaration] -exportedDeclarations (Module _ _ mn decls exps) = go decls - where - go = flattenDecls - >>> filter (isExported exps) - >>> map (filterDataConstructors exps) - >>> filterInstances mn exps - >>> maybe id reorder exps - --- | --- Filter out all data constructors from a declaration which are not exported. --- If the supplied declaration is not a data declaration, this function returns --- it unchanged. --- -filterDataConstructors :: Maybe [DeclarationRef] -> Declaration -> Declaration -filterDataConstructors exps (DataDeclaration sa dType tyName tyArgs dctors) = - DataDeclaration sa dType tyName tyArgs $ - filter (isDctorExported tyName exps . dataCtorName) dctors -filterDataConstructors _ other = other - --- | --- Filter out all the type instances from a list of declarations which --- reference a type or type class which is both local and not exported. --- --- Note that this function assumes that the module has already had its imports --- desugared using "Language.PureScript.Sugar.Names.desugarImports". It will --- produce incorrect results if this is not the case - for example, type class --- instances will be incorrectly removed in some cases. --- -filterInstances - :: ModuleName - -> Maybe [DeclarationRef] - -> [Declaration] - -> [Declaration] -filterInstances _ Nothing = id -filterInstances mn (Just exps) = - let refs = Left `map` mapMaybe typeClassName exps - ++ Right `map` mapMaybe typeName exps - in filter (all (visibleOutside refs) . typeInstanceConstituents) - where - -- Given a Qualified ProperName, and a list of all exported types and type - -- classes, returns whether the supplied Qualified ProperName is visible - -- outside this module. This is true if one of the following hold: - -- - -- * the name is defined in the same module and is exported, - -- * the name is defined in a different module (and must be exported from - -- that module; the code would fail to compile otherwise). - visibleOutside - :: [Either (ProperName 'ClassName) (ProperName 'TypeName)] - -> Either (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'TypeName)) - -> Bool - visibleOutside refs q - | either checkQual checkQual q = True - | otherwise = either (Left . disqualify) (Right . disqualify) q `elem` refs - - -- Check that a qualified name is qualified for a different module - checkQual :: Qualified a -> Bool - checkQual q = isQualified q && not (isQualifiedWith mn q) - - typeName :: DeclarationRef -> Maybe (ProperName 'TypeName) - typeName (TypeRef _ n _) = Just n - typeName _ = Nothing - - typeClassName :: DeclarationRef -> Maybe (ProperName 'ClassName) - typeClassName (TypeClassRef _ n) = Just n - typeClassName _ = Nothing - --- | --- Get all type and type class names referenced by a type instance declaration. --- -typeInstanceConstituents :: Declaration -> [Either (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'TypeName))] -typeInstanceConstituents (TypeInstanceDeclaration _ _ _ _ _ constraints className tys _) = - Left className : (concatMap fromConstraint constraints ++ concatMap fromType tys) - where - - fromConstraint c = Left (constraintClass c) : concatMap fromType (constraintArgs c) - fromType = everythingOnTypes (++) go - - -- Note that type synonyms are disallowed in instance declarations, so - -- we don't need to handle them here. - go (TypeConstructor _ n) = [Right n] - go (ConstrainedType _ c _) = fromConstraint c - go _ = [] - -typeInstanceConstituents _ = [] - - --- | --- Test if a declaration is exported, given a module's export list. Note that --- this function does not account for type instance declarations of --- non-exported types, or non-exported data constructors. Therefore, you should --- prefer 'exportedDeclarations' to this function, where possible. --- -isExported :: Maybe [DeclarationRef] -> Declaration -> Bool -isExported Nothing _ = True -isExported _ TypeInstanceDeclaration{} = True -isExported (Just exps) (KindDeclaration _ _ n _) = any matches exps - where - matches declRef = do - let refName = declRefName declRef - TyName n == refName || TyClassName (tyToClassName n) == refName -isExported (Just exps) decl = any matches exps - where - matches declRef = declName decl == Just (declRefName declRef) - --- | --- Test if a data constructor for a given type is exported, given a module's --- export list. Prefer 'exportedDeclarations' to this function, where possible. --- -isDctorExported :: ProperName 'TypeName -> Maybe [DeclarationRef] -> ProperName 'ConstructorName -> Bool -isDctorExported _ Nothing _ = True -isDctorExported ident (Just exps) ctor = test `any` exps - where - test (TypeRef _ ident' Nothing) = ident == ident' - test (TypeRef _ ident' (Just ctors)) = ident == ident' && ctor `elem` ctors - test _ = False - --- | --- Reorder declarations based on the order they appear in the given export --- list. --- -reorder :: [DeclarationRef] -> [Declaration] -> [Declaration] -reorder refs = - sortOn refIndex - where - refIndices = - M.fromList $ zip (map declRefName refs) [(0::Int)..] - refIndex = \case - KindDeclaration _ _ n _ -> - M.lookup (TyName n) refIndices <|> M.lookup (TyClassName (tyToClassName n)) refIndices - - decl -> declName decl >>= flip M.lookup refIndices - --- | --- Workaround to the fact that a `KindDeclaration`'s name's `ProperNameType` --- isn't the same as the corresponding `TypeClassDeclaration`'s `ProperNameType` -tyToClassName :: ProperName 'TypeName -> ProperName 'ClassName -tyToClassName = coerceProperName diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Literals.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Literals.hs deleted file mode 100644 index 05e06ab8..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Literals.hs +++ /dev/null @@ -1,41 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} --- | --- The core functional representation for literal values. --- -module Language.PureScript.AST.Literals where - -import Prelude -import Control.DeepSeq (NFData) -import GHC.Generics (Generic) -import Language.PureScript.PSString (PSString) - --- | --- Data type for literal values. Parameterised so it can be used for Exprs and --- Binders. --- -data Literal a - -- | - -- A numeric literal - -- - = NumericLiteral (Either Integer Double) - -- | - -- A string literal - -- - | StringLiteral PSString - -- | - -- A character literal - -- - | CharLiteral Char - -- | - -- A boolean literal - -- - | BooleanLiteral Bool - -- | - -- An array literal - -- - | ArrayLiteral [a] - -- | - -- An object literal - -- - | ObjectLiteral [(PSString, a)] - deriving (Eq, Ord, Show, Functor, Generic, NFData) diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Operators.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Operators.hs deleted file mode 100644 index eb217a24..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Operators.hs +++ /dev/null @@ -1,60 +0,0 @@ --- | --- Operators fixity and associativity --- -module Language.PureScript.AST.Operators where - -import Prelude - -import Codec.Serialise (Serialise) -import GHC.Generics (Generic) -import Control.DeepSeq (NFData) -import Data.Aeson ((.=)) -import Data.Aeson qualified as A - -import Language.PureScript.Crash (internalError) - --- | --- A precedence level for an infix operator --- -type Precedence = Integer - --- | --- Associativity for infix operators --- -data Associativity = Infixl | Infixr | Infix - deriving (Show, Eq, Ord, Generic) - -instance NFData Associativity -instance Serialise Associativity - -showAssoc :: Associativity -> String -showAssoc Infixl = "infixl" -showAssoc Infixr = "infixr" -showAssoc Infix = "infix" - -readAssoc :: String -> Associativity -readAssoc "infixl" = Infixl -readAssoc "infixr" = Infixr -readAssoc "infix" = Infix -readAssoc _ = internalError "readAssoc: no parse" - -instance A.ToJSON Associativity where - toJSON = A.toJSON . showAssoc - -instance A.FromJSON Associativity where - parseJSON = fmap readAssoc . A.parseJSON - --- | --- Fixity data for infix operators --- -data Fixity = Fixity Associativity Precedence - deriving (Show, Eq, Ord, Generic) - -instance NFData Fixity -instance Serialise Fixity - -instance A.ToJSON Fixity where - toJSON (Fixity associativity precedence) = - A.object [ "associativity" .= associativity - , "precedence" .= precedence - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/SourcePos.hs b/claude-help/original-compiler/src/Language/PureScript/AST/SourcePos.hs deleted file mode 100644 index 262d44b6..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/SourcePos.hs +++ /dev/null @@ -1,118 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} --- | --- Source position information --- -module Language.PureScript.AST.SourcePos where - -import Prelude - -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Data.Aeson ((.=), (.:)) -import Data.Text (Text) -import GHC.Generics (Generic) -import Language.PureScript.Comments (Comment) -import Data.Aeson qualified as A -import Data.Text qualified as T -import System.FilePath (makeRelative) - --- | Source annotation - position information and comments. -type SourceAnn = (SourceSpan, [Comment]) - --- | Source position information -data SourcePos = SourcePos - { sourcePosLine :: Int - -- ^ Line number - , sourcePosColumn :: Int - -- ^ Column number - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -displaySourcePos :: SourcePos -> Text -displaySourcePos sp = - "line " <> T.pack (show (sourcePosLine sp)) <> - ", column " <> T.pack (show (sourcePosColumn sp)) - -displaySourcePosShort :: SourcePos -> Text -displaySourcePosShort sp = - T.pack (show (sourcePosLine sp)) <> - ":" <> T.pack (show (sourcePosColumn sp)) - -instance A.ToJSON SourcePos where - toJSON SourcePos{..} = - A.toJSON [sourcePosLine, sourcePosColumn] - -instance A.FromJSON SourcePos where - parseJSON arr = do - [line, col] <- A.parseJSON arr - return $ SourcePos line col - -data SourceSpan = SourceSpan - { spanName :: String - -- ^ Source name - , spanStart :: SourcePos - -- ^ Start of the span - , spanEnd :: SourcePos - -- ^ End of the span - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -displayStartEndPos :: SourceSpan -> Text -displayStartEndPos sp = - "(" <> - displaySourcePos (spanStart sp) <> " - " <> - displaySourcePos (spanEnd sp) <> ")" - -displayStartEndPosShort :: SourceSpan -> Text -displayStartEndPosShort sp = - displaySourcePosShort (spanStart sp) <> " - " <> - displaySourcePosShort (spanEnd sp) - -displaySourceSpan :: FilePath -> SourceSpan -> Text -displaySourceSpan relPath sp = - T.pack (makeRelative relPath (spanName sp)) <> ":" <> - displayStartEndPosShort sp <> " " <> - displayStartEndPos sp - -instance A.ToJSON SourceSpan where - toJSON SourceSpan{..} = - A.object [ "name" .= spanName - , "start" .= spanStart - , "end" .= spanEnd - ] - -instance A.FromJSON SourceSpan where - parseJSON = A.withObject "SourceSpan" $ \o -> - SourceSpan <$> - o .: "name" <*> - o .: "start" <*> - o .: "end" - -internalModuleSourceSpan :: String -> SourceSpan -internalModuleSourceSpan name = SourceSpan name (SourcePos 0 0) (SourcePos 0 0) - -nullSourceSpan :: SourceSpan -nullSourceSpan = internalModuleSourceSpan "" - -nullSourceAnn :: SourceAnn -nullSourceAnn = (nullSourceSpan, []) - -pattern NullSourceSpan :: SourceSpan -pattern NullSourceSpan = SourceSpan "" (SourcePos 0 0) (SourcePos 0 0) - -pattern NullSourceAnn :: SourceAnn -pattern NullSourceAnn = (NullSourceSpan, []) - -nonEmptySpan :: SourceAnn -> Maybe SourceSpan -nonEmptySpan (NullSourceSpan, _) = Nothing -nonEmptySpan (ss, _) = Just ss - -widenSourceSpan :: SourceSpan -> SourceSpan -> SourceSpan -widenSourceSpan NullSourceSpan b = b -widenSourceSpan a NullSourceSpan = a -widenSourceSpan (SourceSpan n1 s1 e1) (SourceSpan n2 s2 e2) = - SourceSpan n (min s1 s2) (max e1 e2) - where - n | n1 == "" = n2 - | otherwise = n1 - -widenSourceAnn :: SourceAnn -> SourceAnn -> SourceAnn -widenSourceAnn (s1, _) (s2, _) = (widenSourceSpan s1 s2, []) diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Traversals.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Traversals.hs deleted file mode 100644 index abbe6e5a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Traversals.hs +++ /dev/null @@ -1,721 +0,0 @@ --- | --- AST traversal helpers --- -module Language.PureScript.AST.Traversals where - -import Prelude -import Protolude (swap) - -import Control.Monad ((<=<), (>=>)) -import Control.Monad.Trans.State (StateT(..)) - -import Data.Foldable (fold) -import Data.Functor.Identity (runIdentity) -import Data.List (mapAccumL) -import Data.Maybe (mapMaybe) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.AST.Binders (Binder(..), binderNames) -import Language.PureScript.AST.Declarations (CaseAlternative(..), DataConstructorDeclaration(..), Declaration(..), DoNotationElement(..), Expr(..), Guard(..), GuardedExpr(..), TypeDeclarationData(..), TypeInstanceBody(..), pattern ValueDecl, ValueDeclarationData(..), mapTypeInstanceBody, traverseTypeInstanceBody) -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident) -import Language.PureScript.Traversals (sndM, sndM', thirdM) -import Language.PureScript.TypeClassDictionaries (TypeClassDictionaryInScope(..)) -import Language.PureScript.Types (Constraint(..), SourceType, mapConstraintArgs) - -guardedExprM :: Applicative m - => (Guard -> m Guard) - -> (Expr -> m Expr) - -> GuardedExpr - -> m GuardedExpr -guardedExprM f g (GuardedExpr guards rhs) = - GuardedExpr <$> traverse f guards <*> g rhs - -mapGuardedExpr :: (Guard -> Guard) - -> (Expr -> Expr) - -> GuardedExpr - -> GuardedExpr -mapGuardedExpr f g (GuardedExpr guards rhs) = - GuardedExpr (fmap f guards) (g rhs) - -litM :: Monad m => (a -> m a) -> Literal a -> m (Literal a) -litM go (ObjectLiteral as) = ObjectLiteral <$> traverse (sndM go) as -litM go (ArrayLiteral as) = ArrayLiteral <$> traverse go as -litM _ other = pure other - -everywhereOnValues - :: (Declaration -> Declaration) - -> (Expr -> Expr) - -> (Binder -> Binder) - -> ( Declaration -> Declaration - , Expr -> Expr - , Binder -> Binder - ) -everywhereOnValues f g h = (f', g', h') - where - f' :: Declaration -> Declaration - f' (DataBindingGroupDeclaration ds) = f (DataBindingGroupDeclaration (fmap f' ds)) - f' (ValueDecl sa name nameKind bs val) = - f (ValueDecl sa name nameKind (fmap h' bs) (fmap (mapGuardedExpr handleGuard g') val)) - f' (BoundValueDeclaration sa b expr) = f (BoundValueDeclaration sa (h' b) (g' expr)) - f' (BindingGroupDeclaration ds) = f (BindingGroupDeclaration (fmap (\(name, nameKind, val) -> (name, nameKind, g' val)) ds)) - f' (TypeClassDeclaration sa name args implies deps ds) = f (TypeClassDeclaration sa name args implies deps (fmap f' ds)) - f' (TypeInstanceDeclaration sa na ch idx name cs className args ds) = f (TypeInstanceDeclaration sa na ch idx name cs className args (mapTypeInstanceBody (fmap f') ds)) - f' other = f other - - g' :: Expr -> Expr - g' (Literal ss l) = g (Literal ss (lit g' l)) - g' (UnaryMinus ss v) = g (UnaryMinus ss (g' v)) - g' (BinaryNoParens op v1 v2) = g (BinaryNoParens (g' op) (g' v1) (g' v2)) - g' (Parens v) = g (Parens (g' v)) - g' (Accessor prop v) = g (Accessor prop (g' v)) - g' (ObjectUpdate obj vs) = g (ObjectUpdate (g' obj) (fmap (fmap g') vs)) - g' (ObjectUpdateNested obj vs) = g (ObjectUpdateNested (g' obj) (fmap g' vs)) - g' (Abs binder v) = g (Abs (h' binder) (g' v)) - g' (App v1 v2) = g (App (g' v1) (g' v2)) - g' (VisibleTypeApp v ty) = g (VisibleTypeApp (g' v) ty) - g' (Unused v) = g (Unused (g' v)) - g' (IfThenElse v1 v2 v3) = g (IfThenElse (g' v1) (g' v2) (g' v3)) - g' (Case vs alts) = g (Case (fmap g' vs) (fmap handleCaseAlternative alts)) - g' (TypedValue check v ty) = g (TypedValue check (g' v) ty) - g' (Let w ds v) = g (Let w (fmap f' ds) (g' v)) - g' (Do m es) = g (Do m (fmap handleDoNotationElement es)) - g' (Ado m es v) = g (Ado m (fmap handleDoNotationElement es) (g' v)) - g' (PositionedValue pos com v) = g (PositionedValue pos com (g' v)) - g' other = g other - - h' :: Binder -> Binder - h' (ConstructorBinder ss ctor bs) = h (ConstructorBinder ss ctor (fmap h' bs)) - h' (BinaryNoParensBinder b1 b2 b3) = h (BinaryNoParensBinder (h' b1) (h' b2) (h' b3)) - h' (ParensInBinder b) = h (ParensInBinder (h' b)) - h' (LiteralBinder ss l) = h (LiteralBinder ss (lit h' l)) - h' (NamedBinder ss name b) = h (NamedBinder ss name (h' b)) - h' (PositionedBinder pos com b) = h (PositionedBinder pos com (h' b)) - h' (TypedBinder t b) = h (TypedBinder t (h' b)) - h' other = h other - - lit :: (a -> a) -> Literal a -> Literal a - lit go (ArrayLiteral as) = ArrayLiteral (fmap go as) - lit go (ObjectLiteral as) = ObjectLiteral (fmap (fmap go) as) - lit _ other = other - - handleCaseAlternative :: CaseAlternative -> CaseAlternative - handleCaseAlternative ca = - ca { caseAlternativeBinders = fmap h' (caseAlternativeBinders ca) - , caseAlternativeResult = fmap (mapGuardedExpr handleGuard g') (caseAlternativeResult ca) - } - - handleDoNotationElement :: DoNotationElement -> DoNotationElement - handleDoNotationElement (DoNotationValue v) = DoNotationValue (g' v) - handleDoNotationElement (DoNotationBind b v) = DoNotationBind (h' b) (g' v) - handleDoNotationElement (DoNotationLet ds) = DoNotationLet (fmap f' ds) - handleDoNotationElement (PositionedDoNotationElement pos com e) = PositionedDoNotationElement pos com (handleDoNotationElement e) - - handleGuard :: Guard -> Guard - handleGuard (ConditionGuard e) = ConditionGuard (g' e) - handleGuard (PatternGuard b e) = PatternGuard (h' b) (g' e) - -everywhereOnValuesTopDownM - :: forall m - . (Monad m) - => (Declaration -> m Declaration) - -> (Expr -> m Expr) - -> (Binder -> m Binder) - -> ( Declaration -> m Declaration - , Expr -> m Expr - , Binder -> m Binder - ) -everywhereOnValuesTopDownM f g h = (f' <=< f, g' <=< g, h' <=< h) - where - - f' :: Declaration -> m Declaration - f' (DataBindingGroupDeclaration ds) = DataBindingGroupDeclaration <$> traverse (f' <=< f) ds - f' (ValueDecl sa name nameKind bs val) = - ValueDecl sa name nameKind <$> traverse (h' <=< h) bs <*> traverse (guardedExprM handleGuard (g' <=< g)) val - f' (BindingGroupDeclaration ds) = BindingGroupDeclaration <$> traverse (\(name, nameKind, val) -> (name, nameKind, ) <$> (g val >>= g')) ds - f' (TypeClassDeclaration sa name args implies deps ds) = TypeClassDeclaration sa name args implies deps <$> traverse (f' <=< f) ds - f' (TypeInstanceDeclaration sa na ch idx name cs className args ds) = TypeInstanceDeclaration sa na ch idx name cs className args <$> traverseTypeInstanceBody (traverse (f' <=< f)) ds - f' (BoundValueDeclaration sa b expr) = BoundValueDeclaration sa <$> (h' <=< h) b <*> (g' <=< g) expr - f' other = f other - - g' :: Expr -> m Expr - g' (Literal ss l) = Literal ss <$> litM (g >=> g') l - g' (UnaryMinus ss v) = UnaryMinus ss <$> (g v >>= g') - g' (BinaryNoParens op v1 v2) = BinaryNoParens <$> (g op >>= g') <*> (g v1 >>= g') <*> (g v2 >>= g') - g' (Parens v) = Parens <$> (g v >>= g') - g' (Accessor prop v) = Accessor prop <$> (g v >>= g') - g' (ObjectUpdate obj vs) = ObjectUpdate <$> (g obj >>= g') <*> traverse (sndM (g' <=< g)) vs - g' (ObjectUpdateNested obj vs) = ObjectUpdateNested <$> (g obj >>= g') <*> traverse (g' <=< g) vs - g' (Abs binder v) = Abs <$> (h binder >>= h') <*> (g v >>= g') - g' (App v1 v2) = App <$> (g v1 >>= g') <*> (g v2 >>= g') - g' (VisibleTypeApp v ty) = VisibleTypeApp <$> (g v >>= g') <*> pure ty - g' (Unused v) = Unused <$> (g v >>= g') - g' (IfThenElse v1 v2 v3) = IfThenElse <$> (g v1 >>= g') <*> (g v2 >>= g') <*> (g v3 >>= g') - g' (Case vs alts) = Case <$> traverse (g' <=< g) vs <*> traverse handleCaseAlternative alts - g' (TypedValue check v ty) = TypedValue check <$> (g v >>= g') <*> pure ty - g' (Let w ds v) = Let w <$> traverse (f' <=< f) ds <*> (g v >>= g') - g' (Do m es) = Do m <$> traverse handleDoNotationElement es - g' (Ado m es v) = Ado m <$> traverse handleDoNotationElement es <*> (g v >>= g') - g' (PositionedValue pos com v) = PositionedValue pos com <$> (g v >>= g') - g' other = g other - - h' :: Binder -> m Binder - h' (LiteralBinder ss l) = LiteralBinder ss <$> litM (h >=> h') l - h' (ConstructorBinder ss ctor bs) = ConstructorBinder ss ctor <$> traverse (h' <=< h) bs - h' (BinaryNoParensBinder b1 b2 b3) = BinaryNoParensBinder <$> (h b1 >>= h') <*> (h b2 >>= h') <*> (h b3 >>= h') - h' (ParensInBinder b) = ParensInBinder <$> (h b >>= h') - h' (NamedBinder ss name b) = NamedBinder ss name <$> (h b >>= h') - h' (PositionedBinder pos com b) = PositionedBinder pos com <$> (h b >>= h') - h' (TypedBinder t b) = TypedBinder t <$> (h b >>= h') - h' other = h other - - handleCaseAlternative :: CaseAlternative -> m CaseAlternative - handleCaseAlternative (CaseAlternative bs val) = - CaseAlternative - <$> traverse (h' <=< h) bs - <*> traverse (guardedExprM handleGuard (g' <=< g)) val - - handleDoNotationElement :: DoNotationElement -> m DoNotationElement - handleDoNotationElement (DoNotationValue v) = DoNotationValue <$> (g' <=< g) v - handleDoNotationElement (DoNotationBind b v) = DoNotationBind <$> (h' <=< h) b <*> (g' <=< g) v - handleDoNotationElement (DoNotationLet ds) = DoNotationLet <$> traverse (f' <=< f) ds - handleDoNotationElement (PositionedDoNotationElement pos com e) = PositionedDoNotationElement pos com <$> handleDoNotationElement e - - handleGuard :: Guard -> m Guard - handleGuard (ConditionGuard e) = ConditionGuard <$> (g' <=< g) e - handleGuard (PatternGuard b e) = PatternGuard <$> (h' <=< h) b <*> (g' <=< g) e - -everywhereOnValuesM - :: forall m - . (Monad m) - => (Declaration -> m Declaration) - -> (Expr -> m Expr) - -> (Binder -> m Binder) - -> ( Declaration -> m Declaration - , Expr -> m Expr - , Binder -> m Binder - ) -everywhereOnValuesM f g h = (f', g', h') - where - - f' :: Declaration -> m Declaration - f' (DataBindingGroupDeclaration ds) = (DataBindingGroupDeclaration <$> traverse f' ds) >>= f - f' (ValueDecl sa name nameKind bs val) = - ValueDecl sa name nameKind <$> traverse h' bs <*> traverse (guardedExprM handleGuard g') val >>= f - f' (BindingGroupDeclaration ds) = (BindingGroupDeclaration <$> traverse (\(name, nameKind, val) -> (name, nameKind, ) <$> g' val) ds) >>= f - f' (BoundValueDeclaration sa b expr) = (BoundValueDeclaration sa <$> h' b <*> g' expr) >>= f - f' (TypeClassDeclaration sa name args implies deps ds) = (TypeClassDeclaration sa name args implies deps <$> traverse f' ds) >>= f - f' (TypeInstanceDeclaration sa na ch idx name cs className args ds) = (TypeInstanceDeclaration sa na ch idx name cs className args <$> traverseTypeInstanceBody (traverse f') ds) >>= f - f' other = f other - - g' :: Expr -> m Expr - g' (Literal ss l) = (Literal ss <$> litM g' l) >>= g - g' (UnaryMinus ss v) = (UnaryMinus ss <$> g' v) >>= g - g' (BinaryNoParens op v1 v2) = (BinaryNoParens <$> g' op <*> g' v1 <*> g' v2) >>= g - g' (Parens v) = (Parens <$> g' v) >>= g - g' (Accessor prop v) = (Accessor prop <$> g' v) >>= g - g' (ObjectUpdate obj vs) = (ObjectUpdate <$> g' obj <*> traverse (sndM g') vs) >>= g - g' (ObjectUpdateNested obj vs) = (ObjectUpdateNested <$> g' obj <*> traverse g' vs) >>= g - g' (Abs binder v) = (Abs <$> h' binder <*> g' v) >>= g - g' (App v1 v2) = (App <$> g' v1 <*> g' v2) >>= g - g' (VisibleTypeApp v ty) = (VisibleTypeApp <$> g' v <*> pure ty) >>= g - g' (Unused v) = (Unused <$> g' v) >>= g - g' (IfThenElse v1 v2 v3) = (IfThenElse <$> g' v1 <*> g' v2 <*> g' v3) >>= g - g' (Case vs alts) = (Case <$> traverse g' vs <*> traverse handleCaseAlternative alts) >>= g - g' (TypedValue check v ty) = (TypedValue check <$> g' v <*> pure ty) >>= g - g' (Let w ds v) = (Let w <$> traverse f' ds <*> g' v) >>= g - g' (Do m es) = (Do m <$> traverse handleDoNotationElement es) >>= g - g' (Ado m es v) = (Ado m <$> traverse handleDoNotationElement es <*> g' v) >>= g - g' (PositionedValue pos com v) = (PositionedValue pos com <$> g' v) >>= g - g' other = g other - - h' :: Binder -> m Binder - h' (LiteralBinder ss l) = (LiteralBinder ss <$> litM h' l) >>= h - h' (ConstructorBinder ss ctor bs) = (ConstructorBinder ss ctor <$> traverse h' bs) >>= h - h' (BinaryNoParensBinder b1 b2 b3) = (BinaryNoParensBinder <$> h' b1 <*> h' b2 <*> h' b3) >>= h - h' (ParensInBinder b) = (ParensInBinder <$> h' b) >>= h - h' (NamedBinder ss name b) = (NamedBinder ss name <$> h' b) >>= h - h' (PositionedBinder pos com b) = (PositionedBinder pos com <$> h' b) >>= h - h' (TypedBinder t b) = (TypedBinder t <$> h' b) >>= h - h' other = h other - - handleCaseAlternative :: CaseAlternative -> m CaseAlternative - handleCaseAlternative (CaseAlternative bs val) = - CaseAlternative - <$> traverse h' bs - <*> traverse (guardedExprM handleGuard g') val - - handleDoNotationElement :: DoNotationElement -> m DoNotationElement - handleDoNotationElement (DoNotationValue v) = DoNotationValue <$> g' v - handleDoNotationElement (DoNotationBind b v) = DoNotationBind <$> h' b <*> g' v - handleDoNotationElement (DoNotationLet ds) = DoNotationLet <$> traverse f' ds - handleDoNotationElement (PositionedDoNotationElement pos com e) = PositionedDoNotationElement pos com <$> handleDoNotationElement e - - handleGuard :: Guard -> m Guard - handleGuard (ConditionGuard e) = ConditionGuard <$> g' e - handleGuard (PatternGuard b e) = PatternGuard <$> h' b <*> g' e - -everythingOnValues - :: forall r - . (r -> r -> r) - -> (Declaration -> r) - -> (Expr -> r) - -> (Binder -> r) - -> (CaseAlternative -> r) - -> (DoNotationElement -> r) - -> ( Declaration -> r - , Expr -> r - , Binder -> r - , CaseAlternative -> r - , DoNotationElement -> r - ) -everythingOnValues (<>.) f g h i j = (f', g', h', i', j') - where - - f' :: Declaration -> r - f' d@(DataBindingGroupDeclaration ds) = foldl (<>.) (f d) (fmap f' ds) - f' d@(ValueDeclaration vd) = foldl (<>.) (f d) (fmap h' (valdeclBinders vd) ++ concatMap (\(GuardedExpr grd v) -> fmap k' grd ++ [g' v]) (valdeclExpression vd)) - f' d@(BindingGroupDeclaration ds) = foldl (<>.) (f d) (fmap (\(_, _, val) -> g' val) ds) - f' d@(TypeClassDeclaration _ _ _ _ _ ds) = foldl (<>.) (f d) (fmap f' ds) - f' d@(TypeInstanceDeclaration _ _ _ _ _ _ _ _ (ExplicitInstance ds)) = foldl (<>.) (f d) (fmap f' ds) - f' d@(BoundValueDeclaration _ b expr) = f d <>. h' b <>. g' expr - f' d = f d - - g' :: Expr -> r - g' v@(Literal _ l) = lit (g v) g' l - g' v@(UnaryMinus _ v1) = g v <>. g' v1 - g' v@(BinaryNoParens op v1 v2) = g v <>. g' op <>. g' v1 <>. g' v2 - g' v@(Parens v1) = g v <>. g' v1 - g' v@(Accessor _ v1) = g v <>. g' v1 - g' v@(ObjectUpdate obj vs) = foldl (<>.) (g v <>. g' obj) (fmap (g' . snd) vs) - g' v@(ObjectUpdateNested obj vs) = foldl (<>.) (g v <>. g' obj) (fmap g' vs) - g' v@(Abs b v1) = g v <>. h' b <>. g' v1 - g' v@(App v1 v2) = g v <>. g' v1 <>. g' v2 - g' v@(VisibleTypeApp v' _) = g v <>. g' v' - g' v@(Unused v1) = g v <>. g' v1 - g' v@(IfThenElse v1 v2 v3) = g v <>. g' v1 <>. g' v2 <>. g' v3 - g' v@(Case vs alts) = foldl (<>.) (foldl (<>.) (g v) (fmap g' vs)) (fmap i' alts) - g' v@(TypedValue _ v1 _) = g v <>. g' v1 - g' v@(Let _ ds v1) = foldl (<>.) (g v) (fmap f' ds) <>. g' v1 - g' v@(Do _ es) = foldl (<>.) (g v) (fmap j' es) - g' v@(Ado _ es v1) = foldl (<>.) (g v) (fmap j' es) <>. g' v1 - g' v@(PositionedValue _ _ v1) = g v <>. g' v1 - g' v = g v - - h' :: Binder -> r - h' b@(LiteralBinder _ l) = lit (h b) h' l - h' b@(ConstructorBinder _ _ bs) = foldl (<>.) (h b) (fmap h' bs) - h' b@(BinaryNoParensBinder b1 b2 b3) = h b <>. h' b1 <>. h' b2 <>. h' b3 - h' b@(ParensInBinder b1) = h b <>. h' b1 - h' b@(NamedBinder _ _ b1) = h b <>. h' b1 - h' b@(PositionedBinder _ _ b1) = h b <>. h' b1 - h' b@(TypedBinder _ b1) = h b <>. h' b1 - h' b = h b - - lit :: r -> (a -> r) -> Literal a -> r - lit r go (ArrayLiteral as) = foldl (<>.) r (fmap go as) - lit r go (ObjectLiteral as) = foldl (<>.) r (fmap (go . snd) as) - lit r _ _ = r - - i' :: CaseAlternative -> r - i' ca@(CaseAlternative bs gs) = - foldl (<>.) (i ca) (fmap h' bs ++ concatMap (\(GuardedExpr grd val) -> fmap k' grd ++ [g' val]) gs) - - j' :: DoNotationElement -> r - j' e@(DoNotationValue v) = j e <>. g' v - j' e@(DoNotationBind b v) = j e <>. h' b <>. g' v - j' e@(DoNotationLet ds) = foldl (<>.) (j e) (fmap f' ds) - j' e@(PositionedDoNotationElement _ _ e1) = j e <>. j' e1 - - k' :: Guard -> r - k' (ConditionGuard e) = g' e - k' (PatternGuard b e) = h' b <>. g' e - -everythingWithContextOnValues - :: forall s r - . s - -> r - -> (r -> r -> r) - -> (s -> Declaration -> (s, r)) - -> (s -> Expr -> (s, r)) - -> (s -> Binder -> (s, r)) - -> (s -> CaseAlternative -> (s, r)) - -> (s -> DoNotationElement -> (s, r)) - -> ( Declaration -> r - , Expr -> r - , Binder -> r - , CaseAlternative -> r - , DoNotationElement -> r) -everythingWithContextOnValues s0 r0 (<>.) f g h i j = (f'' s0, g'' s0, h'' s0, i'' s0, j'' s0) - where - - f'' :: s -> Declaration -> r - f'' s d = let (s', r) = f s d in r <>. f' s' d - - f' :: s -> Declaration -> r - f' s (DataBindingGroupDeclaration ds) = foldl (<>.) r0 (fmap (f'' s) ds) - f' s (ValueDeclaration vd) = foldl (<>.) r0 (fmap (h'' s) (valdeclBinders vd) ++ concatMap (\(GuardedExpr grd v) -> fmap (k' s) grd ++ [g'' s v]) (valdeclExpression vd)) - f' s (BindingGroupDeclaration ds) = foldl (<>.) r0 (fmap (\(_, _, val) -> g'' s val) ds) - f' s (TypeClassDeclaration _ _ _ _ _ ds) = foldl (<>.) r0 (fmap (f'' s) ds) - f' s (TypeInstanceDeclaration _ _ _ _ _ _ _ _ (ExplicitInstance ds)) = foldl (<>.) r0 (fmap (f'' s) ds) - f' _ _ = r0 - - g'' :: s -> Expr -> r - g'' s v = let (s', r) = g s v in r <>. g' s' v - - g' :: s -> Expr -> r - g' s (Literal _ l) = lit g'' s l - g' s (UnaryMinus _ v1) = g'' s v1 - g' s (BinaryNoParens op v1 v2) = g'' s op <>. g'' s v1 <>. g'' s v2 - g' s (Parens v1) = g'' s v1 - g' s (Accessor _ v1) = g'' s v1 - g' s (ObjectUpdate obj vs) = foldl (<>.) (g'' s obj) (fmap (g'' s . snd) vs) - g' s (ObjectUpdateNested obj vs) = foldl (<>.) (g'' s obj) (fmap (g'' s) vs) - g' s (Abs binder v1) = h'' s binder <>. g'' s v1 - g' s (App v1 v2) = g'' s v1 <>. g'' s v2 - g' s (VisibleTypeApp v _) = g'' s v - g' s (Unused v) = g'' s v - g' s (IfThenElse v1 v2 v3) = g'' s v1 <>. g'' s v2 <>. g'' s v3 - g' s (Case vs alts) = foldl (<>.) (foldl (<>.) r0 (fmap (g'' s) vs)) (fmap (i'' s) alts) - g' s (TypedValue _ v1 _) = g'' s v1 - g' s (Let _ ds v1) = foldl (<>.) r0 (fmap (f'' s) ds) <>. g'' s v1 - g' s (Do _ es) = foldl (<>.) r0 (fmap (j'' s) es) - g' s (Ado _ es v1) = foldl (<>.) r0 (fmap (j'' s) es) <>. g'' s v1 - g' s (PositionedValue _ _ v1) = g'' s v1 - g' _ _ = r0 - - h'' :: s -> Binder -> r - h'' s b = let (s', r) = h s b in r <>. h' s' b - - h' :: s -> Binder -> r - h' s (LiteralBinder _ l) = lit h'' s l - h' s (ConstructorBinder _ _ bs) = foldl (<>.) r0 (fmap (h'' s) bs) - h' s (BinaryNoParensBinder b1 b2 b3) = h'' s b1 <>. h'' s b2 <>. h'' s b3 - h' s (ParensInBinder b) = h'' s b - h' s (NamedBinder _ _ b1) = h'' s b1 - h' s (PositionedBinder _ _ b1) = h'' s b1 - h' s (TypedBinder _ b1) = h'' s b1 - h' _ _ = r0 - - lit :: (s -> a -> r) -> s -> Literal a -> r - lit go s (ArrayLiteral as) = foldl (<>.) r0 (fmap (go s) as) - lit go s (ObjectLiteral as) = foldl (<>.) r0 (fmap (go s . snd) as) - lit _ _ _ = r0 - - i'' :: s -> CaseAlternative -> r - i'' s ca = let (s', r) = i s ca in r <>. i' s' ca - - i' :: s -> CaseAlternative -> r - i' s (CaseAlternative bs gs) = foldl (<>.) r0 (fmap (h'' s) bs ++ concatMap (\(GuardedExpr grd val) -> fmap (k' s) grd ++ [g'' s val]) gs) - - j'' :: s -> DoNotationElement -> r - j'' s e = let (s', r) = j s e in r <>. j' s' e - - j' :: s -> DoNotationElement -> r - j' s (DoNotationValue v) = g'' s v - j' s (DoNotationBind b v) = h'' s b <>. g'' s v - j' s (DoNotationLet ds) = foldl (<>.) r0 (fmap (f'' s) ds) - j' s (PositionedDoNotationElement _ _ e1) = j'' s e1 - - k' :: s -> Guard -> r - k' s (ConditionGuard e) = g'' s e - k' s (PatternGuard b e) = h'' s b <>. g'' s e - -everywhereWithContextOnValues - :: forall s - . s - -> (s -> Declaration -> (s, Declaration)) - -> (s -> Expr -> (s, Expr)) - -> (s -> Binder -> (s, Binder)) - -> (s -> CaseAlternative -> (s, CaseAlternative)) - -> (s -> DoNotationElement -> (s, DoNotationElement)) - -> (s -> Guard -> (s, Guard)) - -> ( Declaration -> Declaration - , Expr -> Expr - , Binder -> Binder - , CaseAlternative -> CaseAlternative - , DoNotationElement -> DoNotationElement - , Guard -> Guard - ) -everywhereWithContextOnValues s f g h i j k = (runIdentity . f', runIdentity . g', runIdentity . h', runIdentity . i', runIdentity . j', runIdentity . k') - where - (f', g', h', i', j', k') = everywhereWithContextOnValuesM s (wrap f) (wrap g) (wrap h) (wrap i) (wrap j) (wrap k) - wrap = ((pure .) .) - -everywhereWithContextOnValuesM - :: forall m s - . (Monad m) - => s - -> (s -> Declaration -> m (s, Declaration)) - -> (s -> Expr -> m (s, Expr)) - -> (s -> Binder -> m (s, Binder)) - -> (s -> CaseAlternative -> m (s, CaseAlternative)) - -> (s -> DoNotationElement -> m (s, DoNotationElement)) - -> (s -> Guard -> m (s, Guard)) - -> ( Declaration -> m Declaration - , Expr -> m Expr - , Binder -> m Binder - , CaseAlternative -> m CaseAlternative - , DoNotationElement -> m DoNotationElement - , Guard -> m Guard - ) -everywhereWithContextOnValuesM s0 f g h i j k = (f'' s0, g'' s0, h'' s0, i'' s0, j'' s0, k'' s0) - where - f'' s = uncurry f' <=< f s - - f' s (DataBindingGroupDeclaration ds) = DataBindingGroupDeclaration <$> traverse (f'' s) ds - f' s (ValueDecl sa name nameKind bs val) = - ValueDecl sa name nameKind <$> traverse (h'' s) bs <*> traverse (guardedExprM (k' s) (g'' s)) val - f' s (BindingGroupDeclaration ds) = BindingGroupDeclaration <$> traverse (thirdM (g'' s)) ds - f' s (TypeClassDeclaration sa name args implies deps ds) = TypeClassDeclaration sa name args implies deps <$> traverse (f'' s) ds - f' s (TypeInstanceDeclaration sa na ch idx name cs className args ds) = TypeInstanceDeclaration sa na ch idx name cs className args <$> traverseTypeInstanceBody (traverse (f'' s)) ds - f' _ other = return other - - g'' s = uncurry g' <=< g s - - g' s (Literal ss l) = Literal ss <$> lit g'' s l - g' s (UnaryMinus ss v) = UnaryMinus ss <$> g'' s v - g' s (BinaryNoParens op v1 v2) = BinaryNoParens <$> g'' s op <*> g'' s v1 <*> g'' s v2 - g' s (Parens v) = Parens <$> g'' s v - g' s (Accessor prop v) = Accessor prop <$> g'' s v - g' s (ObjectUpdate obj vs) = ObjectUpdate <$> g'' s obj <*> traverse (sndM (g'' s)) vs - g' s (ObjectUpdateNested obj vs) = ObjectUpdateNested <$> g'' s obj <*> traverse (g'' s) vs - g' s (Abs binder v) = Abs <$> h' s binder <*> g'' s v - g' s (App v1 v2) = App <$> g'' s v1 <*> g'' s v2 - g' s (VisibleTypeApp v ty) = VisibleTypeApp <$> g'' s v <*> pure ty - g' s (Unused v) = Unused <$> g'' s v - g' s (IfThenElse v1 v2 v3) = IfThenElse <$> g'' s v1 <*> g'' s v2 <*> g'' s v3 - g' s (Case vs alts) = Case <$> traverse (g'' s) vs <*> traverse (i'' s) alts - g' s (TypedValue check v ty) = TypedValue check <$> g'' s v <*> pure ty - g' s (Let w ds v) = Let w <$> traverse (f'' s) ds <*> g'' s v - g' s (Do m es) = Do m <$> traverse (j'' s) es - g' s (Ado m es v) = Ado m <$> traverse (j'' s) es <*> g'' s v - g' s (PositionedValue pos com v) = PositionedValue pos com <$> g'' s v - g' _ other = return other - - h'' s = uncurry h' <=< h s - - h' s (LiteralBinder ss l) = LiteralBinder ss <$> lit h'' s l - h' s (ConstructorBinder ss ctor bs) = ConstructorBinder ss ctor <$> traverse (h'' s) bs - h' s (BinaryNoParensBinder b1 b2 b3) = BinaryNoParensBinder <$> h'' s b1 <*> h'' s b2 <*> h'' s b3 - h' s (ParensInBinder b) = ParensInBinder <$> h'' s b - h' s (NamedBinder ss name b) = NamedBinder ss name <$> h'' s b - h' s (PositionedBinder pos com b) = PositionedBinder pos com <$> h'' s b - h' s (TypedBinder t b) = TypedBinder t <$> h'' s b - h' _ other = return other - - lit :: (s -> a -> m a) -> s -> Literal a -> m (Literal a) - lit go s (ArrayLiteral as) = ArrayLiteral <$> traverse (go s) as - lit go s (ObjectLiteral as) = ObjectLiteral <$> traverse (sndM (go s)) as - lit _ _ other = return other - - i'' s = uncurry i' <=< i s - - i' s (CaseAlternative bs val) = CaseAlternative <$> traverse (h'' s) bs <*> traverse (guardedExprM' s) val - - -- A specialized `guardedExprM` that keeps track of the context `s` - -- after traversing `guards`, such that it's also exposed to `expr`. - guardedExprM' :: s -> GuardedExpr -> m GuardedExpr - guardedExprM' s (GuardedExpr guards expr) = do - (guards', s') <- runStateT (traverse (StateT . goGuard) guards) s - GuardedExpr guards' <$> g'' s' expr - - -- Like k'', but `s` is tracked. - goGuard :: Guard -> s -> m (Guard, s) - goGuard x s = k s x >>= fmap swap . sndM' k' - - j'' s = uncurry j' <=< j s - - j' s (DoNotationValue v) = DoNotationValue <$> g'' s v - j' s (DoNotationBind b v) = DoNotationBind <$> h'' s b <*> g'' s v - j' s (DoNotationLet ds) = DoNotationLet <$> traverse (f'' s) ds - j' s (PositionedDoNotationElement pos com e1) = PositionedDoNotationElement pos com <$> j'' s e1 - - k'' s = uncurry k' <=< k s - - k' s (ConditionGuard e) = ConditionGuard <$> g'' s e - k' s (PatternGuard b e) = PatternGuard <$> h'' s b <*> g'' s e - -data ScopedIdent = LocalIdent Ident | ToplevelIdent Ident - deriving (Show, Eq, Ord) - -inScope :: Ident -> S.Set ScopedIdent -> Bool -inScope i s = (LocalIdent i `S.member` s) || (ToplevelIdent i `S.member` s) - -everythingWithScope - :: forall r - . (Monoid r) - => (S.Set ScopedIdent -> Declaration -> r) - -> (S.Set ScopedIdent -> Expr -> r) - -> (S.Set ScopedIdent -> Binder -> r) - -> (S.Set ScopedIdent -> CaseAlternative -> r) - -> (S.Set ScopedIdent -> DoNotationElement -> r) - -> ( S.Set ScopedIdent -> Declaration -> r - , S.Set ScopedIdent -> Expr -> r - , S.Set ScopedIdent -> Binder -> r - , S.Set ScopedIdent -> CaseAlternative -> r - , S.Set ScopedIdent -> DoNotationElement -> r - ) -everythingWithScope f g h i j = (f'', g'', h'', i'', \s -> snd . j'' s) - where - f'' :: S.Set ScopedIdent -> Declaration -> r - f'' s a = f s a <> f' s a - - f' :: S.Set ScopedIdent -> Declaration -> r - f' s (DataBindingGroupDeclaration ds) = - let s' = S.union s (S.fromList (map ToplevelIdent (mapMaybe getDeclIdent (NEL.toList ds)))) - in foldMap (f'' s') ds - f' s (ValueDecl _ name _ bs val) = - let s' = S.insert (ToplevelIdent name) s - s'' = S.union s' (S.fromList (concatMap localBinderNames bs)) - in foldMap (h'' s') bs <> foldMap (l' s'') val - f' s (BindingGroupDeclaration ds) = - let s' = S.union s (S.fromList (NEL.toList (fmap (\((_, name), _, _) -> ToplevelIdent name) ds))) - in foldMap (\(_, _, val) -> g'' s' val) ds - f' s (TypeClassDeclaration _ _ _ _ _ ds) = foldMap (f'' s) ds - f' s (TypeInstanceDeclaration _ _ _ _ _ _ _ _ (ExplicitInstance ds)) = foldMap (f'' s) ds - f' _ _ = mempty - - g'' :: S.Set ScopedIdent -> Expr -> r - g'' s a = g s a <> g' s a - - g' :: S.Set ScopedIdent -> Expr -> r - g' s (Literal _ l) = lit g'' s l - g' s (UnaryMinus _ v1) = g'' s v1 - g' s (BinaryNoParens op v1 v2) = g'' s op <> g'' s v1 <> g'' s v2 - g' s (Parens v1) = g'' s v1 - g' s (Accessor _ v1) = g'' s v1 - g' s (ObjectUpdate obj vs) = g'' s obj <> foldMap (g'' s . snd) vs - g' s (ObjectUpdateNested obj vs) = g'' s obj <> foldMap (g'' s) vs - g' s (Abs b v1) = - let s' = S.union (S.fromList (localBinderNames b)) s - in h'' s b <> g'' s' v1 - g' s (App v1 v2) = g'' s v1 <> g'' s v2 - g' s (VisibleTypeApp v _) = g'' s v - g' s (Unused v) = g'' s v - g' s (IfThenElse v1 v2 v3) = g'' s v1 <> g'' s v2 <> g'' s v3 - g' s (Case vs alts) = foldMap (g'' s) vs <> foldMap (i'' s) alts - g' s (TypedValue _ v1 _) = g'' s v1 - g' s (Let _ ds v1) = - let s' = S.union s (S.fromList (map LocalIdent (mapMaybe getDeclIdent ds))) - in foldMap (f'' s') ds <> g'' s' v1 - g' s (Do _ es) = fold . snd . mapAccumL j'' s $ es - g' s (Ado _ es v1) = - let s' = S.union s (foldMap (fst . j'' s) es) - in g'' s' v1 - g' s (PositionedValue _ _ v1) = g'' s v1 - g' _ _ = mempty - - h'' :: S.Set ScopedIdent -> Binder -> r - h'' s a = h s a <> h' s a - - h' :: S.Set ScopedIdent -> Binder -> r - h' s (LiteralBinder _ l) = lit h'' s l - h' s (ConstructorBinder _ _ bs) = foldMap (h'' s) bs - h' s (BinaryNoParensBinder b1 b2 b3) = foldMap (h'' s) [b1, b2, b3] - h' s (ParensInBinder b) = h'' s b - h' s (NamedBinder _ name b1) = h'' (S.insert (LocalIdent name) s) b1 - h' s (PositionedBinder _ _ b1) = h'' s b1 - h' s (TypedBinder _ b1) = h'' s b1 - h' _ _ = mempty - - lit :: (S.Set ScopedIdent -> a -> r) -> S.Set ScopedIdent -> Literal a -> r - lit go s (ArrayLiteral as) = foldMap (go s) as - lit go s (ObjectLiteral as) = foldMap (go s . snd) as - lit _ _ _ = mempty - - i'' :: S.Set ScopedIdent -> CaseAlternative -> r - i'' s a = i s a <> i' s a - - i' :: S.Set ScopedIdent -> CaseAlternative -> r - i' s (CaseAlternative bs gs) = - let s' = S.union s (S.fromList (concatMap localBinderNames bs)) - in foldMap (h'' s) bs <> foldMap (l' s') gs - - j'' :: S.Set ScopedIdent -> DoNotationElement -> (S.Set ScopedIdent, r) - j'' s a = let (s', r) = j' s a in (s', j s a <> r) - - j' :: S.Set ScopedIdent -> DoNotationElement -> (S.Set ScopedIdent, r) - j' s (DoNotationValue v) = (s, g'' s v) - j' s (DoNotationBind b v) = - let s' = S.union (S.fromList (localBinderNames b)) s - in (s', h'' s b <> g'' s v) - j' s (DoNotationLet ds) = - let s' = S.union s (S.fromList (map LocalIdent (mapMaybe getDeclIdent ds))) - in (s', foldMap (f'' s') ds) - j' s (PositionedDoNotationElement _ _ e1) = j'' s e1 - - k' :: S.Set ScopedIdent -> Guard -> (S.Set ScopedIdent, r) - k' s (ConditionGuard e) = (s, g'' s e) - k' s (PatternGuard b e) = - let s' = S.union (S.fromList (localBinderNames b)) s - in (s', h'' s b <> g'' s' e) - - l' s (GuardedExpr [] e) = g'' s e - l' s (GuardedExpr (grd:gs) e) = - let (s', r) = k' s grd - in r <> l' s' (GuardedExpr gs e) - - getDeclIdent :: Declaration -> Maybe Ident - getDeclIdent (ValueDeclaration vd) = Just (valdeclIdent vd) - getDeclIdent (TypeDeclaration td) = Just (tydeclIdent td) - getDeclIdent _ = Nothing - - localBinderNames = map LocalIdent . binderNames - -accumTypes - :: (Monoid r) - => (SourceType -> r) - -> ( Declaration -> r - , Expr -> r - , Binder -> r - , CaseAlternative -> r - , DoNotationElement -> r - ) -accumTypes f = everythingOnValues mappend forDecls forValues forBinders (const mempty) (const mempty) - where - forDecls (DataDeclaration _ _ _ args dctors) = - foldMap (foldMap f . snd) args <> - foldMap (foldMap (f . snd) . dataCtorFields) dctors - forDecls (ExternDataDeclaration _ _ ty) = f ty - forDecls (ExternDeclaration _ _ ty) = f ty - forDecls (TypeClassDeclaration _ _ args implies _ _) = - foldMap (foldMap (foldMap f)) args <> - foldMap (foldMap f . constraintArgs) implies - forDecls (TypeInstanceDeclaration _ _ _ _ _ cs _ tys _) = - foldMap (foldMap f . constraintArgs) cs <> foldMap f tys - forDecls (TypeSynonymDeclaration _ _ args ty) = - foldMap (foldMap f . snd) args <> - f ty - forDecls (KindDeclaration _ _ _ ty) = f ty - forDecls (TypeDeclaration td) = f (tydeclType td) - forDecls _ = mempty - - forValues (TypeClassDictionary c _ _) = foldMap f (constraintArgs c) - forValues (DeferredDictionary _ tys) = foldMap f tys - forValues (TypedValue _ _ ty) = f ty - forValues (VisibleTypeApp _ ty) = f ty - forValues _ = mempty - - forBinders (TypedBinder ty _) = f ty - forBinders _ = mempty - --- | --- Map a function over type annotations appearing inside a value --- -overTypes :: (SourceType -> SourceType) -> Expr -> Expr -overTypes f = let (_, f', _) = everywhereOnValues id g id in f' - where - g :: Expr -> Expr - g (TypedValue checkTy val t) = TypedValue checkTy val (f t) - g (TypeClassDictionary c sco hints) = - TypeClassDictionary - (mapConstraintArgs (fmap f) c) - (updateCtx sco) - hints - g other = other - updateDict fn dict = dict { tcdInstanceTypes = fn (tcdInstanceTypes dict) } - updateScope = fmap . fmap . fmap . fmap $ updateDict $ fmap f - updateCtx = M.alter updateScope ByNullSourcePos diff --git a/claude-help/original-compiler/src/Language/PureScript/AST/Utils.hs b/claude-help/original-compiler/src/Language/PureScript/AST/Utils.hs deleted file mode 100644 index d768a884..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/AST/Utils.hs +++ /dev/null @@ -1,59 +0,0 @@ -module Language.PureScript.AST.Utils where - -import Protolude - -import Language.PureScript.AST (Binder(..), CaseAlternative, Expr(..), GuardedExpr, Literal, pattern MkUnguarded, nullSourceSpan) -import Language.PureScript.Names (Ident, ModuleName, ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), byMaybeModuleName) -import Language.PureScript.Types (SourceType, Type(..)) - -lam :: Ident -> Expr -> Expr -lam = Abs . mkBinder - -lamCase :: Ident -> [CaseAlternative] -> Expr -lamCase s = lam s . Case [mkVar s] - -lamCase2 :: Ident -> Ident -> [CaseAlternative] -> Expr -lamCase2 s t = lam s . lam t . Case [mkVar s, mkVar t] - -mkRef :: Qualified Ident -> Expr -mkRef = Var nullSourceSpan - -mkVarMn :: Maybe ModuleName -> Ident -> Expr -mkVarMn mn = mkRef . Qualified (byMaybeModuleName mn) - -mkVar :: Ident -> Expr -mkVar = mkVarMn Nothing - -mkBinder :: Ident -> Binder -mkBinder = VarBinder nullSourceSpan - -mkLit :: Literal Expr -> Expr -mkLit = Literal nullSourceSpan - -mkCtor :: ModuleName -> ProperName 'ConstructorName -> Expr -mkCtor mn name = Constructor nullSourceSpan (Qualified (ByModuleName mn) name) - -mkCtorBinder :: ModuleName -> ProperName 'ConstructorName -> [Binder] -> Binder -mkCtorBinder mn name = ConstructorBinder nullSourceSpan (Qualified (ByModuleName mn) name) - -unguarded :: Expr -> [GuardedExpr] -unguarded e = [MkUnguarded e] - -data UnwrappedTypeConstructor = UnwrappedTypeConstructor - { utcModuleName :: ModuleName - , utcTyCon :: ProperName 'TypeName - , utcKindArgs :: [SourceType] - , utcArgs :: [SourceType] - } - -utcQTyCon :: UnwrappedTypeConstructor -> Qualified (ProperName 'TypeName) -utcQTyCon UnwrappedTypeConstructor{..} = Qualified (ByModuleName utcModuleName) utcTyCon - -unwrapTypeConstructor :: SourceType -> Maybe UnwrappedTypeConstructor -unwrapTypeConstructor = go [] [] - where - go kargs args = \case - TypeConstructor _ (Qualified (ByModuleName mn) tyCon) -> Just (UnwrappedTypeConstructor mn tyCon kargs args) - TypeApp _ ty arg -> go kargs (arg : args) ty - KindApp _ ty karg -> go (karg : kargs) args ty - _ -> Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/Bundle.hs b/claude-help/original-compiler/src/Language/PureScript/Bundle.hs deleted file mode 100644 index f40cc44e..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Bundle.hs +++ /dev/null @@ -1,449 +0,0 @@ --- | --- Bundles compiled PureScript modules for the browser. --- --- This module takes as input the individual generated modules from 'Language.PureScript.Make' and --- performs dead code elimination, filters empty modules, --- and generates the final JavaScript bundle. -{-# LANGUAGE DeriveAnyClass #-} -module Language.PureScript.Bundle - ( ModuleIdentifier(..) - , ModuleType(..) - , ErrorMessage(..) - , printErrorMessage - , ForeignModuleExports(..) - , getExportedIdentifiers - , ForeignModuleImports(..) - , getImportedModules - , Module - ) where - -import Prelude - -import Control.DeepSeq (NFData) -import Control.Monad.Error.Class (MonadError(..)) - -import Data.Aeson ((.=)) -import Data.Char (chr, digitToInt) -import Data.Foldable (fold) -import Data.Maybe (mapMaybe, maybeToList) -import Data.Aeson qualified as A -import Data.Text.Lazy qualified as LT - -import GHC.Generics (Generic) - -import Language.JavaScript.Parser (JSAST(..), JSAnnot(..), JSAssignOp(..), JSExpression(..), JSStatement(..), renderToText) -import Language.JavaScript.Parser.AST (JSCommaList(..), JSCommaTrailingList(..), JSExportClause(..), JSExportDeclaration(..), JSExportSpecifier(..), JSFromClause(..), JSIdent(..), JSImportDeclaration(..), JSModuleItem(..), JSObjectProperty(..), JSObjectPropertyList, JSPropertyName(..), JSVarInitializer(..)) -import Language.JavaScript.Process.Minify (minifyJS) - --- | The type of error messages. We separate generation and rendering of errors using a data --- type, in case we need to match on error types later. -data ErrorMessage - = UnsupportedModulePath String - | InvalidTopLevel - | UnableToParseModule String - | UnsupportedImport - | UnsupportedExport - | ErrorInModule ModuleIdentifier ErrorMessage - | MissingEntryPoint String - | MissingMainModule String - deriving (Show, Generic, NFData) - --- | Modules are either "regular modules" (i.e. those generated by the PureScript compiler) or --- foreign modules. -data ModuleType - = Regular - | Foreign - deriving (Show, Eq, Ord, Generic, NFData) - -showModuleType :: ModuleType -> String -showModuleType Regular = "Regular" -showModuleType Foreign = "Foreign" - --- | A module is identified by its module name and its type. -data ModuleIdentifier = ModuleIdentifier String ModuleType - deriving (Show, Eq, Ord, Generic, NFData) - -instance A.ToJSON ModuleIdentifier where - toJSON (ModuleIdentifier name mt) = - A.object [ "name" .= name - , "type" .= show mt - ] - -data Visibility - = Public - | Internal - deriving (Show, Eq, Ord) - --- | A piece of code is identified by its module, its name, and whether it is an internal variable --- or a public member. These keys are used to label vertices in the dependency graph. -type Key = (ModuleIdentifier, String, Visibility) - --- | An export is either a "regular export", which exports a name from the regular module we are in, --- or a reexport of a declaration in the corresponding foreign module. --- --- Regular exports are labelled, since they might re-export an operator with another name. -data ExportType - = RegularExport String - | ForeignReexport - deriving (Show, Eq, Ord) - --- | There are four types of module element we are interested in: --- --- 1) Import declarations and require statements --- 2) Member declarations --- 3) Export lists --- 4) Everything else --- --- Each is labelled with the original AST node which generated it, so that we can dump it back --- into the output during codegen. -data ModuleElement - = Import JSModuleItem String (Either String ModuleIdentifier) - | Member JSStatement Visibility String JSExpression [Key] - | ExportsList [(ExportType, String, JSExpression, [Key])] - | Other JSStatement - | Skip JSModuleItem - deriving (Show) - -instance A.ToJSON ModuleElement where - toJSON = \case - (Import _ name (Right target)) -> - A.object [ "type" .= A.String "Import" - , "name" .= name - , "target" .= target - ] - (Import _ name (Left targetPath)) -> - A.object [ "type" .= A.String "Import" - , "name" .= name - , "targetPath" .= targetPath - ] - (Member _ visibility name _ dependsOn) -> - A.object [ "type" .= A.String "Member" - , "name" .= name - , "visibility" .= show visibility - , "dependsOn" .= map keyToJSON dependsOn - ] - (ExportsList exports) -> - A.object [ "type" .= A.String "ExportsList" - , "exports" .= map exportToJSON exports - ] - (Other stmt) -> - A.object [ "type" .= A.String "Other" - , "js" .= getFragment (JSAstStatement stmt JSNoAnnot) - ] - (Skip item) -> - A.object [ "type" .= A.String "Skip" - , "js" .= getFragment (JSAstModule [item] JSNoAnnot) - ] - - where - - keyToJSON (mid, member, visibility) = - A.object [ "module" .= mid - , "member" .= member - , "visibility" .= show visibility - ] - - exportToJSON (RegularExport sourceName, name, _, dependsOn) = - A.object [ "type" .= A.String "RegularExport" - , "name" .= name - , "sourceName" .= sourceName - , "dependsOn" .= map keyToJSON dependsOn - ] - exportToJSON (ForeignReexport, name, _, dependsOn) = - A.object [ "type" .= A.String "ForeignReexport" - , "name" .= name - , "dependsOn" .= map keyToJSON dependsOn - ] - - getFragment = ellipsize . renderToText . minifyJS - where - ellipsize text = if LT.compareLength text 20 == GT then LT.take 19 text `LT.snoc` ellipsis else text - ellipsis = '\x2026' - --- | A module is just a list of elements of the types listed above. -data Module = Module ModuleIdentifier (Maybe FilePath) [ModuleElement] deriving (Show) - -instance A.ToJSON Module where - toJSON (Module moduleId filePath elements) = - A.object [ "moduleId" .= moduleId - , "filePath" .= filePath - , "elements" .= elements - ] - --- | Prepare an error message for consumption by humans. -printErrorMessage :: ErrorMessage -> [String] -printErrorMessage (UnsupportedModulePath s) = - [ "An ES or CommonJS module has an unsupported name (" ++ show s ++ ")." - , "The following file names are supported:" - , " 1) index.js (PureScript native modules)" - , " 2) foreign.js (PureScript ES foreign modules)" - , " 3) foreign.cjs (PureScript CommonJS foreign modules)" - ] -printErrorMessage InvalidTopLevel = - [ "Expected a list of source elements at the top level." ] -printErrorMessage (UnableToParseModule err) = - [ "The module could not be parsed:" - , err - ] -printErrorMessage UnsupportedImport = - [ "An import was unsupported." - , "Modules can be imported with ES namespace imports declarations:" - , " import * as module from \"Module.Name\"" - , "Alternatively, they can be also be imported with the CommonJS require function:" - , " var module = require(\"Module.Name\")" - ] -printErrorMessage UnsupportedExport = - [ "An export was unsupported." - , "Declarations can be exported as ES named exports:" - , " export var decl" - , "Existing identifiers can be exported as well:" - , " export { name }" - , "They can also be renamed on export:" - , " export { name as alias }" - , "Alternatively, CommonJS exports can be defined in one of two ways:" - , " 1) exports.name = value" - , " 2) exports = { name: value }" - ] -printErrorMessage (ErrorInModule mid e) = - ("Error in module " ++ displayIdentifier mid ++ ":") - : "" - : map (" " ++) (printErrorMessage e) - where - displayIdentifier (ModuleIdentifier name ty) = - name ++ " (" ++ showModuleType ty ++ ")" -printErrorMessage (MissingEntryPoint mName) = - [ "Could not find an ES module or CommonJS module for the specified entry point: " ++ mName - ] -printErrorMessage (MissingMainModule mName) = - [ "Could not find an ES module or CommonJS module for the specified main module: " ++ mName - ] - --- String literals include the quote chars -fromStringLiteral :: JSExpression -> Maybe String -fromStringLiteral (JSStringLiteral _ str) = Just $ strValue str -fromStringLiteral _ = Nothing - -strValue :: String -> String -strValue str = go $ drop 1 str - where - go ('\\' : 'b' : xs) = '\b' : go xs - go ('\\' : 'f' : xs) = '\f' : go xs - go ('\\' : 'n' : xs) = '\n' : go xs - go ('\\' : 'r' : xs) = '\r' : go xs - go ('\\' : 't' : xs) = '\t' : go xs - go ('\\' : 'v' : xs) = '\v' : go xs - go ('\\' : '0' : xs) = '\0' : go xs - go ('\\' : 'x' : a : b : xs) = chr (a' + b') : go xs - where - a' = 16 * digitToInt a - b' = digitToInt b - go ('\\' : 'u' : a : b : c : d : xs) = chr (a' + b' + c' + d') : go xs - where - a' = 16 * 16 * 16 * digitToInt a - b' = 16 * 16 * digitToInt b - c' = 16 * digitToInt c - d' = digitToInt d - go ('\\' : x : xs) = x : go xs - go "\"" = "" - go "'" = "" - go (x : xs) = x : go xs - go "" = "" - -commaList :: JSCommaList a -> [a] -commaList JSLNil = [] -commaList (JSLOne x) = [x] -commaList (JSLCons l _ x) = commaList l ++ [x] - -trailingCommaList :: JSCommaTrailingList a -> [a] -trailingCommaList (JSCTLComma l _) = commaList l -trailingCommaList (JSCTLNone l) = commaList l - -identName :: JSIdent -> Maybe String -identName (JSIdentName _ ident) = Just ident -identName _ = Nothing - -exportStatementIdentifiers :: JSStatement -> [String] -exportStatementIdentifiers (JSVariable _ jsExpressions _) = - varNames jsExpressions -exportStatementIdentifiers (JSConstant _ jsExpressions _) = - varNames jsExpressions -exportStatementIdentifiers (JSLet _ jsExpressions _) = - varNames jsExpressions -exportStatementIdentifiers (JSClass _ jsIdent _ _ _ _ _) = - maybeToList . identName $ jsIdent -exportStatementIdentifiers (JSFunction _ jsIdent _ _ _ _ _) = - maybeToList . identName $ jsIdent -exportStatementIdentifiers (JSGenerator _ _ jsIdent _ _ _ _ _) = - maybeToList . identName $ jsIdent -exportStatementIdentifiers _ = [] - -varNames :: JSCommaList JSExpression -> [String] -varNames = mapMaybe varName . commaList - where - varName (JSVarInitExpression (JSIdentifier _ ident) _) = Just ident - varName _ = Nothing - -data ForeignModuleExports = - ForeignModuleExports - { cjsExports :: [String] - , esExports :: [String] - } deriving (Show) - -instance Semigroup ForeignModuleExports where - (ForeignModuleExports cjsExports esExports) <> (ForeignModuleExports cjsExports' esExports') = - ForeignModuleExports (cjsExports <> cjsExports') (esExports <> esExports') -instance Monoid ForeignModuleExports where - mempty = ForeignModuleExports [] [] - --- Get a list of all the exported identifiers from a foreign module. --- --- TODO: what if we assign to exports.foo and then later assign to --- module.exports (presumably overwriting exports.foo)? -getExportedIdentifiers :: forall m. (MonadError ErrorMessage m) - => String - -> JSAST - -> m ForeignModuleExports -getExportedIdentifiers mname top - | JSAstModule jsModuleItems _ <- top = fold <$> traverse go jsModuleItems - | otherwise = err InvalidTopLevel - where - err :: ErrorMessage -> m a - err = throwError . ErrorInModule (ModuleIdentifier mname Foreign) - - go (JSModuleStatementListItem jsStatement) - | Just props <- matchExportsAssignment jsStatement - = do cjsExports <- traverse toIdent (trailingCommaList props) - pure ForeignModuleExports{ cjsExports, esExports = [] } - | Just (Public, name, _) <- matchMember jsStatement - = pure ForeignModuleExports{ cjsExports = [name], esExports = [] } - | otherwise - = pure mempty - go (JSModuleExportDeclaration _ jsExportDeclaration) = - pure ForeignModuleExports{ cjsExports = [], esExports = exportDeclarationIdentifiers jsExportDeclaration } - go _ = pure mempty - - toIdent (JSPropertyNameandValue name _ [_]) = - extractLabel' name - toIdent _ = - err UnsupportedExport - - extractLabel' = maybe (err UnsupportedExport) pure . extractLabel - - exportDeclarationIdentifiers (JSExportFrom jsExportClause _ _) = - exportClauseIdentifiers jsExportClause - exportDeclarationIdentifiers (JSExportLocals jsExportClause _) = - exportClauseIdentifiers jsExportClause - exportDeclarationIdentifiers (JSExport jsStatement _) = - exportStatementIdentifiers jsStatement - - exportClauseIdentifiers (JSExportClause _ jsExportsSpecifiers _) = - mapMaybe exportSpecifierName $ commaList jsExportsSpecifiers - - exportSpecifierName (JSExportSpecifier jsIdent) = identName jsIdent - exportSpecifierName (JSExportSpecifierAs _ _ jsIdentAs) = identName jsIdentAs - -data ForeignModuleImports = - ForeignModuleImports - { cjsImports :: [String] - , esImports :: [String] - } deriving (Show) - -instance Semigroup ForeignModuleImports where - (ForeignModuleImports cjsImports esImports) <> (ForeignModuleImports cjsImports' esImports') = - ForeignModuleImports (cjsImports <> cjsImports') (esImports <> esImports') -instance Monoid ForeignModuleImports where - mempty = ForeignModuleImports [] [] - --- Get a list of all the imported module identifiers from a foreign module. -getImportedModules :: forall m. (MonadError ErrorMessage m) - => String - -> JSAST - -> m ForeignModuleImports -getImportedModules mname top - | JSAstModule jsModuleItems _ <- top = pure $ foldMap go jsModuleItems - | otherwise = err InvalidTopLevel - where - err :: ErrorMessage -> m a - err = throwError . ErrorInModule (ModuleIdentifier mname Foreign) - - go (JSModuleStatementListItem jsStatement) - | Just (_, mid) <- matchRequire jsStatement - = ForeignModuleImports{ cjsImports = [mid], esImports = [] } - go (JSModuleImportDeclaration _ jsImportDeclaration) = - ForeignModuleImports{ cjsImports = [], esImports = [importDeclarationModuleId jsImportDeclaration] } - go _ = mempty - - importDeclarationModuleId (JSImportDeclaration _ (JSFromClause _ _ mid) _) = mid - importDeclarationModuleId (JSImportDeclarationBare _ mid _) = mid - --- Matches JS statements like this: --- var ModuleName = require("file"); -matchRequire :: JSStatement -> Maybe (String, String) -matchRequire stmt - | JSVariable _ jsInit _ <- stmt - , [JSVarInitExpression var varInit] <- commaList jsInit - , JSIdentifier _ importName <- var - , JSVarInit _ jsInitEx <- varInit - , JSMemberExpression req _ argsE _ <- jsInitEx - , JSIdentifier _ "require" <- req - , [ Just importPath ] <- map fromStringLiteral (commaList argsE) - = Just (importName, importPath) - | otherwise - = Nothing - --- Matches JS member declarations. -matchMember :: JSStatement -> Maybe (Visibility, String, JSExpression) -matchMember stmt - | Just (name, decl) <- matchInternalMember stmt - = pure (Internal, name, decl) - -- exports.foo = expr; exports["foo"] = expr; - | JSAssignStatement e (JSAssign _) decl _ <- stmt - , Just name <- exportsAccessor e - = Just (Public, name, decl) - | otherwise - = Nothing - -matchInternalMember :: JSStatement -> Maybe (String, JSExpression) -matchInternalMember stmt - -- var foo = expr; - | JSVariable _ jsInit _ <- stmt - , [JSVarInitExpression var varInit] <- commaList jsInit - , JSIdentifier _ name <- var - , JSVarInit _ decl <- varInit - = pure (name, decl) - -- function foo(...args) { body } - | JSFunction a0 jsIdent a1 args a2 body _ <- stmt - , JSIdentName _ name <- jsIdent - = pure (name, JSFunctionExpression a0 jsIdent a1 args a2 body) - | otherwise - = Nothing - --- Matches exports.* or exports["*"] expressions and returns the property name. -exportsAccessor :: JSExpression -> Maybe String -exportsAccessor (JSMemberDot exports _ nm) - | JSIdentifier _ "exports" <- exports - , JSIdentifier _ name <- nm - = Just name -exportsAccessor (JSMemberSquare exports _ nm _) - | JSIdentifier _ "exports" <- exports - , Just name <- fromStringLiteral nm - = Just name -exportsAccessor _ = Nothing - --- Matches assignments to module.exports, like this: --- module.exports = { ... } -matchExportsAssignment :: JSStatement -> Maybe JSObjectPropertyList -matchExportsAssignment stmt - | JSAssignStatement e (JSAssign _) decl _ <- stmt - , JSMemberDot module' _ exports <- e - , JSIdentifier _ "module" <- module' - , JSIdentifier _ "exports" <- exports - , JSObjectLiteral _ props _ <- decl - = Just props - | otherwise - = Nothing - -extractLabel :: JSPropertyName -> Maybe String -extractLabel (JSPropertyString _ nm) = Just $ strValue nm -extractLabel (JSPropertyIdent _ nm) = Just nm -extractLabel _ = Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/CST.hs b/claude-help/original-compiler/src/Language/PureScript/CST.hs deleted file mode 100644 index b8e895fb..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST.hs +++ /dev/null @@ -1,105 +0,0 @@ -module Language.PureScript.CST - ( parseFromFile - , parseFromFiles - , parseModuleFromFile - , parseModulesFromFiles - , unwrapParserError - , toMultipleErrors - , toMultipleWarnings - , toPositionedError - , toPositionedWarning - , pureResult - , module Language.PureScript.CST.Convert - , module Language.PureScript.CST.Errors - , module Language.PureScript.CST.Lexer - , module Language.PureScript.CST.Monad - , module Language.PureScript.CST.Parser - , module Language.PureScript.CST.Print - , module Language.PureScript.CST.Types - ) where - -import Prelude hiding (lex) - -import Control.Monad.Error.Class (MonadError(..)) -import Control.Parallel.Strategies (withStrategy, parList, evalTuple2, r0, rseq) -import Data.List.NonEmpty qualified as NE -import Data.Text (Text) -import Language.PureScript.AST qualified as AST -import Language.PureScript.Errors qualified as E -import Language.PureScript.CST.Convert -import Language.PureScript.CST.Errors -import Language.PureScript.CST.Lexer -import Language.PureScript.CST.Monad (Parser, ParserM(..), ParserState(..), LexResult, runParser, runTokenParser) -import Language.PureScript.CST.Parser -import Language.PureScript.CST.Print -import Language.PureScript.CST.Types - -pureResult :: a -> PartialResult a -pureResult a = PartialResult a ([], pure a) - -parseModulesFromFiles - :: forall m k - . MonadError E.MultipleErrors m - => (k -> FilePath) - -> [(k, Text)] - -> m [(k, PartialResult AST.Module)] -parseModulesFromFiles toFilePath input = - flip E.parU (handleParserError toFilePath) - . inParallel - . flip fmap input - $ \(k, a) -> (k, parseModuleFromFile (toFilePath k) a) - -parseFromFiles - :: forall m k - . MonadError E.MultipleErrors m - => (k -> FilePath) - -> [(k, Text)] - -> m [(k, ([ParserWarning], AST.Module))] -parseFromFiles toFilePath input = - flip E.parU (handleParserError toFilePath) - . inParallel - . flip fmap input - $ \(k, a) -> (k, sequence $ parseFromFile (toFilePath k) a) - -parseModuleFromFile :: FilePath -> Text -> Either (NE.NonEmpty ParserError) (PartialResult AST.Module) -parseModuleFromFile fp content = fmap (convertModule fp) <$> parseModule (lexModule content) - -parseFromFile :: FilePath -> Text -> ([ParserWarning], Either (NE.NonEmpty ParserError) AST.Module) -parseFromFile fp content = fmap (convertModule fp) <$> parse content - -handleParserError - :: forall m k a - . MonadError E.MultipleErrors m - => (k -> FilePath) - -> (k, Either (NE.NonEmpty ParserError) a) - -> m (k, a) -handleParserError toFilePath (k, res) = - (k,) <$> unwrapParserError (toFilePath k) res - -unwrapParserError - :: forall m a - . MonadError E.MultipleErrors m - => FilePath - -> Either (NE.NonEmpty ParserError) a - -> m a -unwrapParserError fp = - either (throwError . toMultipleErrors fp) pure - -toMultipleErrors :: FilePath -> NE.NonEmpty ParserError -> E.MultipleErrors -toMultipleErrors fp = - E.MultipleErrors . NE.toList . fmap (toPositionedError fp) - -toMultipleWarnings :: FilePath -> [ParserWarning] -> E.MultipleErrors -toMultipleWarnings fp = - E.MultipleErrors . fmap (toPositionedWarning fp) - -toPositionedError :: FilePath -> ParserError -> E.ErrorMessage -toPositionedError name perr = - E.ErrorMessage [E.positionedError $ sourceSpan name $ errRange perr] (E.ErrorParsingCSTModule perr) - -toPositionedWarning :: FilePath -> ParserWarning -> E.ErrorMessage -toPositionedWarning name perr = - E.ErrorMessage [E.positionedError $ sourceSpan name $ errRange perr] (E.WarningParsingCSTModule perr) - -inParallel :: [(k, Either (NE.NonEmpty ParserError) a)] -> [(k, Either (NE.NonEmpty ParserError) a)] -inParallel = withStrategy (parList (evalTuple2 r0 rseq)) diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Convert.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Convert.hs deleted file mode 100644 index 59b68adf..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Convert.hs +++ /dev/null @@ -1,710 +0,0 @@ --- | This module contains functions for converting the CST into the core AST. It --- is mostly boilerplate, and does the job of resolving ranges for all the nodes --- and attaching comments. - -module Language.PureScript.CST.Convert - ( convertType - , convertExpr - , convertBinder - , convertDeclaration - , convertImportDecl - , convertModule - , sourcePos - , sourceSpan - , comment - , comments - ) where - -import Prelude hiding (take) -import Protolude (headDef) - -import Data.Bifunctor (bimap, first) -import Data.Char (toLower) -import Data.Foldable (foldl', toList) -import Data.Functor (($>)) -import Data.List.NonEmpty qualified as NE -import Data.Maybe (isJust, fromJust, mapMaybe) -import Data.Text qualified as Text -import Language.PureScript.AST qualified as AST -import Language.PureScript.AST.Declarations.ChainId (mkChainId) -import Language.PureScript.AST.SourcePos qualified as Pos -import Language.PureScript.Comments qualified as C -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment qualified as Env -import Language.PureScript.Label qualified as L -import Language.PureScript.Names qualified as N -import Language.PureScript.PSString (mkString, prettyPrintStringJS) -import Language.PureScript.Types qualified as T -import Language.PureScript.CST.Positions -import Language.PureScript.CST.Print (printToken) -import Language.PureScript.CST.Types - -comment :: Comment a -> Maybe C.Comment -comment = \case - Comment t - | "{-" `Text.isPrefixOf` t -> Just $ C.BlockComment $ Text.drop 2 $ Text.dropEnd 2 t - | "--" `Text.isPrefixOf` t -> Just $ C.LineComment $ Text.drop 2 t - _ -> Nothing - -comments :: [Comment a] -> [C.Comment] -comments = mapMaybe comment - -sourcePos :: SourcePos -> Pos.SourcePos -sourcePos (SourcePos line col) = Pos.SourcePos line col - -sourceSpan :: String -> SourceRange -> Pos.SourceSpan -sourceSpan name (SourceRange start end) = Pos.SourceSpan name (sourcePos start) (sourcePos end) - -widenLeft :: TokenAnn -> Pos.SourceAnn -> Pos.SourceAnn -widenLeft ann (sp, _) = - ( Pos.widenSourceSpan (sourceSpan (Pos.spanName sp) $ tokRange ann) sp - , comments $ tokLeadingComments ann - ) - -sourceAnnCommented :: String -> SourceToken -> SourceToken -> Pos.SourceAnn -sourceAnnCommented fileName (SourceToken ann1 _) (SourceToken ann2 _) = - ( Pos.SourceSpan fileName (sourcePos $ srcStart $ tokRange ann1) (sourcePos $ srcEnd $ tokRange ann2) - , comments $ tokLeadingComments ann1 - ) - -sourceAnn :: String -> SourceToken -> SourceToken -> Pos.SourceAnn -sourceAnn fileName (SourceToken ann1 _) (SourceToken ann2 _) = - ( Pos.SourceSpan fileName (sourcePos $ srcStart $ tokRange ann1) (sourcePos $ srcEnd $ tokRange ann2) - , [] - ) - -sourceName :: String -> Name a -> Pos.SourceAnn -sourceName fileName a = sourceAnnCommented fileName (nameTok a) (nameTok a) - -sourceQualName :: String -> QualifiedName a -> Pos.SourceAnn -sourceQualName fileName a = sourceAnnCommented fileName (qualTok a) (qualTok a) - -moduleName :: Token -> Maybe N.ModuleName -moduleName = \case - TokLowerName as _ -> go as - TokUpperName as _ -> go as - TokSymbolName as _ -> go as - TokOperator as _ -> go as - _ -> Nothing - where - go [] = Nothing - go ns = Just $ N.ModuleName $ Text.intercalate "." ns - -qualified :: QualifiedName a -> N.Qualified a -qualified q = N.Qualified qb (qualName q) - where - qb = maybe N.ByNullSourcePos N.ByModuleName $ qualModule q - -ident :: Ident -> N.Ident -ident = N.Ident . getIdent - -convertType :: String -> Type a -> T.SourceType -convertType = convertType' False - -convertVtaType :: String -> Type a -> T.SourceType -convertVtaType = convertType' True - -convertType' :: Bool -> String -> Type a -> T.SourceType -convertType' withinVta fileName = go - where - goRow (Row labels tl) b = do - let - rowTail = case tl of - Just (_, ty) -> go ty - Nothing -> T.REmpty $ sourceAnnCommented fileName b b - rowCons (Labeled a _ ty) c = do - let ann = sourceAnnCommented fileName (lblTok a) (snd $ typeRange ty) - T.RCons ann (L.Label $ lblName a) (go ty) c - case labels of - Just (Separated h t) -> - rowCons h $ foldr (rowCons . snd) rowTail t - Nothing -> - rowTail - - go = \case - TypeVar _ a -> - T.TypeVar (sourceName fileName a) . getIdent $ nameValue a - TypeConstructor _ a -> - T.TypeConstructor (sourceQualName fileName a) $ qualified a - TypeWildcard _ a -> - T.TypeWildcard (sourceAnnCommented fileName a a) $ if withinVta then T.IgnoredWildcard else T.UnnamedWildcard - TypeHole _ a -> - T.TypeWildcard (sourceName fileName a) . T.HoleWildcard . getIdent $ nameValue a - TypeString _ a b -> - T.TypeLevelString (sourceAnnCommented fileName a a) b - TypeInt _ _ a b -> - T.TypeLevelInt (sourceAnnCommented fileName a a) b - TypeRow _ (Wrapped _ row b) -> - goRow row b - TypeRecord _ (Wrapped a row b) -> do - let - ann = sourceAnnCommented fileName a b - annRec = sourceAnn fileName a a - T.TypeApp ann (Env.tyRecord $> annRec) $ goRow row b - TypeForall _ kw bindings _ ty -> do - let - mkForAll a b v t = do - let ann' = widenLeft (tokAnn $ nameTok a) $ T.getAnnForType t - T.ForAll ann' (maybe T.TypeVarInvisible (const T.TypeVarVisible) v) (getIdent $ nameValue a) b t Nothing - k (TypeVarKinded (Wrapped _ (Labeled (v, a) _ b) _)) = mkForAll a (Just (go b)) v - k (TypeVarName (v, a)) = mkForAll a Nothing v - ty' = foldr k (go ty) bindings - ann = widenLeft (tokAnn kw) $ T.getAnnForType ty' - T.setAnnForType ann ty' - TypeKinded _ ty _ kd -> do - let - ty' = go ty - kd' = go kd - ann = Pos.widenSourceAnn (T.getAnnForType ty') (T.getAnnForType kd') - T.KindedType ann ty' kd' - TypeApp _ a b -> do - let - a' = go a - b' = go b - ann = Pos.widenSourceAnn (T.getAnnForType a') (T.getAnnForType b') - T.TypeApp ann a' b' - ty@(TypeOp _ _ _ _) -> do - let - reassoc op b' a = do - let - a' = go a - op' = T.TypeOp (sourceQualName fileName op) $ qualified op - ann = Pos.widenSourceAnn (T.getAnnForType a') (T.getAnnForType b') - T.BinaryNoParensType ann op' (go a) b' - loop k = \case - TypeOp _ a op b -> loop (reassoc op (k b)) a - expr' -> k expr' - loop go ty - TypeOpName _ op -> do - let rng = qualRange op - T.TypeOp (uncurry (sourceAnnCommented fileName) rng) (qualified op) - TypeArr _ a arr b -> do - let - a' = go a - b' = go b - arr' = Env.tyFunction $> sourceAnnCommented fileName arr arr - ann = Pos.widenSourceAnn (T.getAnnForType a') (T.getAnnForType b') - T.TypeApp ann (T.TypeApp ann arr' a') b' - TypeArrName _ a -> - Env.tyFunction $> sourceAnnCommented fileName a a - TypeConstrained _ a _ b -> do - let - a' = convertConstraint withinVta fileName a - b' = go b - ann = Pos.widenSourceAnn (T.constraintAnn a') (T.getAnnForType b') - T.ConstrainedType ann a' b' - TypeParens _ (Wrapped a ty b) -> - T.ParensInType (sourceAnnCommented fileName a b) $ go ty - ty@(TypeUnaryRow _ _ a) -> do - let - a' = go a - rng = typeRange ty - ann = uncurry (sourceAnnCommented fileName) rng - T.setAnnForType ann $ Env.kindRow a' - -convertConstraint :: Bool -> String -> Constraint a -> T.SourceConstraint -convertConstraint withinVta fileName = go - where - go = \case - cst@(Constraint _ name args) -> do - let ann = uncurry (sourceAnnCommented fileName) $ constraintRange cst - T.Constraint ann (qualified name) [] (convertType' withinVta fileName <$> args) Nothing - ConstraintParens _ (Wrapped _ c _) -> go c - -convertGuarded :: String -> Guarded a -> [AST.GuardedExpr] -convertGuarded fileName = \case - Unconditional _ x -> [AST.GuardedExpr [] (convertWhere fileName x)] - Guarded gs -> (\(GuardedExpr _ ps _ x) -> AST.GuardedExpr (p <$> toList ps) (convertWhere fileName x)) <$> NE.toList gs - where - go = convertExpr fileName - p (PatternGuard Nothing x) = AST.ConditionGuard (go x) - p (PatternGuard (Just (b, _)) x) = AST.PatternGuard (convertBinder fileName b) (go x) - -convertWhere :: String -> Where a -> AST.Expr -convertWhere fileName = \case - Where expr Nothing -> convertExpr fileName expr - Where expr (Just (_, bs)) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - uncurry AST.PositionedValue ann . AST.Let AST.FromWhere (convertLetBinding fileName <$> NE.toList bs) $ convertExpr fileName expr - -convertLetBinding :: String -> LetBinding a -> AST.Declaration -convertLetBinding fileName = \case - LetBindingSignature _ lbl -> - convertSignature fileName lbl - binding@(LetBindingName _ fields) -> do - let ann = uncurry (sourceAnnCommented fileName) $ letBindingRange binding - convertValueBindingFields fileName ann fields - binding@(LetBindingPattern _ a _ b) -> do - let ann = uncurry (sourceAnnCommented fileName) $ letBindingRange binding - AST.BoundValueDeclaration ann (convertBinder fileName a) (convertWhere fileName b) - -convertExpr :: forall a. String -> Expr a -> AST.Expr -convertExpr fileName = go - where - positioned = - uncurry AST.PositionedValue - - goDoStatement = \case - stmt@(DoLet _ as) -> do - let ann = uncurry (sourceAnnCommented fileName) $ doStatementRange stmt - uncurry AST.PositionedDoNotationElement ann . AST.DoNotationLet $ convertLetBinding fileName <$> NE.toList as - stmt@(DoDiscard a) -> do - let ann = uncurry (sourceAnn fileName) $ doStatementRange stmt - uncurry AST.PositionedDoNotationElement ann . AST.DoNotationValue $ go a - stmt@(DoBind a _ b) -> do - let - ann = uncurry (sourceAnn fileName) $ doStatementRange stmt - a' = convertBinder fileName a - b' = go b - uncurry AST.PositionedDoNotationElement ann $ AST.DoNotationBind a' b' - - go = \case - ExprHole _ a -> - positioned (sourceName fileName a) . AST.Hole . getIdent $ nameValue a - ExprSection _ a -> - positioned (sourceAnnCommented fileName a a) AST.AnonymousArgument - ExprIdent _ a -> do - let ann = sourceQualName fileName a - positioned ann . AST.Var (fst ann) . qualified $ fmap ident a - ExprConstructor _ a -> do - let ann = sourceQualName fileName a - positioned ann . AST.Constructor (fst ann) $ qualified a - ExprBoolean _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.Literal (fst ann) $ AST.BooleanLiteral b - ExprChar _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.Literal (fst ann) $ AST.CharLiteral b - ExprString _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.Literal (fst ann) . AST.StringLiteral $ b - ExprNumber _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.Literal (fst ann) $ AST.NumericLiteral b - ExprArray _ (Wrapped a bs c) -> do - let - ann = sourceAnnCommented fileName a c - vals = case bs of - Just (Separated x xs) -> go x : (go . snd <$> xs) - Nothing -> [] - positioned ann . AST.Literal (fst ann) $ AST.ArrayLiteral vals - ExprRecord z (Wrapped a bs c) -> do - let - ann = sourceAnnCommented fileName a c - lbl = \case - RecordPun f -> (mkString . getIdent $ nameValue f, go . ExprIdent z $ QualifiedName (nameTok f) Nothing (nameValue f)) - RecordField f _ v -> (lblName f, go v) - vals = case bs of - Just (Separated x xs) -> lbl x : (lbl . snd <$> xs) - Nothing -> [] - positioned ann . AST.Literal (fst ann) $ AST.ObjectLiteral vals - ExprParens _ (Wrapped a b c) -> - positioned (sourceAnnCommented fileName a c) . AST.Parens $ go b - expr@(ExprTyped _ a _ b) -> do - let - a' = go a - b' = convertType fileName b - ann = (sourceSpan fileName . toSourceRange $ exprRange expr, []) - positioned ann $ AST.TypedValue True a' b' - expr@(ExprInfix _ a (Wrapped _ b _) c) -> do - let ann = (sourceSpan fileName . toSourceRange $ exprRange expr, []) - positioned ann $ AST.BinaryNoParens (go b) (go a) (go c) - expr@(ExprOp _ _ _ _) -> do - let - ann = uncurry (sourceAnn fileName) $ exprRange expr - reassoc op b a = do - let op' = AST.Op (sourceSpan fileName . toSourceRange $ qualRange op) $ qualified op - AST.BinaryNoParens op' (go a) b - loop k = \case - ExprOp _ a op b -> loop (reassoc op (k b)) a - expr' -> k expr' - positioned ann $ loop go expr - ExprOpName _ op -> do - let - rng = qualRange op - op' = AST.Op (sourceSpan fileName $ toSourceRange rng) $ qualified op - positioned (uncurry (sourceAnnCommented fileName) rng) op' - expr@(ExprNegate _ _ b) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - positioned ann . AST.UnaryMinus (fst ann) $ go b - expr@(ExprRecordAccessor _ (RecordAccessor a _ (Separated h t))) -> do - let - ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - field x f = AST.Accessor (lblName f) x - positioned ann $ foldl' (\x (_, f) -> field x f) (field (go a) h) t - expr@(ExprRecordUpdate _ a b) -> do - let - ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - k (RecordUpdateLeaf f _ x) = (lblName f, AST.Leaf $ go x) - k (RecordUpdateBranch f xs) = (lblName f, AST.Branch $ toTree xs) - toTree (Wrapped _ xs _) = AST.PathTree . AST.AssocList . map k $ toList xs - positioned ann . AST.ObjectUpdateNested (go a) $ toTree b - expr@(ExprApp _ a b) -> do - let ann = uncurry (sourceAnn fileName) $ exprRange expr - positioned ann $ AST.App (go a) (go b) - expr@(ExprVisibleTypeApp _ a _ b) -> do - let ann = uncurry (sourceAnn fileName) $ exprRange expr - positioned ann $ AST.VisibleTypeApp (go a) (convertVtaType fileName b) - expr@(ExprLambda _ (Lambda _ as _ b)) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - positioned ann - . AST.Abs (convertBinder fileName (NE.head as)) - . foldr (AST.Abs . convertBinder fileName) (go b) - $ NE.tail as - expr@(ExprIf _ (IfThenElse _ a _ b _ c)) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - positioned ann $ AST.IfThenElse (go a) (go b) (go c) - expr@(ExprCase _ (CaseOf _ as _ bs)) -> do - let - ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - as' = go <$> toList as - bs' = uncurry AST.CaseAlternative . bimap (map (convertBinder fileName) . toList) (convertGuarded fileName) <$> NE.toList bs - positioned ann $ AST.Case as' bs' - expr@(ExprLet _ (LetIn _ as _ b)) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - positioned ann . AST.Let AST.FromLet (convertLetBinding fileName <$> NE.toList as) $ go b - -- expr@(ExprWhere _ (Where a _ bs)) -> do - -- let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - -- positioned ann . AST.Let AST.FromWhere (goLetBinding <$> bs) $ go a - expr@(ExprDo _ (DoBlock kw stmts)) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - positioned ann . AST.Do (moduleName $ tokValue kw) $ goDoStatement <$> NE.toList stmts - expr@(ExprAdo _ (AdoBlock kw stms _ a)) -> do - let ann = uncurry (sourceAnnCommented fileName) $ exprRange expr - positioned ann . AST.Ado (moduleName $ tokValue kw) (goDoStatement <$> stms) $ go a - -convertBinder :: String -> Binder a -> AST.Binder -convertBinder fileName = go - where - positioned = - uncurry AST.PositionedBinder - - go = \case - BinderWildcard _ a -> - positioned (sourceAnnCommented fileName a a) AST.NullBinder - BinderVar _ a -> do - let ann = sourceName fileName a - positioned ann . AST.VarBinder (fst ann) . ident $ nameValue a - binder@(BinderNamed _ a _ b) -> do - let ann = uncurry (sourceAnnCommented fileName) $ binderRange binder - positioned ann . AST.NamedBinder (fst ann) (ident $ nameValue a) $ go b - binder@(BinderConstructor _ a bs) -> do - let ann = uncurry (sourceAnnCommented fileName) $ binderRange binder - positioned ann . AST.ConstructorBinder (fst ann) (qualified a) $ go <$> bs - BinderBoolean _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.LiteralBinder (fst ann) $ AST.BooleanLiteral b - BinderChar _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.LiteralBinder (fst ann) $ AST.CharLiteral b - BinderString _ a b -> do - let ann = sourceAnnCommented fileName a a - positioned ann . AST.LiteralBinder (fst ann) . AST.StringLiteral $ b - BinderNumber _ n a b -> do - let - ann = sourceAnnCommented fileName a a - b' - | isJust n = bimap negate negate b - | otherwise = b - positioned ann . AST.LiteralBinder (fst ann) $ AST.NumericLiteral b' - BinderArray _ (Wrapped a bs c) -> do - let - ann = sourceAnnCommented fileName a c - vals = case bs of - Just (Separated x xs) -> go x : (go . snd <$> xs) - Nothing -> [] - positioned ann . AST.LiteralBinder (fst ann) $ AST.ArrayLiteral vals - BinderRecord z (Wrapped a bs c) -> do - let - ann = sourceAnnCommented fileName a c - lbl = \case - RecordPun f -> (mkString . getIdent $ nameValue f, go $ BinderVar z f) - RecordField f _ v -> (lblName f, go v) - vals = case bs of - Just (Separated x xs) -> lbl x : (lbl . snd <$> xs) - Nothing -> [] - positioned ann . AST.LiteralBinder (fst ann) $ AST.ObjectLiteral vals - BinderParens _ (Wrapped a b c) -> - positioned (sourceAnnCommented fileName a c) . AST.ParensInBinder $ go b - binder@(BinderTyped _ a _ b) -> do - let - a' = go a - b' = convertType fileName b - ann = (sourceSpan fileName . toSourceRange $ binderRange binder, []) - positioned ann $ AST.TypedBinder b' a' - binder@(BinderOp _ _ _ _) -> do - let - ann = uncurry (sourceAnn fileName) $ binderRange binder - reassoc op b a = do - let op' = AST.OpBinder (sourceSpan fileName . toSourceRange $ qualRange op) $ qualified op - AST.BinaryNoParensBinder op' (go a) b - loop k = \case - BinderOp _ a op b -> loop (reassoc op (k b)) a - binder' -> k binder' - positioned ann $ loop go binder - -convertDeclaration :: String -> Declaration a -> [AST.Declaration] -convertDeclaration fileName decl = case decl of - DeclData _ (DataHead _ a vars) bd -> do - let - ctrs :: SourceToken -> DataCtor b -> [(SourceToken, DataCtor b)] -> [AST.DataConstructorDeclaration] - ctrs st (DataCtor _ name fields) tl - = AST.DataConstructorDeclaration (sourceAnnCommented fileName st (nameTok name)) (nameValue name) (zip ctrFields $ convertType fileName <$> fields) - : (case tl of - [] -> [] - (st', ctor) : tl' -> ctrs st' ctor tl' - ) - pure $ AST.DataDeclaration ann Env.Data (nameValue a) (goTypeVar <$> vars) (maybe [] (\(st, Separated hd tl) -> ctrs st hd tl) bd) - DeclType _ (DataHead _ a vars) _ bd -> - pure $ AST.TypeSynonymDeclaration ann - (nameValue a) - (goTypeVar <$> vars) - (convertType fileName bd) - DeclNewtype _ (DataHead _ a vars) st x ys -> do - let ctrs = [AST.DataConstructorDeclaration (sourceAnnCommented fileName st (snd $ declRange decl)) (nameValue x) [(headDef (internalError "No constructor name") ctrFields, convertType fileName ys)]] - pure $ AST.DataDeclaration ann Env.Newtype (nameValue a) (goTypeVar <$> vars) ctrs - DeclClass _ (ClassHead _ sup name vars fdeps) bd -> do - let - goTyVar (TypeVarKinded (Wrapped _ (Labeled (_, a) _ _) _)) = nameValue a - goTyVar (TypeVarName (_, a)) = nameValue a - vars' = zip (toList $ goTyVar <$> vars) [0..] - goName = fromJust . flip lookup vars' . nameValue - goFundep (FundepDetermined _ bs) = Env.FunctionalDependency [] (goName <$> NE.toList bs) - goFundep (FundepDetermines as _ bs) = Env.FunctionalDependency (goName <$> NE.toList as) (goName <$> NE.toList bs) - goSig (Labeled n _ ty) = do - let - ty' = convertType fileName ty - ann' = widenLeft (tokAnn $ nameTok n) $ T.getAnnForType ty' - AST.TypeDeclaration $ AST.TypeDeclarationData ann' (ident $ nameValue n) ty' - pure $ AST.TypeClassDeclaration ann - (nameValue name) - (goTypeVar <$> vars) - (convertConstraint False fileName <$> maybe [] (toList . fst) sup) - (goFundep <$> maybe [] (toList . snd) fdeps) - (goSig <$> maybe [] (NE.toList . snd) bd) - DeclInstanceChain _ insts -> do - let - chainId = mkChainId fileName $ startSourcePos $ instKeyword $ instHead $ sepHead insts - goInst ix inst@(Instance (InstanceHead _ nameSep ctrs cls args) bd) = do - let ann' = uncurry (sourceAnnCommented fileName) $ instanceRange inst - clsAnn = findInstanceAnn cls args - AST.TypeInstanceDeclaration ann' clsAnn chainId ix - (mkPartialInstanceName nameSep cls args) - (convertConstraint False fileName <$> maybe [] (toList . fst) ctrs) - (qualified cls) - (convertType fileName <$> args) - (AST.ExplicitInstance $ goInstanceBinding <$> maybe [] (NE.toList . snd) bd) - uncurry goInst <$> zip [0..] (toList insts) - DeclDerive _ _ new (InstanceHead kw nameSep ctrs cls args) -> do - let - chainId = mkChainId fileName $ startSourcePos kw - name' = mkPartialInstanceName nameSep cls args - instTy - | isJust new = AST.NewtypeInstance - | otherwise = AST.DerivedInstance - clsAnn = findInstanceAnn cls args - pure $ AST.TypeInstanceDeclaration ann clsAnn chainId 0 name' - (convertConstraint False fileName <$> maybe [] (toList . fst) ctrs) - (qualified cls) - (convertType fileName <$> args) - instTy - DeclKindSignature _ kw (Labeled name _ ty) -> do - let - kindFor = case tokValue kw of - TokLowerName [] "data" -> AST.DataSig - TokLowerName [] "newtype" -> AST.NewtypeSig - TokLowerName [] "type" -> AST.TypeSynonymSig - TokLowerName [] "class" -> AST.ClassSig - tok -> internalError $ "Invalid kind signature keyword " <> Text.unpack (printToken tok) - pure . AST.KindDeclaration ann kindFor (nameValue name) $ convertType fileName ty - DeclSignature _ lbl -> - pure $ convertSignature fileName lbl - DeclValue _ fields -> - pure $ convertValueBindingFields fileName ann fields - DeclFixity _ (FixityFields (_, kw) (_, prec) fxop) -> do - let - assoc = case kw of - Infix -> AST.Infix - Infixr -> AST.Infixr - Infixl -> AST.Infixl - fixity = AST.Fixity assoc prec - pure $ AST.FixityDeclaration ann $ case fxop of - FixityValue name _ op -> do - Left $ AST.ValueFixity fixity (first ident <$> qualified name) (nameValue op) - FixityType _ name _ op -> - Right $ AST.TypeFixity fixity (qualified name) (nameValue op) - DeclForeign _ _ _ frn -> - pure $ case frn of - ForeignValue (Labeled a _ b) -> - AST.ExternDeclaration ann (ident $ nameValue a) $ convertType fileName b - ForeignData _ (Labeled a _ b) -> - AST.ExternDataDeclaration ann (nameValue a) $ convertType fileName b - ForeignKind _ a -> - AST.DataDeclaration ann Env.Data (nameValue a) [] [] - DeclRole _ _ _ name roles -> - pure $ AST.RoleDeclaration $ - AST.RoleDeclarationData ann (nameValue name) (roleValue <$> NE.toList roles) - where - ann = - uncurry (sourceAnnCommented fileName) $ declRange decl - - startSourcePos :: SourceToken -> Pos.SourcePos - startSourcePos = sourcePos . srcStart . tokRange . tokAnn - - mkPartialInstanceName :: Maybe (Name Ident, SourceToken) -> QualifiedName (N.ProperName 'N.ClassName) -> [Type a] -> Either Text.Text N.Ident - mkPartialInstanceName nameSep cls args = - maybe (Left genName) (Right . ident . nameValue . fst) nameSep - where - -- truncate to 25 chars to reduce verbosity - -- of name and still keep it readable - -- name will be used to create a GenIdent - -- in desugaring process - genName :: Text.Text - genName = Text.take 25 (className <> typeArgs) - - className :: Text.Text - className - = foldMap (uncurry Text.cons . first toLower) - . Text.uncons - . N.runProperName - $ qualName cls - - typeArgs :: Text.Text - typeArgs = foldMap argName args - - argName :: Type a -> Text.Text - argName = \case - -- These are only useful to disambiguate between overlapping instances - -- but they’re disallowed outside of instance chains. Since we’re - -- avoiding name collisions with unique identifiers anyway, - -- we don't need to render this constructor. - TypeVar{} -> "" - TypeConstructor _ qn -> N.runProperName $ qualName qn - TypeOpName _ qn -> N.runOpName $ qualName qn - TypeString _ _ ps -> prettyPrintStringJS ps - TypeInt _ _ _ nt -> Text.pack $ show nt - - -- Typed holes are disallowed in instance heads - TypeHole{} -> "" - TypeParens _ t -> argName $ wrpValue t - TypeKinded _ t1 _ t2 -> argName t1 <> argName t2 - TypeRecord _ _ -> "Record" - TypeRow _ _ -> "Row" - TypeArrName _ _ -> "Function" - TypeWildcard{} -> "_" - - -- Polytypes are disallowed in instance heads - TypeForall{} -> "" - TypeApp _ t1 t2 -> argName t1 <> argName t2 - TypeOp _ t1 op t2 -> - argName t1 <> N.runOpName (qualName op) <> argName t2 - TypeArr _ t1 _ t2 -> argName t1 <> "Function" <> argName t2 - TypeConstrained{} -> "" - TypeUnaryRow{} -> "Row" - - goTypeVar = \case - TypeVarKinded (Wrapped _ (Labeled (_, x) _ y) _) -> (getIdent $ nameValue x, Just $ convertType fileName y) - TypeVarName (_, x) -> (getIdent $ nameValue x, Nothing) - - goInstanceBinding = \case - InstanceBindingSignature _ lbl -> - convertSignature fileName lbl - binding@(InstanceBindingName _ fields) -> do - let ann' = uncurry (sourceAnnCommented fileName) $ instanceBindingRange binding - convertValueBindingFields fileName ann' fields - - findInstanceAnn cls args = uncurry (sourceAnnCommented fileName) $ - if null args then - qualRange cls - else - (fst $ qualRange cls, snd $ typeRange $ last args) - -convertSignature :: String -> Labeled (Name Ident) (Type a) -> AST.Declaration -convertSignature fileName (Labeled a _ b) = do - let - b' = convertType fileName b - ann = widenLeft (tokAnn $ nameTok a) $ T.getAnnForType b' - AST.TypeDeclaration $ AST.TypeDeclarationData ann (ident $ nameValue a) b' - -convertValueBindingFields :: String -> Pos.SourceAnn -> ValueBindingFields a -> AST.Declaration -convertValueBindingFields fileName ann (ValueBindingFields a bs c) = do - let - bs' = convertBinder fileName <$> bs - cs' = convertGuarded fileName c - AST.ValueDeclaration $ AST.ValueDeclarationData ann (ident $ nameValue a) Env.Public bs' cs' - -convertImportDecl - :: String - -> ImportDecl a - -> (Pos.SourceAnn, N.ModuleName, AST.ImportDeclarationType, Maybe N.ModuleName) -convertImportDecl fileName decl@(ImportDecl _ _ modName mbNames mbQual) = do - let - ann = uncurry (sourceAnnCommented fileName) $ importDeclRange decl - importTy = case mbNames of - Nothing -> AST.Implicit - Just (hiding, Wrapped _ imps _) -> do - let imps' = convertImport fileName <$> toList imps - if isJust hiding - then AST.Hiding imps' - else AST.Explicit imps' - (ann, nameValue modName, importTy, nameValue . snd <$> mbQual) - -convertImport :: String -> Import a -> AST.DeclarationRef -convertImport fileName imp = case imp of - ImportValue _ a -> - AST.ValueRef ann . ident $ nameValue a - ImportOp _ a -> - AST.ValueOpRef ann $ nameValue a - ImportType _ a mb -> do - let - ctrs = case mb of - Nothing -> Just [] - Just (DataAll _ _) -> Nothing - Just (DataEnumerated _ (Wrapped _ Nothing _)) -> Just [] - Just (DataEnumerated _ (Wrapped _ (Just idents) _)) -> - Just . map nameValue $ toList idents - AST.TypeRef ann (nameValue a) ctrs - ImportTypeOp _ _ a -> - AST.TypeOpRef ann $ nameValue a - ImportClass _ _ a -> - AST.TypeClassRef ann $ nameValue a - where - ann = sourceSpan fileName . toSourceRange $ importRange imp - -convertExport :: String -> Export a -> AST.DeclarationRef -convertExport fileName export = case export of - ExportValue _ a -> - AST.ValueRef ann . ident $ nameValue a - ExportOp _ a -> - AST.ValueOpRef ann $ nameValue a - ExportType _ a mb -> do - let - ctrs = case mb of - Nothing -> Just [] - Just (DataAll _ _) -> Nothing - Just (DataEnumerated _ (Wrapped _ Nothing _)) -> Just [] - Just (DataEnumerated _ (Wrapped _ (Just idents) _)) -> - Just . map nameValue $ toList idents - AST.TypeRef ann (nameValue a) ctrs - ExportTypeOp _ _ a -> - AST.TypeOpRef ann $ nameValue a - ExportClass _ _ a -> - AST.TypeClassRef ann $ nameValue a - ExportModule _ _ a -> - AST.ModuleRef ann (nameValue a) - where - ann = sourceSpan fileName . toSourceRange $ exportRange export - -convertModule :: String -> Module a -> AST.Module -convertModule fileName module'@(Module _ _ modName exps _ imps decls _) = do - let - ann = uncurry (sourceAnnCommented fileName) $ moduleRange module' - imps' = importCtr. convertImportDecl fileName <$> imps - decls' = convertDeclaration fileName =<< decls - exps' = map (convertExport fileName) . toList . wrpValue <$> exps - uncurry AST.Module ann (nameValue modName) (imps' <> decls') exps' - where - importCtr (a, b, c, d) = AST.ImportDeclaration a b c d - -ctrFields :: [N.Ident] -ctrFields = [N.Ident ("value" <> Text.pack (show (n :: Integer))) | n <- [0..]] diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Errors.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Errors.hs deleted file mode 100644 index 3682f2f0..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Errors.hs +++ /dev/null @@ -1,201 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -module Language.PureScript.CST.Errors - ( ParserErrorInfo(..) - , ParserErrorType(..) - , ParserWarningType(..) - , ParserError - , ParserWarning - , prettyPrintError - , prettyPrintErrorMessage - , prettyPrintWarningMessage - ) where - -import Prelude - -import Control.DeepSeq (NFData) -import Data.Text qualified as Text -import Data.Char (isSpace, toUpper) -import GHC.Generics (Generic) -import Language.PureScript.CST.Layout (LayoutStack) -import Language.PureScript.CST.Print (printToken) -import Language.PureScript.CST.Types (SourcePos(..), SourceRange(..), SourceToken(..), Token(..)) -import Text.Printf (printf) - -data ParserErrorType - = ErrWildcardInType - | ErrConstraintInKind - | ErrHoleInType - | ErrExprInBinder - | ErrExprInDeclOrBinder - | ErrExprInDecl - | ErrBinderInDecl - | ErrRecordUpdateInCtr - | ErrRecordPunInUpdate - | ErrRecordCtrInUpdate - | ErrTypeInConstraint - | ErrElseInDecl - | ErrInstanceNameMismatch - | ErrUnknownFundep - | ErrImportInDecl - | ErrGuardInLetBinder - | ErrKeywordVar - | ErrKeywordSymbol - | ErrQuotedPun - | ErrToken - | ErrLineFeedInString - | ErrAstralCodePointInChar - | ErrCharEscape - | ErrNumberOutOfRange - | ErrLeadingZero - | ErrExpectedFraction - | ErrExpectedExponent - | ErrExpectedHex - | ErrReservedSymbol - | ErrCharInGap Char - | ErrModuleName - | ErrQualifiedName - | ErrEmptyDo - | ErrLexeme (Maybe String) [String] - | ErrConstraintInForeignImportSyntax - | ErrEof - | ErrCustom String - deriving (Show, Eq, Ord, Generic, NFData) - -data ParserWarningType - = WarnDeprecatedRowSyntax - | WarnDeprecatedForeignKindSyntax - | WarnDeprecatedKindImportSyntax - | WarnDeprecatedKindExportSyntax - | WarnDeprecatedCaseOfOffsideSyntax - deriving (Show, Eq, Ord, Generic, NFData) - -data ParserErrorInfo a = ParserErrorInfo - { errRange :: SourceRange - , errToks :: [SourceToken] - , errStack :: LayoutStack - , errType :: a - } deriving (Show, Eq, Generic, NFData) - -type ParserError = ParserErrorInfo ParserErrorType -type ParserWarning = ParserErrorInfo ParserWarningType - -prettyPrintError :: ParserError -> String -prettyPrintError pe@ParserErrorInfo { errRange } = - prettyPrintErrorMessage pe <> " at " <> errPos - where - errPos = case errRange of - SourceRange (SourcePos line col) _ -> - "line " <> show line <> ", column " <> show col - -prettyPrintErrorMessage :: ParserError -> String -prettyPrintErrorMessage ParserErrorInfo {..} = case errType of - ErrWildcardInType -> - "Unexpected wildcard in type; type wildcards are only allowed in value annotations" - ErrConstraintInKind -> - "Unsupported constraint in kind; constraints are only allowed in value annotations" - ErrHoleInType -> - "Unexpected hole in type; type holes are only allowed in value annotations" - ErrExprInBinder -> - "Expected pattern, saw expression" - ErrExprInDeclOrBinder -> - "Expected declaration or pattern, saw expression" - ErrExprInDecl -> - "Expected declaration, saw expression" - ErrBinderInDecl -> - "Expected declaration, saw pattern" - ErrRecordUpdateInCtr -> - "Expected ':', saw '='" - ErrRecordPunInUpdate -> - "Expected record update, saw pun" - ErrRecordCtrInUpdate -> - "Expected '=', saw ':'" - ErrTypeInConstraint -> - "Expected constraint, saw type" - ErrElseInDecl -> - "Expected declaration, saw 'else'" - ErrInstanceNameMismatch -> - "All instances in a chain must implement the same type class" - ErrUnknownFundep -> - "Unknown type variable in functional dependency" - ErrImportInDecl -> - "Expected declaration, saw 'import'" - ErrGuardInLetBinder -> - "Unexpected guard in let pattern" - ErrKeywordVar -> - "Expected variable, saw keyword" - ErrKeywordSymbol -> - "Expected symbol, saw reserved symbol" - ErrQuotedPun -> - "Unexpected quoted label in record pun, perhaps due to a missing ':'" - ErrEof -> - "Unexpected end of input" - ErrLexeme (Just (hd : _)) _ | isSpace hd -> - "Illegal whitespace character " <> displayCodePoint hd - ErrLexeme (Just a) _ -> - "Unexpected " <> a - ErrLineFeedInString -> - "Unexpected line feed in string literal" - ErrAstralCodePointInChar -> - "Illegal astral code point in character literal" - ErrCharEscape -> - "Illegal character escape code" - ErrNumberOutOfRange -> - "Number literal is out of range" - ErrLeadingZero -> - "Unexpected leading zeros" - ErrExpectedFraction -> - "Expected fraction" - ErrExpectedExponent -> - "Expected exponent" - ErrExpectedHex -> - "Expected hex digit" - ErrReservedSymbol -> - "Unexpected reserved symbol" - ErrCharInGap ch -> - "Unexpected character '" <> [ch] <> "' in gap" - ErrModuleName -> - "Invalid module name; underscores and primes are not allowed in module names" - ErrQualifiedName -> - "Unexpected qualified name" - ErrEmptyDo -> - "Expected do statement" - ErrLexeme _ _ -> - basicError - ErrConstraintInForeignImportSyntax -> - "Constraints are not allowed in foreign imports. Omit the constraint instead and update the foreign module accordingly." - ErrToken - | SourceToken _ (TokLeftArrow _) : _ <- errToks -> - "Unexpected \"<-\" in expression, perhaps due to a missing 'do' or 'ado' keyword" - ErrToken -> - basicError - ErrCustom err -> - err - - where - basicError = case errToks of - tok : _ -> basicTokError (tokValue tok) - [] -> "Unexpected input" - - basicTokError = \case - TokLayoutStart -> "Unexpected or mismatched indentation" - TokLayoutSep -> "Unexpected or mismatched indentation" - TokLayoutEnd -> "Unexpected or mismatched indentation" - TokEof -> "Unexpected end of input" - tok -> "Unexpected token '" <> Text.unpack (printToken tok) <> "'" - - displayCodePoint :: Char -> String - displayCodePoint x = - "U+" <> map toUpper (printf "%0.4x" (fromEnum x)) - -prettyPrintWarningMessage :: ParserWarning -> String -prettyPrintWarningMessage ParserErrorInfo {..} = case errType of - WarnDeprecatedRowSyntax -> - "Unary '#' syntax for row kinds is deprecated and will be removed in a future release. Use the 'Row' kind instead." - WarnDeprecatedForeignKindSyntax -> - "Foreign kind imports are deprecated and will be removed in a future release. Use empty 'data' instead." - WarnDeprecatedKindImportSyntax -> - "Kind imports are deprecated and will be removed in a future release. Omit the 'kind' keyword instead." - WarnDeprecatedKindExportSyntax -> - "Kind exports are deprecated and will be removed in a future release. Omit the 'kind' keyword instead." - WarnDeprecatedCaseOfOffsideSyntax -> - "Dedented expressions in case branches are deprecated and will be removed in a future release. Indent the branch's expression past it's binder instead." diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Flatten.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Flatten.hs deleted file mode 100644 index 89061407..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Flatten.hs +++ /dev/null @@ -1,315 +0,0 @@ -module Language.PureScript.CST.Flatten where - -import Prelude - -import Data.DList (DList) -import Language.PureScript.CST.Types -import Language.PureScript.CST.Positions (advanceLeading, moduleRange, srcRange) - -flattenModule :: Module a -> DList SourceToken -flattenModule m@(Module _ a b c d e f g) = - pure a <> - flattenName b <> - foldMap (flattenWrapped (flattenSeparated flattenExport)) c <> - pure d <> - foldMap flattenImportDecl e <> - foldMap flattenDeclaration f <> - pure (SourceToken (TokenAnn eofRange g []) TokEof) - where - (_, endTkn) = moduleRange m - eofPos = advanceLeading (srcEnd (srcRange endTkn)) g - eofRange = SourceRange eofPos eofPos - -flattenDataHead :: DataHead a -> DList SourceToken -flattenDataHead (DataHead a b c) = pure a <> flattenName b <> foldMap flattenTypeVarBinding c - -flattenDataCtor :: DataCtor a -> DList SourceToken -flattenDataCtor (DataCtor _ a b) = flattenName a <> foldMap flattenType b - -flattenClassHead :: ClassHead a -> DList SourceToken -flattenClassHead (ClassHead a b c d e) = - pure a <> - foldMap (\(f, g) -> flattenOneOrDelimited flattenConstraint f <> pure g) b <> - flattenName c <> - foldMap flattenTypeVarBinding d <> - foldMap (\(f, g) -> pure f <> flattenSeparated flattenClassFundep g) e - -flattenClassFundep :: ClassFundep -> DList SourceToken -flattenClassFundep = \case - FundepDetermined a b -> - pure a <> foldMap flattenName b - FundepDetermines a b c -> - foldMap flattenName a <> pure b <> foldMap flattenName c - -flattenInstance :: Instance a -> DList SourceToken -flattenInstance (Instance a b) = - flattenInstanceHead a <> foldMap (\(c, d) -> pure c <> foldMap flattenInstanceBinding d) b - -flattenInstanceHead :: InstanceHead a -> DList SourceToken -flattenInstanceHead (InstanceHead a b c d e) = - pure a <> - foldMap (\(n, s) -> flattenName n <> pure s) b <> - foldMap (\(g, h) -> flattenOneOrDelimited flattenConstraint g <> pure h) c <> - flattenQualifiedName d <> - foldMap flattenType e - -flattenInstanceBinding :: InstanceBinding a -> DList SourceToken -flattenInstanceBinding = \case - InstanceBindingSignature _ a -> flattenLabeled flattenName flattenType a - InstanceBindingName _ a -> flattenValueBindingFields a - -flattenValueBindingFields :: ValueBindingFields a -> DList SourceToken -flattenValueBindingFields (ValueBindingFields a b c) = - flattenName a <> - foldMap flattenBinder b <> - flattenGuarded c - -flattenBinder :: Binder a -> DList SourceToken -flattenBinder = \case - BinderWildcard _ a -> pure a - BinderVar _ a -> flattenName a - BinderNamed _ a b c -> flattenName a <> pure b <> flattenBinder c - BinderConstructor _ a b -> flattenQualifiedName a <> foldMap flattenBinder b - BinderBoolean _ a _ -> pure a - BinderChar _ a _ -> pure a - BinderString _ a _ -> pure a - BinderNumber _ a b _ -> foldMap pure a <> pure b - BinderArray _ a -> flattenWrapped (foldMap (flattenSeparated flattenBinder)) a - BinderRecord _ a -> - flattenWrapped (foldMap (flattenSeparated (flattenRecordLabeled flattenBinder))) a - BinderParens _ a -> flattenWrapped flattenBinder a - BinderTyped _ a b c -> flattenBinder a <> pure b <> flattenType c - BinderOp _ a b c -> flattenBinder a <> flattenQualifiedName b <> flattenBinder c - -flattenRecordLabeled :: (a -> DList SourceToken) -> RecordLabeled a -> DList SourceToken -flattenRecordLabeled f = \case - RecordPun a -> flattenName a - RecordField a b c -> flattenLabel a <> pure b <> f c - -flattenRecordAccessor :: RecordAccessor a -> DList SourceToken -flattenRecordAccessor (RecordAccessor a b c) = - flattenExpr a <> pure b <> flattenSeparated flattenLabel c - -flattenRecordUpdate :: RecordUpdate a -> DList SourceToken -flattenRecordUpdate = \case - RecordUpdateLeaf a b c -> flattenLabel a <> pure b <> flattenExpr c - RecordUpdateBranch a b -> - flattenLabel a <> flattenWrapped (flattenSeparated flattenRecordUpdate) b - -flattenLambda :: Lambda a -> DList SourceToken -flattenLambda (Lambda a b c d) = - pure a <> foldMap flattenBinder b <> pure c <> flattenExpr d - -flattenIfThenElse :: IfThenElse a -> DList SourceToken -flattenIfThenElse (IfThenElse a b c d e f) = - pure a <> flattenExpr b <> pure c <> flattenExpr d <> pure e <> flattenExpr f - -flattenCaseOf :: CaseOf a -> DList SourceToken -flattenCaseOf (CaseOf a b c d) = - pure a <> - flattenSeparated flattenExpr b <> - pure c <> - foldMap (\(e, f) -> flattenSeparated flattenBinder e <> flattenGuarded f) d - -flattenLetIn :: LetIn a -> DList SourceToken -flattenLetIn (LetIn a b c d) = - pure a <> foldMap flattenLetBinding b <> pure c <> flattenExpr d - -flattenDoBlock :: DoBlock a -> DList SourceToken -flattenDoBlock (DoBlock a b) = - pure a <> foldMap flattenDoStatement b - -flattenAdoBlock :: AdoBlock a -> DList SourceToken -flattenAdoBlock (AdoBlock a b c d) = - pure a <> foldMap flattenDoStatement b <> pure c <> flattenExpr d - -flattenDoStatement :: DoStatement a -> DList SourceToken -flattenDoStatement = \case - DoLet a b -> pure a <> foldMap flattenLetBinding b - DoDiscard a -> flattenExpr a - DoBind a b c -> flattenBinder a <> pure b <> flattenExpr c - -flattenExpr :: Expr a -> DList SourceToken -flattenExpr = \case - ExprHole _ a -> flattenName a - ExprSection _ a -> pure a - ExprIdent _ a -> flattenQualifiedName a - ExprConstructor _ a -> flattenQualifiedName a - ExprBoolean _ a _ -> pure a - ExprChar _ a _ -> pure a - ExprString _ a _ -> pure a - ExprNumber _ a _ -> pure a - ExprArray _ a -> flattenWrapped (foldMap (flattenSeparated flattenExpr)) a - ExprRecord _ a -> - flattenWrapped (foldMap (flattenSeparated (flattenRecordLabeled flattenExpr))) a - ExprParens _ a -> flattenWrapped flattenExpr a - ExprTyped _ a b c -> flattenExpr a <> pure b <> flattenType c - ExprInfix _ a b c -> flattenExpr a <> flattenWrapped flattenExpr b <> flattenExpr c - ExprOp _ a b c -> flattenExpr a <> flattenQualifiedName b <> flattenExpr c - ExprOpName _ a -> flattenQualifiedName a - ExprNegate _ a b -> pure a <> flattenExpr b - ExprRecordAccessor _ a -> flattenRecordAccessor a - ExprRecordUpdate _ a b -> flattenExpr a <> flattenWrapped (flattenSeparated flattenRecordUpdate) b - ExprApp _ a b -> flattenExpr a <> flattenExpr b - ExprVisibleTypeApp _ a b c -> flattenExpr a <> pure b <> flattenType c - ExprLambda _ a -> flattenLambda a - ExprIf _ a -> flattenIfThenElse a - ExprCase _ a -> flattenCaseOf a - ExprLet _ a -> flattenLetIn a - ExprDo _ a -> flattenDoBlock a - ExprAdo _ a -> flattenAdoBlock a - -flattenLetBinding :: LetBinding a -> DList SourceToken -flattenLetBinding = \case - LetBindingSignature _ a -> flattenLabeled flattenName flattenType a - LetBindingName _ a -> flattenValueBindingFields a - LetBindingPattern _ a b c -> flattenBinder a <> pure b <> flattenWhere c - -flattenWhere :: Where a -> DList SourceToken -flattenWhere (Where a b) = - flattenExpr a <> foldMap (\(c, d) -> pure c <> foldMap flattenLetBinding d) b - -flattenPatternGuard :: PatternGuard a -> DList SourceToken -flattenPatternGuard (PatternGuard a b) = - foldMap (\(c, d) -> flattenBinder c <> pure d) a <> flattenExpr b - -flattenGuardedExpr :: GuardedExpr a -> DList SourceToken -flattenGuardedExpr (GuardedExpr a b c d) = - pure a <> - flattenSeparated flattenPatternGuard b <> - pure c <> - flattenWhere d - -flattenGuarded :: Guarded a -> DList SourceToken -flattenGuarded = \case - Unconditional a b -> pure a <> flattenWhere b - Guarded a -> foldMap flattenGuardedExpr a - -flattenFixityFields :: FixityFields -> DList SourceToken -flattenFixityFields (FixityFields (a, _) (b, _) c) = - pure a <> pure b <> flattenFixityOp c - -flattenFixityOp :: FixityOp -> DList SourceToken -flattenFixityOp = \case - FixityValue a b c -> flattenQualifiedName a <> pure b <> flattenName c - FixityType a b c d -> pure a <> flattenQualifiedName b <> pure c <> flattenName d - -flattenForeign :: Foreign a -> DList SourceToken -flattenForeign = \case - ForeignValue a -> flattenLabeled flattenName flattenType a - ForeignData a b -> pure a <> flattenLabeled flattenName flattenType b - ForeignKind a b -> pure a <> flattenName b - -flattenRole :: Role -> DList SourceToken -flattenRole = pure . roleTok - -flattenDeclaration :: Declaration a -> DList SourceToken -flattenDeclaration = \case - DeclData _ a b -> - flattenDataHead a <> - foldMap (\(t, cs) -> pure t <> flattenSeparated flattenDataCtor cs) b - DeclType _ a b c ->flattenDataHead a <> pure b <> flattenType c - DeclNewtype _ a b c d -> flattenDataHead a <> pure b <> flattenName c <> flattenType d - DeclClass _ a b -> - flattenClassHead a <> - foldMap (\(c, d) -> pure c <> foldMap (flattenLabeled flattenName flattenType) d) b - DeclInstanceChain _ a -> flattenSeparated flattenInstance a - DeclDerive _ a b c -> pure a <> foldMap pure b <> flattenInstanceHead c - DeclKindSignature _ a b -> pure a <> flattenLabeled flattenName flattenType b - DeclSignature _ a -> flattenLabeled flattenName flattenType a - DeclFixity _ a -> flattenFixityFields a - DeclForeign _ a b c -> pure a <> pure b <> flattenForeign c - DeclRole _ a b c d -> pure a <> pure b <> flattenName c <> foldMap flattenRole d - DeclValue _ a -> flattenValueBindingFields a - -flattenQualifiedName :: QualifiedName a -> DList SourceToken -flattenQualifiedName = pure . qualTok - -flattenName :: Name a -> DList SourceToken -flattenName = pure . nameTok - -flattenLabel :: Label -> DList SourceToken -flattenLabel = pure . lblTok - -flattenExport :: Export a -> DList SourceToken -flattenExport = \case - ExportValue _ n -> flattenName n - ExportOp _ n -> flattenName n - ExportType _ n dms -> flattenName n <> foldMap flattenDataMembers dms - ExportTypeOp _ t n -> pure t <> flattenName n - ExportClass _ t n -> pure t <> flattenName n - ExportModule _ t n -> pure t <> flattenName n - -flattenDataMembers :: DataMembers a -> DList SourceToken -flattenDataMembers = \case - DataAll _ t -> pure t - DataEnumerated _ ns -> flattenWrapped (foldMap (flattenSeparated flattenName)) ns - -flattenImportDecl :: ImportDecl a -> DList SourceToken -flattenImportDecl (ImportDecl _ a b c d) = - pure a <> - flattenName b <> - foldMap (\(mt, is) -> - foldMap pure mt <> flattenWrapped (flattenSeparated flattenImport) is) c <> - foldMap (\(t, n) -> pure t <> flattenName n) d - -flattenImport :: Import a -> DList SourceToken -flattenImport = \case - ImportValue _ n -> flattenName n - ImportOp _ n -> flattenName n - ImportType _ n dms -> flattenName n <> foldMap flattenDataMembers dms - ImportTypeOp _ t n -> pure t <> flattenName n - ImportClass _ t n -> pure t <> flattenName n - -flattenWrapped :: (a -> DList SourceToken) -> Wrapped a -> DList SourceToken -flattenWrapped k (Wrapped a b c) = pure a <> k b <> pure c - -flattenSeparated :: (a -> DList SourceToken) -> Separated a -> DList SourceToken -flattenSeparated k (Separated a b) = k a <> foldMap (\(c, d) -> pure c <> k d) b - -flattenOneOrDelimited - :: (a -> DList SourceToken) -> OneOrDelimited a -> DList SourceToken -flattenOneOrDelimited f = \case - One a -> f a - Many a -> flattenWrapped (flattenSeparated f) a - -flattenLabeled :: (a -> DList SourceToken) -> (b -> DList SourceToken) -> Labeled a b -> DList SourceToken -flattenLabeled ka kc (Labeled a b c) = ka a <> pure b <> kc c - -flattenType :: Type a -> DList SourceToken -flattenType = \case - TypeVar _ a -> pure $ nameTok a - TypeConstructor _ a -> pure $ qualTok a - TypeWildcard _ a -> pure a - TypeHole _ a -> pure $ nameTok a - TypeString _ a _ -> pure a - TypeInt _ a b _ -> maybe mempty pure a <> pure b - TypeRow _ a -> flattenWrapped flattenRow a - TypeRecord _ a -> flattenWrapped flattenRow a - TypeForall _ a b c d -> pure a <> foldMap flattenTypeVarBinding b <> pure c <> flattenType d - TypeKinded _ a b c -> flattenType a <> pure b <> flattenType c - TypeApp _ a b -> flattenType a <> flattenType b - TypeOp _ a b c -> flattenType a <> pure (qualTok b) <> flattenType c - TypeOpName _ a -> pure $ qualTok a - TypeArr _ a b c -> flattenType a <> pure b <> flattenType c - TypeArrName _ a -> pure a - TypeConstrained _ a b c -> flattenConstraint a <> pure b <> flattenType c - TypeParens _ a -> flattenWrapped flattenType a - TypeUnaryRow _ a b -> pure a <> flattenType b - -flattenRow :: Row a -> DList SourceToken -flattenRow (Row lbls tl) = - foldMap (flattenSeparated (flattenLabeled (pure . lblTok) flattenType)) lbls - <> foldMap (\(a, b) -> pure a <> flattenType b) tl - -flattenTypeVarBinding :: TypeVarBinding a -> DList SourceToken -flattenTypeVarBinding = \case - TypeVarKinded a -> flattenWrapped (flattenLabeled go flattenType) a - TypeVarName a -> go a - where - go (a, b) = maybe mempty pure a <> pure (nameTok b) - -flattenConstraint :: Constraint a -> DList SourceToken -flattenConstraint = \case - Constraint _ a b -> pure (qualTok a) <> foldMap flattenType b - ConstraintParens _ a -> flattenWrapped flattenConstraint a diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Layout.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Layout.hs deleted file mode 100644 index 2f41df6b..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Layout.hs +++ /dev/null @@ -1,552 +0,0 @@ --- | --- ## High-Level Summary --- --- This section provides a high-level summary of this file. For those who --- know more about compiler-development, the below explanation is likely enough. --- For everyone else, see the next section. --- --- The parser itself is unaware of indentation, and instead only parses explicit --- delimiters which are inserted by this layout algorithm (much like Haskell). --- This is convenient because the actual grammar can be specified apart from the --- indentation rules. Haskell has a few problematic productions which make it --- impossible to implement a purely lexical layout algorithm, so it also has an --- additional (and somewhat contentious) parser error side condition. PureScript --- does not have these problematic productions (particularly foo, bar :: --- SomeType syntax in declarations), but it does have a few gotchas of it's own. --- The algorithm is "non-trivial" to say the least, but it is implemented as a --- purely lexical delimiter parser on a token-by-token basis, which is highly --- convenient, since it can be replicated in any language or toolchain. There is --- likely room to simplify it, but there are some seemingly innocuous things --- that complicate it. --- --- "Naked" commas (case, patterns, guards, fundeps) are a constant source of --- complexity, and indeed too much of this is what prevents Haskell from having --- such an algorithm. Unquoted properties for layout keywords introduce a domino --- effect of complexity since we have to mask and unmask any usage of . (also in --- foralls!) or labels in record literals. --- --- ## Detailed Summary --- --- ### The Problem --- --- The parser itself is unaware of indentation or other such layout concerns. --- Rather than dealing with it explicitly, the parser and its --- grammar rules are only aware of normal tokens (e.g. @TokLowerName@) and --- three special zero-width tokens, @TokLayoutStart@, @TokLayoutSep@, --- and @TokLayoutEnd@. This is convenient because the actual grammar --- can be specified apart from the indentation rules and other such --- layout concerns. --- --- For a simple example, the parser parses all three examples of the code below --- using the exact same grammar rules for the @let@ keyword despite --- each example using different indentations levels: --- --- @ --- -- Example 1 --- let foo = 5 --- x = 2 in foo --- --- -- Example 2 --- let --- bar = 5 --- y = 2 --- in bar --- --- -- Example 3 --- let baz --- = --- 5 --- z= 2 in baz --- @ --- --- Each block of code might appear to the parser as a stream of the --- following source tokens where the @\{@ sequence represents --- @TokLayoutStart@, the @\;@ sequence represents @TokLayoutSep@, --- and the @\}@ sequence represents @TokLayoutEnd@: --- - @let \{foo = 5\;x = 2\} in foo@ --- - @let \{bar = 5\;y = 2\} in bar@ --- - @let \{baz = 5\;z = 2\} in baz@ --- --- --- For a more complex example, consider commas: --- --- @ --- case one, { twoA, twoB }, [ three1 --- , three2 --- , do --- { three3, three4 } <- case arg1, arg2 of --- Nothing, _ -> { three3: 1, three4: 2 } --- Just _, Nothing -> { three3: 2, three4: 3 } --- _, _ -> { three3: 3, three4: 4 } --- pure $ three3 + three4 --- ] of --- @ --- --- Which of the above 13 commas function as the separators between the --- case binders (e.g. @one@) in the outermost @case ... of@ context? --- --- ### The Solution --- --- The parser doesn't have to care about layout concerns (e.g. indentation --- or what starts and ends a context, such as a case binder) because the --- lexer solves that problem instead. --- --- So, how does the lexer solve this problem? It follows this general algorithm: --- 1. Lex the source code text into an initial stream of `SourceToken`s --- that do not have any of the three special tokens mentioned previously. --- 2. On a token-by-token basis, determine whether the lexer should --- 1. insert one of the three special tokens, --- 2. modify the current context (e.g. are we within a case binder? --- Are we in a record expression?) --- --- Step 2 is handled via 'insertLayout' and is essentially a state machine. --- The layout delimiters, (e.g. 'LytCase', 'LytBrace', 'LytProperty', --- and 'LytOf' in the next section's example) either stop certain "rules" --- from applying or ensure that certain "rules" now apply. By "rules", --- we mean whether and where one of the three special tokens are added. --- The comments in the source code for the 'insertLayout' algorithm call --- pushing these delimiters onto the stack "masking" and popping them off --- as "unmasking". Seeing when a layout delimiter is pushed and popped --- are the keys to understanding this algorithm. --- --- ### Walking Through an Example --- --- Before showing an example, let's remember a few things. --- 1. The @TokLowerName "case"@ token (i.e. a "case" keyword) indicates the start --- of a @case ... of@ context. That context includes case binders (like the --- example shown previously) that can get quite complex. When encountered, --- we may need to insert one or more of the three special tokens here --- until we encounter the terminating @TokLowerName "of"@ token that --- signifies its end. --- 2. "case" and "of" can also appear as a record field's name. In such a context, --- they would not start or end a @case ... of@ block. --- --- Given the below source code... --- --- @ --- case { case: "foo", of: "bar" } of --- @ --- --- the lexer would go through something like the following states: --- 1. Encountered @TokLowerName "case"@. Update current context to --- "within a case of expression" by pushing the 'LytCase' delimiter --- onto the layout delimiter stack. Insert the @case@ token --- into the stream of source tokens. --- 2. Encountered @TokLeftBrace@. Update current context to --- "within a record expression" by pushing the 'LytBrace' delimiter. --- Since we expect a field name to be the next token we see, --- which may include a reserved keyword, update the current context again to --- "expecting a field name" by pushing the `LytProperty`. --- delimiter. Insert the @{@ token into the stream of source tokens. --- 3. Encountered @TokLowerName "case"@. Check the current context. --- Since it's a `LytProperty`, this is a field name and we shouldn't --- assume that the next few tokens will be case binders. However, --- since this might be a record with no more fields, update the --- current context back to "within a record expression" by popping --- the `LytProperty` off the layout delimiter stack. Insert the @case@ token --- 4. Encountered @TokColon@. Insert the @:@ token --- 5. Encountered @TokLowerName "foo"@. Insert the @foo@ token. --- 6. Encountered @TokComma@. Check the current context. Since it's a `LytBrace`, --- we're in a record expression and there is another field. Update the --- current context by pushing `LytProperty` as we expect a field name again. --- 7. Encountered @TokLowerName "of"@. Check the current context. --- Since it's a `LytProperty`, this is a field name rather --- than the end of a case binder. Thus, we don't expect the next tokens --- to be the @body@ in a @case ... of body@ expression. However, since --- this might be a record with no more fields, update the current context --- back to "within a record expression" by popping the `LytProperty` --- off the stack. Insert the @of@ token. --- 8. Encountered @TokRightBrace@. Check the current context. --- Since it's a `LytBrace`, this is the end of a record expression. --- Update the current context to "within a case of expression" --- by popping the `LytBrace` off the stack. Insert the @}@ token. --- 9. Encountered @TokLowername "of"@. Check the current context. --- Since it's a 'LytCase', this is the end of a @case ... of@ expression --- and the body will follow. Update the current context to --- "body of a case of expression" by pushing 'LytOf' onto the layout stack. --- Insert the @of@ token into the stream of tokens. --- -{-# LANGUAGE DeriveAnyClass #-} -module Language.PureScript.CST.Layout where - -import Prelude - -import Control.DeepSeq (NFData) -import Data.DList (snoc) -import Data.DList qualified as DList -import Data.Foldable (find) -import Data.Function ((&)) -import GHC.Generics (Generic) -import Language.PureScript.CST.Types (Comment, LineFeed, SourcePos(..), SourceRange(..), SourceToken(..), Token(..), TokenAnn(..)) - -type LayoutStack = [(SourcePos, LayoutDelim)] - -data LayoutDelim - = LytRoot - | LytTopDecl - | LytTopDeclHead - | LytDeclGuard - | LytCase - | LytCaseBinders - | LytCaseGuard - | LytLambdaBinders - | LytParen - | LytBrace - | LytSquare - | LytIf - | LytThen - | LytProperty - | LytForall - | LytTick - | LytLet - | LytLetStmt - | LytWhere - | LytOf - | LytDo - | LytAdo - deriving (Show, Eq, Ord, Generic, NFData) - -isIndented :: LayoutDelim -> Bool -isIndented = \case - LytLet -> True - LytLetStmt -> True - LytWhere -> True - LytOf -> True - LytDo -> True - LytAdo -> True - _ -> False - -isTopDecl :: SourcePos -> LayoutStack -> Bool -isTopDecl tokPos = \case - [(lytPos, LytWhere), (_, LytRoot)] - | srcColumn tokPos == srcColumn lytPos -> True - _ -> False - -lytToken :: SourcePos -> Token -> SourceToken -lytToken pos = SourceToken ann - where - ann = TokenAnn - { tokRange = SourceRange pos pos - , tokLeadingComments = [] - , tokTrailingComments = [] - } - -insertLayout :: SourceToken -> SourcePos -> LayoutStack -> (LayoutStack, [SourceToken]) -insertLayout src@(SourceToken tokAnn tok) nextPos stack = - DList.toList <$> insert (stack, mempty) - where - tokPos = - srcStart $ tokRange tokAnn - - insert state@(stk, acc) = case tok of - -- `data` declarations need masking (LytTopDecl) because the usage of `|` - -- should not introduce a LytDeclGard context. - TokLowerName [] "data" -> - case state & insertDefault of - state'@(stk', _) | isTopDecl tokPos stk' -> - state' & pushStack tokPos LytTopDecl - state' -> - state' & popStack (== LytProperty) - - -- `class` declaration heads need masking (LytTopDeclHead) because the - -- usage of commas in functional dependencies. - TokLowerName [] "class" -> - case state & insertDefault of - state'@(stk', _) | isTopDecl tokPos stk' -> - state' & pushStack tokPos LytTopDeclHead - state' -> - state' & popStack (== LytProperty) - - TokLowerName [] "where" -> - case stk of - (_, LytTopDeclHead) : stk' -> - (stk', acc) & insertToken src & insertStart LytWhere - (_, LytProperty) : stk' -> - (stk', acc) & insertToken src - _ -> - state & collapse whereP & insertToken src & insertStart LytWhere - where - -- `where` always closes do blocks: - -- example = do do do do foo where foo = ... - -- - -- `where` closes layout contexts even when indented at the same level: - -- example = case - -- Foo -> ... - -- Bar -> ... - -- where foo = ... - whereP _ LytDo = True - whereP lytPos lyt = offsideEndP lytPos lyt - - TokLowerName [] "in" -> - case collapse inP state of - -- `let/in` is not allowed in `ado` syntax. `in` is treated as a - -- delimiter and must always close the `ado`. - -- example = ado - -- foo <- ... - -- let bar = ... - -- in ... - ((_, LytLetStmt) : (_, LytAdo) : stk', acc') -> - (stk', acc') & insertEnd & insertEnd & insertToken src - ((_, lyt) : stk', acc') | isIndented lyt -> - (stk', acc') & insertEnd & insertToken src - _ -> - state & insertDefault & popStack (== LytProperty) - where - inP _ LytLet = False - inP _ LytAdo = False - inP _ lyt = isIndented lyt - - TokLowerName [] "let" -> - state & insertKwProperty next - where - next state'@(stk', _) = case stk' of - (p, LytDo) : _ | srcColumn p == srcColumn tokPos -> - state' & insertStart LytLetStmt - (p, LytAdo) : _ | srcColumn p == srcColumn tokPos -> - state' & insertStart LytLetStmt - _ -> - state' & insertStart LytLet - - TokLowerName _ "do" -> - state & insertKwProperty (insertStart LytDo) - - TokLowerName _ "ado" -> - state & insertKwProperty (insertStart LytAdo) - - -- `case` heads need masking due to commas. - TokLowerName [] "case" -> - state & insertKwProperty (pushStack tokPos LytCase) - - TokLowerName [] "of" -> - case collapse indentedP state of - -- When `of` is matched with a `case`, we are in a case block, and we - -- need to mask additional contexts (LytCaseBinders, LytCaseGuards) - -- due to commas. - ((_, LytCase) : stk', acc') -> - (stk', acc') & insertToken src & insertStart LytOf & pushStack nextPos LytCaseBinders - state' -> - state' & insertDefault & popStack (== LytProperty) - - -- `if/then/else` is considered a delimiter context. This allows us to - -- write chained expressions in `do` blocks without stair-stepping: - -- example = do - -- foo - -- if ... then - -- ... - -- else if ... then - -- ... - -- else - -- ... - TokLowerName [] "if" -> - state & insertKwProperty (pushStack tokPos LytIf) - - TokLowerName [] "then" -> - case state & collapse indentedP of - ((_, LytIf) : stk', acc') -> - (stk', acc') & insertToken src & pushStack tokPos LytThen - _ -> - state & insertDefault & popStack (== LytProperty) - - TokLowerName [] "else" -> - case state & collapse indentedP of - ((_, LytThen) : stk', acc') -> - (stk', acc') & insertToken src - _ -> - -- We don't want to insert a layout separator for top-level `else` in - -- instance chains. - case state & collapse offsideP of - state'@(stk', _) | isTopDecl tokPos stk' -> - state' & insertToken src - state' -> - state' & insertSep & insertToken src & popStack (== LytProperty) - - -- `forall` binders need masking because the usage of `.` should not - -- introduce a LytProperty context. - TokForall _ -> - state & insertKwProperty (pushStack tokPos LytForall) - - -- Lambdas need masking because the usage of `->` should not close a - -- LytDeclGuard or LytCaseGuard context. - TokBackslash -> - state & insertDefault & pushStack tokPos LytLambdaBinders - - TokRightArrow _ -> - state & collapse arrowP & popStack guardP & insertToken src - where - arrowP _ LytDo = True - arrowP _ LytOf = False - arrowP lytPos lyt = offsideEndP lytPos lyt - - guardP LytCaseBinders = True - guardP LytCaseGuard = True - guardP LytLambdaBinders = True - guardP _ = False - - TokEquals -> - case state & collapse equalsP of - ((_, LytDeclGuard) : stk', acc') -> - (stk', acc') & insertToken src - _ -> - state & insertDefault - where - equalsP _ LytWhere = True - equalsP _ LytLet = True - equalsP _ LytLetStmt = True - equalsP _ _ = False - - -- Guards need masking because of commas. - TokPipe -> - case collapse offsideEndP state of - state'@((_, LytOf) : _, _) -> - state' & pushStack tokPos LytCaseGuard & insertToken src - state'@((_, LytLet) : _, _) -> - state' & pushStack tokPos LytDeclGuard & insertToken src - state'@((_, LytLetStmt) : _, _) -> - state' & pushStack tokPos LytDeclGuard & insertToken src - state'@((_, LytWhere) : _, _) -> - state' & pushStack tokPos LytDeclGuard & insertToken src - _ -> - state & insertDefault - - -- Ticks can either start or end an infix expression. We preemptively - -- collapse all indentation contexts in search of a starting delimiter, - -- and backtrack if we don't find one. - TokTick -> - case state & collapse indentedP of - ((_, LytTick) : stk', acc') -> - (stk', acc') & insertToken src - _ -> - state & collapse offsideEndP & insertSep & insertToken src & pushStack tokPos LytTick - - -- In general, commas should close all indented contexts. - -- example = [ do foo - -- bar, baz ] - TokComma -> - case state & collapse indentedP of - -- If we see a LytBrace, then we are in a record type or literal. - -- Record labels need masking so we can use unquoted keywords as labels - -- without accidentally littering layout delimiters. - state'@((_, LytBrace) : _, _) -> - state' & insertToken src & pushStack tokPos LytProperty - state' -> - state' & insertToken src - - -- TokDot tokens usually entail property access, which need masking so we - -- can use unquoted keywords as labels. - TokDot -> - case state & insertDefault of - ((_, LytForall) : stk', acc') -> - (stk', acc') - state' -> - state' & pushStack tokPos LytProperty - - TokLeftParen -> - state & insertDefault & pushStack tokPos LytParen - - TokLeftBrace -> - state & insertDefault & pushStack tokPos LytBrace & pushStack tokPos LytProperty - - TokLeftSquare -> - state & insertDefault & pushStack tokPos LytSquare - - TokRightParen -> - state & collapse indentedP & popStack (== LytParen) & insertToken src - - TokRightBrace -> - state & collapse indentedP & popStack (== LytProperty) & popStack (== LytBrace) & insertToken src - - TokRightSquare -> - state & collapse indentedP & popStack (== LytSquare) & insertToken src - - TokString _ _ -> - state & insertDefault & popStack (== LytProperty) - - TokLowerName [] _ -> - state & insertDefault & popStack (== LytProperty) - - TokOperator _ _ -> - state & collapse offsideEndP & insertSep & insertToken src - - _ -> - state & insertDefault - - insertDefault state = - state & collapse offsideP & insertSep & insertToken src - - insertStart lyt state@(stk, _) = - -- We only insert a new layout start when it's going to increase indentation. - -- This prevents things like the following from parsing: - -- instance foo :: Foo where - -- foo = 42 - case find (isIndented . snd) stk of - Just (pos, _) | srcColumn nextPos <= srcColumn pos -> state - _ -> state & pushStack nextPos lyt & insertToken (lytToken nextPos TokLayoutStart) - - insertSep state@(stk, acc) = case stk of - -- LytTopDecl is closed by a separator. - (lytPos, LytTopDecl) : stk' | sepP lytPos -> - (stk', acc) & insertToken sepTok - -- LytTopDeclHead can be closed by a separator if there is no `where`. - (lytPos, LytTopDeclHead) : stk' | sepP lytPos -> - (stk', acc) & insertToken sepTok - (lytPos, lyt) : _ | indentSepP lytPos lyt -> - case lyt of - -- If a separator is inserted in a case block, we need to push an - -- additional LytCaseBinders context for comma masking. - LytOf -> state & insertToken sepTok & pushStack tokPos LytCaseBinders - _ -> state & insertToken sepTok - _ -> state - where - sepTok = lytToken tokPos TokLayoutSep - - insertKwProperty k state = - case state & insertDefault of - ((_, LytProperty) : stk', acc') -> - (stk', acc') - state' -> - k state' - - insertEnd = - insertToken (lytToken tokPos TokLayoutEnd) - - insertToken token (stk, acc) = - (stk, acc `snoc` token) - - pushStack lytPos lyt (stk, acc) = - ((lytPos, lyt) : stk, acc) - - popStack p ((_, lyt) : stk', acc) - | p lyt = (stk', acc) - popStack _ state = state - - collapse p = uncurry go - where - go ((lytPos, lyt) : stk) acc - | p lytPos lyt = - go stk $ if isIndented lyt - then acc `snoc` lytToken tokPos TokLayoutEnd - else acc - go stk acc = (stk, acc) - - indentedP = - const isIndented - - offsideP lytPos lyt = - isIndented lyt && srcColumn tokPos < srcColumn lytPos - - offsideEndP lytPos lyt = - isIndented lyt && srcColumn tokPos <= srcColumn lytPos - - indentSepP lytPos lyt = - isIndented lyt && sepP lytPos - - sepP lytPos = - srcColumn tokPos == srcColumn lytPos && srcLine tokPos /= srcLine lytPos - -unwindLayout :: SourcePos -> [Comment LineFeed] -> LayoutStack -> [SourceToken] -unwindLayout pos leading = go - where - go [] = [] - go ((_, LytRoot) : _) = [SourceToken (TokenAnn (SourceRange pos pos) leading []) TokEof] - go ((_, lyt) : stk) | isIndented lyt = lytToken pos TokLayoutEnd : go stk - go (_ : stk) = go stk diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Lexer.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Lexer.hs deleted file mode 100644 index 726a76f2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Lexer.hs +++ /dev/null @@ -1,780 +0,0 @@ -module Language.PureScript.CST.Lexer - ( lenient - , lexModule - , lex - , lexTopLevel - , lexWithState - , isUnquotedKey - ) where - -import Prelude hiding (lex, exp, exponent, lines) - -import Control.Monad (join) -import Data.Char qualified as Char -import Data.DList qualified as DList -import Data.Foldable (foldl') -import Data.Functor (($>)) -import Data.Scientific qualified as Sci -import Data.String (fromString) -import Data.Text (Text) -import Data.Text qualified as Text -import Data.Text.PureScript qualified as Text -import Language.PureScript.CST.Errors (ParserErrorInfo(..), ParserErrorType(..)) -import Language.PureScript.CST.Monad (LexResult, LexState(..), ParserM(..), throw) -import Language.PureScript.CST.Layout (LayoutDelim(..), insertLayout, lytToken, unwindLayout) -import Language.PureScript.CST.Positions (advanceLeading, advanceToken, advanceTrailing, applyDelta, textDelta) -import Language.PureScript.CST.Types (Comment(..), LineFeed(..), SourcePos(..), SourceRange(..), SourceStyle(..), SourceToken(..), Token(..), TokenAnn(..)) - --- | Stops at the first lexing error and replaces it with TokEof. Otherwise, --- the parser will fail when it attempts to draw a lookahead token. -lenient :: [LexResult] -> [LexResult] -lenient = go - where - go [] = [] - go (Right a : as) = Right a : go as - go (Left (st, _) : _) = do - let - pos = lexPos st - ann = TokenAnn (SourceRange pos pos) (lexLeading st) [] - [Right (SourceToken ann TokEof)] - -lexModule :: Text -> [LexResult] -lexModule = lex' shebangThenComments - --- | Lexes according to root layout rules. -lex :: Text -> [LexResult] -lex = lex' comments - -lex' :: (Text -> ([Comment LineFeed], Text)) -> Text -> [LexResult] -lex' lexComments src = do - let (leading, src') = lexComments src - - lexWithState $ LexState - { lexPos = advanceLeading (SourcePos 1 1) leading - , lexLeading = leading - , lexSource = src' - , lexStack = [(SourcePos 0 0, LytRoot)] - } - --- | Lexes according to top-level declaration context rules. -lexTopLevel :: Text -> [LexResult] -lexTopLevel src = do - let - (leading, src') = comments src - lexPos = advanceLeading (SourcePos 1 1) leading - hd = Right $ lytToken lexPos TokLayoutStart - tl = lexWithState $ LexState - { lexPos = lexPos - , lexLeading = leading - , lexSource = src' - , lexStack = [(lexPos, LytWhere), (SourcePos 0 0, LytRoot)] - } - hd : tl - --- | Lexes according to some LexState. -lexWithState :: LexState -> [LexResult] -lexWithState = go - where - Parser lexK = - tokenAndComments - - go state@LexState {..} = - lexK lexSource onError onSuccess - where - onError lexSource' err = do - let - len1 = Text.length lexSource - len2 = Text.length lexSource' - chunk = Text.take (max 0 (len1 - len2)) lexSource - chunkDelta = textDelta chunk - pos = applyDelta lexPos chunkDelta - pure $ Left - ( state { lexSource = lexSource' } - , ParserErrorInfo (SourceRange pos $ applyDelta pos (0, 1)) [] lexStack err - ) - - onSuccess _ (TokEof, _) = - Right <$> unwindLayout lexPos lexLeading lexStack - onSuccess lexSource' (tok, (trailing, lexLeading')) = do - let - endPos = advanceToken lexPos tok - lexPos' = advanceLeading (advanceTrailing endPos trailing) lexLeading' - tokenAnn = TokenAnn - { tokRange = SourceRange lexPos endPos - , tokLeadingComments = lexLeading - , tokTrailingComments = trailing - } - (lexStack', toks) = - insertLayout (SourceToken tokenAnn tok) lexPos' lexStack - state' = LexState - { lexPos = lexPos' - , lexLeading = lexLeading' - , lexSource = lexSource' - , lexStack = lexStack' - } - go2 state' toks - - go2 state [] = go state - go2 state (t : ts) = Right t : go2 state ts - -type Lexer = ParserM ParserErrorType Text - -{-# INLINE next #-} -next :: Lexer () -next = Parser $ \inp _ ksucc -> - ksucc (Text.drop 1 inp) () - -{-# INLINE nextWhile #-} -nextWhile :: (Char -> Bool) -> Lexer Text -nextWhile p = Parser $ \inp _ ksucc -> do - let (chs, inp') = Text.span p inp - ksucc inp' chs - -{-# INLINE nextWhile' #-} -nextWhile' :: Int -> (Char -> Bool) -> Lexer Text -nextWhile' n p = Parser $ \inp _ ksucc -> do - let (chs, inp') = Text.spanUpTo n p inp - ksucc inp' chs - -{-# INLINE peek #-} -peek :: Lexer (Maybe Char) -peek = Parser $ \inp _ ksucc -> - if Text.null inp - then ksucc inp Nothing - else ksucc inp $ Just $ Text.head inp - -{-# INLINE restore #-} -restore :: (ParserErrorType -> Bool) -> Lexer a -> Lexer a -restore p (Parser k) = Parser $ \inp kerr ksucc -> - k inp (\inp' err -> kerr (if p err then inp else inp') err) ksucc - -tokenAndComments :: Lexer (Token, ([Comment void], [Comment LineFeed])) -tokenAndComments = (,) <$> token <*> breakComments - -shebangThenComments :: Text -> ([Comment LineFeed], Text) -shebangThenComments src = do - let - (sb, (coms, src')) = comments <$> shebang src - (sb <> coms, src') - -shebang :: Text -> ([Comment LineFeed], Text) -shebang = \src -> k src (\_ _ -> ([], src)) (\inp a -> (a, inp)) - where - Parser k = breakShebang - -comments :: Text -> ([Comment LineFeed], Text) -comments = \src -> k src (\_ _ -> ([], src)) (\inp (a, b) -> (a <> b, inp)) - where - Parser k = breakComments - -breakComments :: Lexer ([Comment void], [Comment LineFeed]) -breakComments = k0 [] - where - k0 acc = do - spaces <- nextWhile (== ' ') - lines <- nextWhile isLineFeed - let - acc' - | Text.null spaces = acc - | otherwise = Space (Text.length spaces) : acc - if Text.null lines - then do - mbComm <- comment - case mbComm of - Just comm -> k0 (comm : acc') - Nothing -> pure (reverse acc', []) - else - k1 acc' (goWs [] $ Text.unpack lines) - - k1 trl acc = do - ws <- nextWhile (\c -> c == ' ' || isLineFeed c) - let acc' = goWs acc $ Text.unpack ws - mbComm <- comment - case mbComm of - Just comm -> k1 trl (comm : acc') - Nothing -> pure (reverse trl, reverse acc') - - goWs a ('\r' : '\n' : ls) = goWs (Line CRLF : a) ls - goWs a ('\r' : ls) = goWs (Line CRLF : a) ls - goWs a ('\n' : ls) = goWs (Line LF : a) ls - goWs a (' ' : ls) = goSpace a 1 ls - goWs a _ = a - - goSpace a !n (' ' : ls) = goSpace a (n + 1) ls - goSpace a n ls = goWs (Space n : a) ls - - isBlockComment = Parser $ \inp _ ksucc -> - case Text.uncons inp of - Just ('-', inp2) -> - case Text.uncons inp2 of - Just ('-', inp3) -> - ksucc inp3 $ Just False - _ -> - ksucc inp Nothing - Just ('{', inp2) -> - case Text.uncons inp2 of - Just ('-', inp3) -> - ksucc inp3 $ Just True - _ -> - ksucc inp Nothing - _ -> - ksucc inp Nothing - - comment = isBlockComment >>= \case - Just True -> Just <$> blockComment "{-" - Just False -> Just <$> lineComment "--" - Nothing -> pure Nothing - - blockComment acc = do - chs <- nextWhile (/= '-') - dashes <- nextWhile (== '-') - if Text.null dashes - then pure $ Comment $ acc <> chs - else peek >>= \case - Just '}' -> next $> Comment (acc <> chs <> dashes <> "}") - _ -> blockComment (acc <> chs <> dashes) - -breakShebang :: ParserM ParserErrorType Text [Comment LineFeed] -breakShebang = shebangComment >>= \case - Just comm -> k0 [comm] - Nothing -> pure [] - where - k0 acc = lineFeedShebang >>= \case - Just (lf, sb) -> do - comm <- lineComment sb - k0 (comm : lf : acc) - Nothing -> - pure $ reverse acc - - lineFeedShebang = Parser $ \inp _ ksucc -> - case unconsLineFeed inp of - Just (lf, inp2) - | Just (sb, inp3) <- unconsShebang inp2 -> - ksucc inp3 $ Just (lf, sb) - _ -> - ksucc inp Nothing - - unconsLineFeed :: Text -> Maybe (Comment LineFeed, Text) - unconsLineFeed inp = - case Text.uncons inp of - Just ('\r', inp2) -> - case Text.uncons inp2 of - Just ('\n', inp3) -> - Just (Line CRLF, inp3) - _ -> - Just (Line CRLF, inp2) - Just ('\n', inp2) -> - Just (Line LF, inp2) - _ -> - Nothing - - unconsShebang :: Text -> Maybe (Text, Text) - unconsShebang = fmap ("#!",) . Text.stripPrefix "#!" - - shebangComment = isShebang >>= traverse lineComment - - isShebang = Parser $ \inp _ ksucc -> - case unconsShebang inp of - Just (sb, inp3) -> - ksucc inp3 $ Just sb - _ -> - ksucc inp Nothing - -lineComment :: forall lf. Text -> ParserM ParserErrorType Text (Comment lf) -lineComment acc = do - comm <- nextWhile (\c -> c /= '\r' && c /= '\n') - pure $ Comment (acc <> comm) - -token :: Lexer Token -token = peek >>= maybe (pure TokEof) k0 - where - k0 ch1 = case ch1 of - '(' -> next *> leftParen - ')' -> next $> TokRightParen - '{' -> next $> TokLeftBrace - '}' -> next $> TokRightBrace - '[' -> next $> TokLeftSquare - ']' -> next $> TokRightSquare - '`' -> next $> TokTick - ',' -> next $> TokComma - '∷' -> next *> orOperator1 (TokDoubleColon Unicode) ch1 - '←' -> next *> orOperator1 (TokLeftArrow Unicode) ch1 - '→' -> next *> orOperator1 (TokRightArrow Unicode) ch1 - '⇒' -> next *> orOperator1 (TokRightFatArrow Unicode) ch1 - '∀' -> next *> orOperator1 (TokForall Unicode) ch1 - '|' -> next *> orOperator1 TokPipe ch1 - '.' -> next *> orOperator1 TokDot ch1 - '\\' -> next *> orOperator1 TokBackslash ch1 - '<' -> next *> orOperator2 (TokLeftArrow ASCII) ch1 '-' - '-' -> next *> orOperator2 (TokRightArrow ASCII) ch1 '>' - '=' -> next *> orOperator2' TokEquals (TokRightFatArrow ASCII) ch1 '>' - ':' -> next *> orOperator2' (TokOperator [] ":") (TokDoubleColon ASCII) ch1 ':' - '?' -> next *> hole - '\'' -> next *> char - '"' -> next *> string - _ | Char.isDigit ch1 -> restore (== ErrNumberOutOfRange) (next *> number ch1) - | Char.isUpper ch1 -> next *> upper [] ch1 - | isIdentStart ch1 -> next *> lower [] ch1 - | isSymbolChar ch1 -> next *> operator [] [ch1] - | otherwise -> throw $ ErrLexeme (Just [ch1]) [] - - {-# INLINE orOperator1 #-} - orOperator1 :: Token -> Char -> Lexer Token - orOperator1 tok ch1 = join $ Parser $ \inp _ ksucc -> - case Text.uncons inp of - Just (ch2, inp2) | isSymbolChar ch2 -> - ksucc inp2 $ operator [] [ch1, ch2] - _ -> - ksucc inp $ pure tok - - {-# INLINE orOperator2 #-} - orOperator2 :: Token -> Char -> Char -> Lexer Token - orOperator2 tok ch1 ch2 = join $ Parser $ \inp _ ksucc -> - case Text.uncons inp of - Just (ch2', inp2) | ch2 == ch2' -> - case Text.uncons inp2 of - Just (ch3, inp3) | isSymbolChar ch3 -> - ksucc inp3 $ operator [] [ch1, ch2, ch3] - _ -> - ksucc inp2 $ pure tok - _ -> - ksucc inp $ operator [] [ch1] - - {-# INLINE orOperator2' #-} - orOperator2' :: Token -> Token -> Char -> Char -> Lexer Token - orOperator2' tok1 tok2 ch1 ch2 = join $ Parser $ \inp _ ksucc -> - case Text.uncons inp of - Just (ch2', inp2) | ch2 == ch2' -> - case Text.uncons inp2 of - Just (ch3, inp3) | isSymbolChar ch3 -> - ksucc inp3 $ operator [] [ch1, ch2, ch3] - _ -> - ksucc inp2 $ pure tok2 - Just (ch2', inp2) | isSymbolChar ch2' -> - ksucc inp2 $ operator [] [ch1, ch2'] - _ -> - ksucc inp $ pure tok1 - - {- - leftParen - : '(' '→' ')' - | '(' '->' ')' - | '(' symbolChar+ ')' - | '(' - -} - leftParen :: Lexer Token - leftParen = Parser $ \inp kerr ksucc -> - case Text.span isSymbolChar inp of - (chs, inp2) - | Text.null chs -> ksucc inp TokLeftParen - | otherwise -> - case Text.uncons inp2 of - Just (')', inp3) -> - case chs of - "→" -> ksucc inp3 $ TokSymbolArr Unicode - "->" -> ksucc inp3 $ TokSymbolArr ASCII - _ | isReservedSymbol chs -> kerr inp ErrReservedSymbol - | otherwise -> ksucc inp3 $ TokSymbolName [] chs - _ -> ksucc inp TokLeftParen - - {- - symbol - : '(' symbolChar+ ')' - -} - symbol :: [Text] -> Lexer Token - symbol qual = restore isReservedSymbolError $ peek >>= \case - Just ch | isSymbolChar ch -> - nextWhile isSymbolChar >>= \chs -> - peek >>= \case - Just ')' - | isReservedSymbol chs -> throw ErrReservedSymbol - | otherwise -> next $> TokSymbolName (reverse qual) chs - Just ch2 -> throw $ ErrLexeme (Just [ch2]) [] - Nothing -> throw ErrEof - Just ch -> throw $ ErrLexeme (Just [ch]) [] - Nothing -> throw ErrEof - - {- - operator - : symbolChar+ - -} - operator :: [Text] -> String -> Lexer Token - operator qual pre = do - rest <- nextWhile isSymbolChar - pure . TokOperator (reverse qual) $ Text.pack pre <> rest - - {- - moduleName - : upperChar alphaNumChar* - - qualifier - : (moduleName '.')* moduleName - - upper - : (qualifier '.')? upperChar identChar* - | qualifier '.' lowerQualified - | qualifier '.' operator - | qualifier '.' symbol - -} - upper :: [Text] -> Char -> Lexer Token - upper qual pre = do - rest <- nextWhile isIdentChar - ch1 <- peek - let name = Text.cons pre rest - case ch1 of - Just '.' -> do - let qual' = name : qual - next *> peek >>= \case - Just '(' -> next *> symbol qual' - Just ch2 - | Char.isUpper ch2 -> next *> upper qual' ch2 - | isIdentStart ch2 -> next *> lower qual' ch2 - | isSymbolChar ch2 -> next *> operator qual' [ch2] - | otherwise -> throw $ ErrLexeme (Just [ch2]) [] - Nothing -> - throw ErrEof - _ -> - pure $ TokUpperName (reverse qual) name - - {- - lower - : '_' - | 'forall' - | lowerChar identChar* - - lowerQualified - : lowerChar identChar* - -} - lower :: [Text] -> Char -> Lexer Token - lower qual pre = do - rest <- nextWhile isIdentChar - case pre of - '_' | Text.null rest -> - if null qual - then pure TokUnderscore - else throw $ ErrLexeme (Just [pre]) [] - _ -> - case Text.cons pre rest of - "forall" | null qual -> pure $ TokForall ASCII - name -> pure $ TokLowerName (reverse qual) name - - {- - hole - : '?' identChar+ - -} - hole :: Lexer Token - hole = do - name <- nextWhile isIdentChar - if Text.null name - then operator [] ['?'] - else pure $ TokHole name - - {- - char - : "'" '\' escape "'" - | "'" [^'] "'" - -} - char :: Lexer Token - char = do - (raw, ch) <- peek >>= \case - Just '\\' -> do - (raw, ch2) <- next *> escape - pure (Text.cons '\\' raw, ch2) - Just ch -> - next $> (Text.singleton ch, ch) - Nothing -> - throw ErrEof - peek >>= \case - Just '\'' - | fromEnum ch > 0xFFFF -> throw ErrAstralCodePointInChar - | otherwise -> next $> TokChar raw ch - Just ch2 -> - throw $ ErrLexeme (Just [ch2]) [] - _ -> - throw ErrEof - - {- - stringPart - : '\' escape - | '\' [ \r\n]+ '\' - | [^"] - - string - : '"' stringPart* '"' - | '"""' '"'{0,2} ([^"]+ '"'{1,2})* [^"]* '"""' - - A raw string literal can't contain any sequence of 3 or more quotes, - although sequences of 1 or 2 quotes are allowed anywhere, including at the - beginning or the end. - -} - string :: Lexer Token - string = do - quotes1 <- nextWhile' 7 (== '"') - case Text.length quotes1 of - 0 -> do - let - go raw acc = do - chs <- nextWhile isNormalStringChar - let - raw' = raw <> chs - acc' = acc <> DList.fromList (Text.unpack chs) - peek >>= \case - Just '"' -> next $> TokString raw' (fromString (DList.toList acc')) - Just '\\' -> next *> goEscape (raw' <> "\\") acc' - Just _ -> throw ErrLineFeedInString - Nothing -> throw ErrEof - - goEscape raw acc = do - mbCh <- peek - case mbCh of - Just ch1 | isStringGapChar ch1 -> do - gap <- nextWhile isStringGapChar - peek >>= \case - Just '"' -> next $> TokString (raw <> gap) (fromString (DList.toList acc)) - Just '\\' -> next *> go (raw <> gap <> "\\") acc - Just ch -> throw $ ErrCharInGap ch - Nothing -> throw ErrEof - _ -> do - (raw', ch) <- escape - go (raw <> raw') (acc <> DList.singleton ch) - go "" mempty - 1 -> - pure $ TokString "" "" - n | n >= 5 -> - pure $ TokRawString $ Text.drop 5 quotes1 - _ -> do - let - go acc = do - chs <- nextWhile (/= '"') - quotes2 <- nextWhile' 5 (== '"') - case Text.length quotes2 of - 0 -> throw ErrEof - n | n >= 3 -> pure $ TokRawString $ acc <> chs <> Text.drop 3 quotes2 - _ -> go (acc <> chs <> quotes2) - go $ Text.drop 2 quotes1 - - {- - escape - : 't' - | 'r' - | 'n' - | "'" - | '"' - | 'x' [0-9a-fA-F]{0,6} - -} - escape :: Lexer (Text, Char) - escape = do - ch <- peek - case ch of - Just 't' -> next $> ("t", '\t') - Just 'r' -> next $> ("r", '\r') - Just 'n' -> next $> ("n", '\n') - Just '"' -> next $> ("\"", '"') - Just '\'' -> next $> ("'", '\'') - Just '\\' -> next $> ("\\", '\\') - Just 'x' -> (*>) next $ Parser $ \inp kerr ksucc -> do - let - go n acc (ch' : chs) - | Char.isHexDigit ch' = go (n * 16 + Char.digitToInt ch') (ch' : acc) chs - go n acc _ - | n <= 0x10FFFF = - ksucc (Text.drop (length acc) inp) - ("x" <> Text.pack (reverse acc), Char.chr n) - | otherwise = - kerr inp ErrCharEscape -- TODO - go 0 [] $ Text.unpack $ Text.take 6 inp - _ -> throw ErrCharEscape - - {- - number - : hexadecimal - | integer ('.' fraction)? exponent? - -} - number :: Char -> Lexer Token - number ch1 = peek >>= \ch2 -> case (ch1, ch2) of - ('0', Just 'x') -> next *> hexadecimal - (_, _) -> do - mbInt <- integer1 ch1 - mbFraction <- fraction - case (mbInt, mbFraction) of - (Just (raw, int), Nothing) -> do - let int' = digitsToInteger int - exponent >>= \case - Just (raw', exp) -> - sciDouble (raw <> raw') $ Sci.scientific int' exp - Nothing -> - pure $ TokInt raw int' - (Just (raw, int), Just (raw', frac)) -> do - let sci = digitsToScientific int frac - exponent >>= \case - Just (raw'', exp) -> - sciDouble (raw <> raw' <> raw'') $ uncurry Sci.scientific $ (+ exp) <$> sci - Nothing -> - sciDouble (raw <> raw') $ uncurry Sci.scientific sci - (Nothing, Just (raw, frac)) -> do - let sci = digitsToScientific [] frac - exponent >>= \case - Just (raw', exp) -> - sciDouble (raw <> raw') $ uncurry Sci.scientific $ (+ exp) <$> sci - Nothing -> - sciDouble raw $ uncurry Sci.scientific sci - (Nothing, Nothing) -> - peek >>= \ch -> throw $ ErrLexeme (pure <$> ch) [] - - sciDouble :: Text -> Sci.Scientific -> Lexer Token - sciDouble raw sci = case Sci.toBoundedRealFloat sci of - Left _ -> throw ErrNumberOutOfRange - Right n -> pure $ TokNumber raw n - - {- - integer - : '0' - | [1-9] digits - -} - integer :: Lexer (Maybe (Text, String)) - integer = peek >>= \case - Just '0' -> next *> peek >>= \case - Just ch | isNumberChar ch -> throw ErrLeadingZero - _ -> pure $ Just ("0", "0") - Just ch | Char.isDigit ch -> Just <$> digits - _ -> pure Nothing - - {- - integer1 - : '0' - | [1-9] digits - - This is the same as 'integer', the only difference is that this expects the - first char to be consumed during dispatch. - -} - integer1 :: Char -> Lexer (Maybe (Text, String)) - integer1 = \case - '0' -> peek >>= \case - Just ch | isNumberChar ch -> throw ErrLeadingZero - _ -> pure $ Just ("0", "0") - ch | Char.isDigit ch -> do - (raw, chs) <- digits - pure $ Just (Text.cons ch raw, ch : chs) - _ -> pure Nothing - - {- - fraction - : '.' [0-9_]+ - -} - fraction :: Lexer (Maybe (Text, String)) - fraction = Parser $ \inp _ ksucc -> - -- We need more than a single char lookahead for things like `1..10`. - case Text.uncons inp of - Just ('.', inp') - | (raw, inp'') <- Text.span isNumberChar inp' - , not (Text.null raw) -> - ksucc inp'' $ Just ("." <> raw, filter (/= '_') $ Text.unpack raw) - _ -> - ksucc inp Nothing - - {- - digits - : [0-9_]* - - Digits can contain underscores, which are ignored. - -} - digits :: Lexer (Text, String) - digits = do - raw <- nextWhile isNumberChar - pure (raw, filter (/= '_') $ Text.unpack raw) - - {- - exponent - : 'e' ('+' | '-')? integer - -} - exponent :: Lexer (Maybe (Text, Int)) - exponent = peek >>= \case - Just 'e' -> do - (neg, sign) <- next *> peek >>= \case - Just '-' -> next $> (True, "-") - Just '+' -> next $> (False, "+") - _ -> pure (False, "") - integer >>= \case - Just (raw, chs) -> do - let - int | neg = negate $ digitsToInteger chs - | otherwise = digitsToInteger chs - pure $ Just ("e" <> sign <> raw, fromInteger int) - Nothing -> throw ErrExpectedExponent - _ -> - pure Nothing - - {- - hexadecimal - : '0x' [0-9a-fA-F]+ - -} - hexadecimal :: Lexer Token - hexadecimal = do - chs <- nextWhile Char.isHexDigit - if Text.null chs - then throw ErrExpectedHex - else pure $ TokInt ("0x" <> chs) $ digitsToIntegerBase 16 $ Text.unpack chs - -digitsToInteger :: String -> Integer -digitsToInteger = digitsToIntegerBase 10 - -digitsToIntegerBase :: Integer -> String -> Integer -digitsToIntegerBase b = foldl' (\n c -> n * b + toInteger (Char.digitToInt c)) 0 - -digitsToScientific :: String -> String -> (Integer, Int) -digitsToScientific = go 0 . reverse - where - go !exp is [] = (digitsToInteger (reverse is), exp) - go exp is (f : fs) = go (exp - 1) (f : is) fs - -isSymbolChar :: Char -> Bool -isSymbolChar c = (c `elem` (":!#$%&*+./<=>?@\\^|-~" :: String)) || (not (Char.isAscii c) && Char.isSymbol c) - -isReservedSymbolError :: ParserErrorType -> Bool -isReservedSymbolError = (== ErrReservedSymbol) - -isReservedSymbol :: Text -> Bool -isReservedSymbol = flip elem symbols - where - symbols = - [ "::" - , "∷" - , "<-" - , "←" - , "->" - , "→" - , "=>" - , "⇒" - , "∀" - , "|" - , "." - , "\\" - , "=" - ] - -isIdentStart :: Char -> Bool -isIdentStart c = Char.isLower c || c == '_' - -isIdentChar :: Char -> Bool -isIdentChar c = Char.isAlphaNum c || c == '_' || c == '\'' - -isNumberChar :: Char -> Bool -isNumberChar c = Char.isDigit c || c == '_' - -isNormalStringChar :: Char -> Bool -isNormalStringChar c = c /= '"' && c /= '\\' && c /= '\r' && c /= '\n' - -isStringGapChar :: Char -> Bool -isStringGapChar c = c == ' ' || c == '\r' || c == '\n' - -isLineFeed :: Char -> Bool -isLineFeed c = c == '\r' || c == '\n' - --- | Checks if some identifier is a valid unquoted key. -isUnquotedKey :: Text -> Bool -isUnquotedKey t = - case Text.uncons t of - Nothing -> - False - Just (hd, tl) -> - isIdentStart hd && Text.all isIdentChar tl diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Monad.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Monad.hs deleted file mode 100644 index 2b79f1a9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Monad.hs +++ /dev/null @@ -1,187 +0,0 @@ -module Language.PureScript.CST.Monad where - -import Prelude - -import Data.List (sortOn) -import Data.List.NonEmpty qualified as NE -import Data.Ord (comparing) -import Data.Text (Text) -import Language.PureScript.CST.Errors (ParserError, ParserErrorInfo(..), ParserErrorType(..), ParserWarning, ParserWarningType) -import Language.PureScript.CST.Layout (LayoutStack) -import Language.PureScript.CST.Positions (widen) -import Language.PureScript.CST.Types (Comment, LineFeed, SourcePos(..), SourceRange(..), SourceToken(..), Token, TokenAnn(..)) - -type LexResult = Either (LexState, ParserError) SourceToken - -data LexState = LexState - { lexPos :: SourcePos - , lexLeading :: [Comment LineFeed] - , lexSource :: Text - , lexStack :: LayoutStack - } deriving (Show) - -data ParserState = ParserState - { parserBuff :: [LexResult] - , parserErrors :: [ParserError] - , parserWarnings :: [ParserWarning] - } deriving (Show) - --- | A bare bones, CPS'ed `StateT s (Except e) a`. -newtype ParserM e s a = - Parser (forall r. s -> (s -> e -> r) -> (s -> a -> r) -> r) - -type Parser = ParserM ParserError ParserState - -instance Functor (ParserM e s) where - {-# INLINE fmap #-} - fmap f (Parser k) = - Parser $ \st kerr ksucc -> - k st kerr (\st' a -> ksucc st' (f a)) - -instance Applicative (ParserM e s) where - {-# INLINE pure #-} - pure a = Parser $ \st _ k -> k st a - {-# INLINE (<*>) #-} - Parser k1 <*> Parser k2 = - Parser $ \st kerr ksucc -> - k1 st kerr $ \st' f -> - k2 st' kerr $ \st'' a -> - ksucc st'' (f a) - -instance Monad (ParserM e s) where - {-# INLINE return #-} - return = pure - {-# INLINE (>>=) #-} - Parser k1 >>= k2 = - Parser $ \st kerr ksucc -> - k1 st kerr $ \st' a -> do - let Parser k3 = k2 a - k3 st' kerr ksucc - -runParser :: ParserState -> Parser a -> (ParserState, Either (NE.NonEmpty ParserError) a) -runParser st (Parser k) = k st left right - where - left st'@ParserState {..} err = - (st', Left $ NE.sortBy (comparing errRange) $ err NE.:| parserErrors) - - right st'@ParserState {..} res - | null parserErrors = (st', Right res) - | otherwise = (st', Left $ NE.fromList $ sortOn errRange parserErrors) - -runTokenParser :: Parser a -> [LexResult] -> Either (NE.NonEmpty ParserError) ([ParserWarning], a) -runTokenParser p buff = fmap (warnings,) res - where - (ParserState _ _ warnings, res) = - runParser initialState p - - initialState = ParserState - { parserBuff = buff - , parserErrors = [] - , parserWarnings = [] - } - -{-# INLINE throw #-} -throw :: e -> ParserM e s a -throw e = Parser $ \st kerr _ -> kerr st e - -parseError :: SourceToken -> Parser a -parseError tok = Parser $ \st kerr _ -> - kerr st $ ParserErrorInfo - { errRange = tokRange . tokAnn $ tok - , errToks = [tok] - , errStack = [] -- TODO parserStack st - , errType = ErrToken - } - -mkParserError :: LayoutStack -> [SourceToken] -> a -> ParserErrorInfo a -mkParserError stack toks ty = - ParserErrorInfo - { errRange = range - , errToks = toks - , errStack = stack - , errType = ty - } - where - range = case NE.nonEmpty toks of - Nothing -> SourceRange (SourcePos 0 0) (SourcePos 0 0) - Just neToks -> widen - (tokRange . tokAnn $ NE.head neToks) - (tokRange . tokAnn $ NE.last neToks) - -addFailure :: [SourceToken] -> ParserErrorType -> Parser () -addFailure toks ty = Parser $ \st _ ksucc -> - ksucc (st { parserErrors = mkParserError [] toks ty : parserErrors st }) () - -parseFail' :: [SourceToken] -> ParserErrorType -> Parser a -parseFail' toks msg = Parser $ \st kerr _ -> kerr st (mkParserError [] toks msg) - -parseFail :: SourceToken -> ParserErrorType -> Parser a -parseFail = parseFail' . pure - -addWarning :: [SourceToken] -> ParserWarningType -> Parser () -addWarning toks ty = Parser $ \st _ ksucc -> - ksucc (st { parserWarnings = mkParserError [] toks ty : parserWarnings st }) () - -pushBack :: SourceToken -> Parser () -pushBack tok = Parser $ \st _ ksucc -> - ksucc (st { parserBuff = Right tok : parserBuff st }) () - -{-# INLINE tryPrefix #-} -tryPrefix :: Parser a -> Parser b -> Parser (Maybe a, b) -tryPrefix (Parser lhs) rhs = Parser $ \st kerr ksucc -> - lhs st - (\_ _ -> do - let Parser k = (Nothing,) <$> rhs - k st kerr ksucc) - (\st' res -> do - let Parser k = (Just res,) <$> rhs - k st' kerr ksucc) - -oneOf :: NE.NonEmpty (Parser a) -> Parser a -oneOf parsers = Parser $ \st kerr ksucc -> do - let - prevErrs = parserErrors st - go (st', Right a) _ = (st', Right a) - go _ (st', Right a) = (st', Right a) - go (st1, Left errs1) (st2, Left errs2) - | errRange (NE.last errs2) > errRange (NE.last errs1) = (st2, Left errs2) - | otherwise = (st1, Left errs1) - case foldr1 go $ runParser (st { parserErrors = [] }) <$> parsers of - (st', Left errs) -> kerr (st' { parserErrors = prevErrs <> NE.tail errs}) $ NE.head errs - (st', Right res) -> ksucc (st' { parserErrors = prevErrs }) res - -manyDelimited :: Token -> Token -> Token -> Parser a -> Parser [a] -manyDelimited open close sep p = do - _ <- token open - res <- go1 - _ <- token close - pure res - where - go1 = - oneOf $ NE.fromList - [ go2 . pure =<< p - , pure [] - ] - - go2 acc = - oneOf $ NE.fromList - [ token sep *> (go2 . (: acc) =<< p) - , pure (reverse acc) - ] - -token :: Token -> Parser SourceToken -token t = do - t' <- munch - if t == tokValue t' - then pure t' - else parseError t' - -munch :: Parser SourceToken -munch = Parser $ \state@ParserState {..} kerr ksucc -> - case parserBuff of - Right tok : parserBuff' -> - ksucc (state { parserBuff = parserBuff' }) tok - Left (_, err) : _ -> - kerr state err - [] -> - error "Empty input" diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Parser.y b/claude-help/original-compiler/src/Language/PureScript/CST/Parser.y deleted file mode 100644 index 55aa95da..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Parser.y +++ /dev/null @@ -1,818 +0,0 @@ -{ -module Language.PureScript.CST.Parser - ( parseType - , parseExpr - , parseDecl - , parseIdent - , parseOperator - , parseModule - , parseImportDeclP - , parseDeclP - , parseExprP - , parseTypeP - , parseModuleNameP - , parseQualIdentP - , parse - , PartialResult(..) - ) where - -import Prelude hiding (lex) - -import Control.Monad ((<=<), when) -import Data.Bifunctor (second) -import Data.Foldable (foldl', for_, toList) -import qualified Data.List.NonEmpty as NE -import Data.Text (Text) -import Data.Traversable (for, sequence) -import Language.PureScript.CST.Errors -import Language.PureScript.CST.Flatten (flattenType) -import Language.PureScript.CST.Lexer -import Language.PureScript.CST.Monad -import Language.PureScript.CST.Positions -import Language.PureScript.CST.Types -import Language.PureScript.CST.Utils -import qualified Language.PureScript.Names as N -import qualified Language.PureScript.Roles as R -import Language.PureScript.PSString (PSString) -} - -%expect 0 - -%name parseType type -%name parseExpr expr -%name parseIdent ident -%name parseOperator op -%name parseModuleBody moduleBody -%name parseDecl decl -%partial parseImportDeclP importDeclP -%partial parseDeclP declP -%partial parseExprP exprP -%partial parseTypeP typeP -%partial parseModuleNameP moduleNameP -%partial parseQualIdentP qualIdentP -%partial parseModuleHeader moduleHeader -%partial parseDoStatement doStatement -%partial parseDoExpr doExpr -%partial parseDoNext doNext -%partial parseGuardExpr guardExpr -%partial parseGuardNext guardNext -%partial parseGuardStatement guardStatement -%partial parseClassSignature classSignature -%partial parseClassSuper classSuper -%partial parseClassNameAndFundeps classNameAndFundeps -%partial parseBinderAndArrow binderAndArrow -%tokentype { SourceToken } -%monad { Parser } -%error { parseError } -%lexer { lexer } { SourceToken _ TokEof } - -%token - '(' { SourceToken _ TokLeftParen } - ')' { SourceToken _ TokRightParen } - '{' { SourceToken _ TokLeftBrace } - '}' { SourceToken _ TokRightBrace } - '[' { SourceToken _ TokLeftSquare } - ']' { SourceToken _ TokRightSquare } - '\{' { SourceToken _ TokLayoutStart } - '\}' { SourceToken _ TokLayoutEnd } - '\;' { SourceToken _ TokLayoutSep } - '<-' { SourceToken _ (TokLeftArrow _) } - '->' { SourceToken _ (TokRightArrow _) } - '<=' { SourceToken _ (TokOperator [] sym) | isLeftFatArrow sym } - '=>' { SourceToken _ (TokRightFatArrow _) } - ':' { SourceToken _ (TokOperator [] ":") } - '::' { SourceToken _ (TokDoubleColon _) } - '=' { SourceToken _ TokEquals } - '|' { SourceToken _ TokPipe } - '`' { SourceToken _ TokTick } - '.' { SourceToken _ TokDot } - ',' { SourceToken _ TokComma } - '_' { SourceToken _ TokUnderscore } - '\\' { SourceToken _ TokBackslash } - '-' { SourceToken _ (TokOperator [] "-") } - '@' { SourceToken _ (TokOperator [] "@") } - 'ado' { SourceToken _ (TokLowerName _ "ado") } - 'as' { SourceToken _ (TokLowerName [] "as") } - 'case' { SourceToken _ (TokLowerName [] "case") } - 'class' { SourceToken _ (TokLowerName [] "class") } - 'data' { SourceToken _ (TokLowerName [] "data") } - 'derive' { SourceToken _ (TokLowerName [] "derive") } - 'do' { SourceToken _ (TokLowerName _ "do") } - 'else' { SourceToken _ (TokLowerName [] "else") } - 'false' { SourceToken _ (TokLowerName [] "false") } - 'forall' { SourceToken _ (TokForall ASCII) } - 'forallu' { SourceToken _ (TokForall Unicode) } - 'foreign' { SourceToken _ (TokLowerName [] "foreign") } - 'hiding' { SourceToken _ (TokLowerName [] "hiding") } - 'import' { SourceToken _ (TokLowerName [] "import") } - 'if' { SourceToken _ (TokLowerName [] "if") } - 'in' { SourceToken _ (TokLowerName [] "in") } - 'infix' { SourceToken _ (TokLowerName [] "infix") } - 'infixl' { SourceToken _ (TokLowerName [] "infixl") } - 'infixr' { SourceToken _ (TokLowerName [] "infixr") } - 'instance' { SourceToken _ (TokLowerName [] "instance") } - 'let' { SourceToken _ (TokLowerName [] "let") } - 'module' { SourceToken _ (TokLowerName [] "module") } - 'newtype' { SourceToken _ (TokLowerName [] "newtype") } - 'nominal' { SourceToken _ (TokLowerName [] "nominal") } - 'phantom' { SourceToken _ (TokLowerName [] "phantom") } - 'of' { SourceToken _ (TokLowerName [] "of") } - 'representational' { SourceToken _ (TokLowerName [] "representational") } - 'role' { SourceToken _ (TokLowerName [] "role") } - 'then' { SourceToken _ (TokLowerName [] "then") } - 'true' { SourceToken _ (TokLowerName [] "true") } - 'type' { SourceToken _ (TokLowerName [] "type") } - 'where' { SourceToken _ (TokLowerName [] "where") } - '(->)' { SourceToken _ (TokSymbolArr _) } - '(..)' { SourceToken _ (TokSymbolName [] "..") } - LOWER { SourceToken _ (TokLowerName [] _) } - QUAL_LOWER { SourceToken _ (TokLowerName _ _) } - UPPER { SourceToken _ (TokUpperName [] _) } - QUAL_UPPER { SourceToken _ (TokUpperName _ _) } - SYMBOL { SourceToken _ (TokSymbolName [] _) } - QUAL_SYMBOL { SourceToken _ (TokSymbolName _ _) } - OPERATOR { SourceToken _ (TokOperator [] _) } - QUAL_OPERATOR { SourceToken _ (TokOperator _ _) } - LIT_HOLE { SourceToken _ (TokHole _) } - LIT_CHAR { SourceToken _ (TokChar _ _) } - LIT_STRING { SourceToken _ (TokString _ _) } - LIT_RAW_STRING { SourceToken _ (TokRawString _) } - LIT_INT { SourceToken _ (TokInt _ _) } - LIT_NUMBER { SourceToken _ (TokNumber _ _) } - -%% - -many(a) :: { NE.NonEmpty a } - : many1(a) %shift { NE.reverse $1 } - -many1(a) :: { NE.NonEmpty a } - : a { pure $1 } - | many1(a) a { NE.cons $2 $1 } - -manySep(a, sep) :: { NE.NonEmpty a } - : manySep1(a, sep) { NE.reverse $1 } - -manySep1(a, sep) :: { NE.NonEmpty a } - : a { pure $1 } - | manySep1(a, sep) sep a { NE.cons $3 $1 } - -manySepOrEmpty(a, sep) :: { [a] } - : {- empty -} { [] } - | manySep(a, sep) { NE.toList $1 } - -manyOrEmpty(a) :: { [a] } - : {- empty -} { [] } - | many(a) { NE.toList $1 } - -sep(a, s) :: { Separated a } - : sep1(a, s) { separated $1 } - -sep1(a, s) :: { [(SourceToken, a)] } - : a %shift { [(placeholder, $1)] } - | sep1(a, s) s a { ($2, $3) : $1 } - -delim(a, b, c, d) :: { Delimited b } - : a d { Wrapped $1 Nothing $2 } - | a sep(b, c) d { Wrapped $1 (Just $2) $3 } - -moduleName :: { Name N.ModuleName } - : UPPER {% upperToModuleName $1 } - | QUAL_UPPER {% upperToModuleName $1 } - -qualProperName :: { QualifiedProperName } - : UPPER {% qualifiedProperName <\$> toQualifiedName N.ProperName $1 } - | QUAL_UPPER {% qualifiedProperName <\$> toQualifiedName N.ProperName $1 } - -properName :: { ProperName } - : UPPER {% properName <\$> toName N.ProperName $1 } - -qualIdent :: { QualifiedName Ident } - : LOWER {% toQualifiedName Ident $1 } - | QUAL_LOWER {% toQualifiedName Ident $1 } - | 'as' {% toQualifiedName Ident $1 } - | 'hiding' {% toQualifiedName Ident $1 } - | 'role' {% toQualifiedName Ident $1 } - | 'nominal' {% toQualifiedName Ident $1 } - | 'representational' {% toQualifiedName Ident $1 } - | 'phantom' {% toQualifiedName Ident $1 } - -ident :: { Name Ident } - : LOWER {% toName Ident $1 } - | 'as' {% toName Ident $1 } - | 'hiding' {% toName Ident $1 } - | 'role' {% toName Ident $1 } - | 'nominal' {% toName Ident $1 } - | 'representational' {% toName Ident $1 } - | 'phantom' {% toName Ident $1 } - -qualOp :: { QualifiedOpName } - : OPERATOR {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - | QUAL_OPERATOR {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - | '<=' {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - | '-' {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - | ':' {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - -op :: { OpName } - : OPERATOR {% opName <\$> toName N.OpName $1 } - | '<=' {% opName <\$> toName N.OpName $1 } - | '-' {% opName <\$> toName N.OpName $1 } - | ':' {% opName <\$> toName N.OpName $1 } - -qualSymbol :: { QualifiedOpName } - : SYMBOL {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - | QUAL_SYMBOL {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - | '(..)' {% qualifiedOpName <\$> toQualifiedName N.OpName $1 } - -symbol :: { OpName } - : SYMBOL {% opName <\$> toName N.OpName $1 } - | '(..)' {% opName <\$> toName N.OpName $1 } - -label :: { Label } - : LOWER { toLabel $1 } - | LIT_STRING { toLabel $1 } - | LIT_RAW_STRING { toLabel $1 } - | 'ado' { toLabel $1 } - | 'as' { toLabel $1 } - | 'case' { toLabel $1 } - | 'class' { toLabel $1 } - | 'data' { toLabel $1 } - | 'derive' { toLabel $1 } - | 'do' { toLabel $1 } - | 'else' { toLabel $1 } - | 'false' { toLabel $1 } - | 'forall' { toLabel $1 } - | 'foreign' { toLabel $1 } - | 'hiding' { toLabel $1 } - | 'import' { toLabel $1 } - | 'if' { toLabel $1 } - | 'in' { toLabel $1 } - | 'infix' { toLabel $1 } - | 'infixl' { toLabel $1 } - | 'infixr' { toLabel $1 } - | 'instance' { toLabel $1 } - | 'let' { toLabel $1 } - | 'module' { toLabel $1 } - | 'newtype' { toLabel $1 } - | 'nominal' { toLabel $1 } - | 'of' { toLabel $1 } - | 'phantom' { toLabel $1 } - | 'representational' { toLabel $1 } - | 'role' { toLabel $1 } - | 'then' { toLabel $1 } - | 'true' { toLabel $1 } - | 'type' { toLabel $1 } - | 'where' { toLabel $1 } - -hole :: { Name Ident } - : LIT_HOLE {% toName Ident $1 } - -string :: { (SourceToken, PSString) } - : LIT_STRING { toString $1 } - | LIT_RAW_STRING { toString $1 } - -char :: { (SourceToken, Char) } - : LIT_CHAR { toChar $1 } - -number :: { (SourceToken, Either Integer Double) } - : LIT_INT { toNumber $1 } - | LIT_NUMBER { toNumber $1 } - -int :: { (SourceToken, Integer) } - : LIT_INT { toInt $1 } - -boolean :: { (SourceToken, Bool) } - : 'true' { toBoolean $1 } - | 'false' { toBoolean $1 } - -type :: { Type () } - : type1 %shift { $1 } - | type1 '::' type { TypeKinded () $1 $2 $3 } - -type1 :: { Type () } - : type2 { $1 } - | forall many(typeVarBinding) '.' type1 { TypeForall () $1 $2 $3 $4 } - -type2 :: { Type () } - : type3 %shift { $1 } - | type3 '->' type1 { TypeArr () $1 $2 $3 } - | type3 '=>' type1 {% do cs <- toConstraint $1; pure $ TypeConstrained () cs $2 $3 } - -type3 :: { Type () } - : type4 %shift { $1 } - | type3 qualOp type4 %shift { TypeOp () $1 (getQualifiedOpName $2) $3 } - -type4 :: { Type () } - : type5 %shift { $1 } - | '-' int { uncurry (TypeInt () (Just $1)) (second negate $2) } - -type5 :: { Type () } - : typeAtom { $1 } - | type5 typeAtom { TypeApp () $1 $2 } - -typeAtom :: { Type ()} - : '_' { TypeWildcard () $1 } - | ident { TypeVar () $1 } - | qualProperName { TypeConstructor () (getQualifiedProperName $1) } - | qualSymbol { TypeOpName () (getQualifiedOpName $1) } - | string { uncurry (TypeString ()) $1 } - | int { uncurry (TypeInt () Nothing) $1 } - | hole { TypeHole () $1 } - | '(->)' { TypeArrName () $1 } - | '{' row '}' { TypeRecord () (Wrapped $1 $2 $3) } - | '(' row ')' { TypeRow () (Wrapped $1 $2 $3) } - | '(' type1 ')' { TypeParens () (Wrapped $1 $2 $3) } - | '(' typeKindedAtom '::' type ')' { TypeParens () (Wrapped $1 (TypeKinded () $2 $3 $4) $5) } - --- Due to a conflict between row syntax and kinded type syntax, we require --- kinded type variables to be wrapped in parens. Thus `(a :: Foo)` is always a --- row, and to annotate `a` with kind `Foo`, one must use `((a) :: Foo)`. -typeKindedAtom :: { Type () } - : '_' { TypeWildcard () $1 } - | qualProperName { TypeConstructor () (getQualifiedProperName $1) } - | qualSymbol { TypeOpName () (getQualifiedOpName $1) } - | int { uncurry (TypeInt () Nothing) $1 } - | hole { TypeHole () $1 } - | '{' row '}' { TypeRecord () (Wrapped $1 $2 $3) } - | '(' row ')' { TypeRow () (Wrapped $1 $2 $3) } - | '(' type1 ')' { TypeParens () (Wrapped $1 $2 $3) } - | '(' typeKindedAtom '::' type ')' { TypeParens () (Wrapped $1 (TypeKinded () $2 $3 $4) $5) } - -row :: { Row () } - : {- empty -} { Row Nothing Nothing } - | '|' type { Row Nothing (Just ($1, $2)) } - | sep(rowLabel, ',') { Row (Just $1) Nothing } - | sep(rowLabel, ',') '|' type { Row (Just $1) (Just ($2, $3)) } - -rowLabel :: { Labeled Label (Type ()) } - : label '::' type { Labeled $1 $2 $3 } - -typeVarBinding :: { TypeVarBinding () } - : ident { TypeVarName (Nothing, $1) } - | '@' ident { TypeVarName (Just $1, $2) } - | '(' ident '::' type ')' {% checkNoWildcards $4 *> pure (TypeVarKinded (Wrapped $1 (Labeled (Nothing, $2) $3 $4) $5)) } - | '(' '@' ident '::' type ')' {% checkNoWildcards $5 *> pure (TypeVarKinded (Wrapped $1 (Labeled (Just $2, $3) $4 $5) $6)) } - -typeVarBindingPlain :: { TypeVarBinding () } - : ident { TypeVarName (Nothing, $1) } - | '(' ident '::' type ')' {% checkNoWildcards $4 *> pure (TypeVarKinded (Wrapped $1 (Labeled (Nothing, $2) $3 $4) $5)) } - -forall :: { SourceToken } - : 'forall' { $1 } - | 'forallu' { $1 } - -exprWhere :: { Where () } - : expr %shift { Where $1 Nothing } - | expr 'where' '\{' manySep(letBinding, '\;') '\}' { Where $1 (Just ($2, $4)) } - -expr :: { Expr () } - : expr1 %shift { $1 } - | expr1 '::' type { ExprTyped () $1 $2 $3 } - -expr1 :: { Expr () } - : expr2 %shift { $1 } - | expr1 qualOp expr2 %shift { ExprOp () $1 (getQualifiedOpName $2) $3 } - -expr2 :: { Expr () } - : expr3 { $1 } - | expr2 '`' exprBacktick '`' expr3 { ExprInfix () $1 (Wrapped $2 $3 $4) $5 } - -exprBacktick :: { Expr () } - : expr3 { $1 } - | exprBacktick qualOp expr3 { ExprOp () $1 (getQualifiedOpName $2) $3 } - -expr3 :: { Expr () } - : expr4 %shift { $1 } - | '-' expr3 { ExprNegate () $1 $2 } - -expr4 :: { Expr () } - : expr5 { $1 } - | expr4 expr5 - { -- Record application/updates can introduce a function application - -- associated to the right, so we need to correct it. - case $2 of - ExprApp _ lhs rhs -> - ExprApp () (ExprApp () $1 lhs) rhs - _ -> ExprApp () $1 $2 - } - | expr4 '@' typeAtom { ExprVisibleTypeApp () $1 $2 $3 } - -expr5 :: { Expr () } - : expr6 { $1 } - | 'if' expr 'then' expr 'else' expr { ExprIf () (IfThenElse $1 $2 $3 $4 $5 $6) } - | doBlock { ExprDo () $1 } - | adoBlock 'in' expr { ExprAdo () $ uncurry AdoBlock $1 $2 $3 } - | '\\' many(binderAtom) '->' expr { ExprLambda () (Lambda $1 $2 $3 $4) } - | 'let' '\{' manySep(letBinding, '\;') '\}' 'in' expr { ExprLet () (LetIn $1 $3 $5 $6) } - | 'case' sep(expr, ',') 'of' '\{' manySep(caseBranch, '\;') '\}' { ExprCase () (CaseOf $1 $2 $3 $5) } - -- These special cases handle some idiosynchratic syntax that the current - -- parser allows. Technically the parser allows the rhs of a case branch to be - -- at any level, but this is ambiguous. We allow it in the case of a singleton - -- case, since this is used in the wild. - | 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '->' '\}' exprWhere - {% addWarning (let (a,b) = whereRange $8 in [a, b]) WarnDeprecatedCaseOfOffsideSyntax *> pure (ExprCase () (CaseOf $1 $2 $3 (pure ($5, Unconditional $6 $8)))) } - | 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '\}' guardedCase - {% addWarning (let (a,b) = guardedRange $7 in [a, b]) WarnDeprecatedCaseOfOffsideSyntax *> pure (ExprCase () (CaseOf $1 $2 $3 (pure ($5, $7)))) } - -expr6 :: { Expr () } - : expr7 %shift { $1 } - | expr7 '{' '}' { ExprApp () $1 (ExprRecord () (Wrapped $2 Nothing $3)) } - | expr7 '{' sep(recordUpdateOrLabel, ',') '}' - {% toRecordFields $3 >>= \case - Left xs -> pure $ ExprApp () $1 (ExprRecord () (Wrapped $2 (Just xs) $4)) - Right xs -> pure $ ExprRecordUpdate () $1 (Wrapped $2 xs $4) - } - -expr7 :: { Expr () } - : exprAtom { $1 } - | exprAtom '.' sep(label, '.') { ExprRecordAccessor () (RecordAccessor $1 $2 $3) } - -exprAtom :: { Expr () } - : '_' { ExprSection () $1 } - | hole { ExprHole () $1 } - | qualIdent { ExprIdent () $1 } - | qualProperName { ExprConstructor () (getQualifiedProperName $1) } - | qualSymbol { ExprOpName () (getQualifiedOpName $1) } - | boolean { uncurry (ExprBoolean ()) $1 } - | char { uncurry (ExprChar ()) $1 } - | string { uncurry (ExprString ()) $1 } - | number { uncurry (ExprNumber ()) $1 } - | delim('[', expr, ',', ']') { ExprArray () $1 } - | delim('{', recordLabel, ',', '}') { ExprRecord () $1 } - | '(' expr ')' { ExprParens () (Wrapped $1 $2 $3) } - -recordLabel :: { RecordLabeled (Expr ()) } - : label {% fmap RecordPun . toName Ident $ lblTok $1 } - | label '=' expr {% addFailure [$2] ErrRecordUpdateInCtr *> pure (RecordPun $ unexpectedName $ lblTok $1) } - | label ':' expr { RecordField $1 $2 $3 } - -recordUpdateOrLabel :: { Either (RecordLabeled (Expr ())) (RecordUpdate ()) } - : label ':' expr { Left (RecordField $1 $2 $3) } - | label {% fmap (Left . RecordPun) . toName Ident $ lblTok $1 } - | label '=' expr { Right (RecordUpdateLeaf $1 $2 $3) } - | label '{' sep(recordUpdate, ',') '}' { Right (RecordUpdateBranch $1 (Wrapped $2 $3 $4)) } - -recordUpdate :: { RecordUpdate () } - : label '=' expr { RecordUpdateLeaf $1 $2 $3 } - | label '{' sep(recordUpdate, ',') '}' { RecordUpdateBranch $1 (Wrapped $2 $3 $4) } - -letBinding :: { LetBinding () } - : ident '::' type { LetBindingSignature () (Labeled $1 $2 $3) } - | ident guardedDecl { LetBindingName () (ValueBindingFields $1 [] $2) } - | ident many(binderAtom) guardedDecl { LetBindingName () (ValueBindingFields $1 (NE.toList $2) $3) } - | binder1 '=' exprWhere { LetBindingPattern () $1 $2 $3 } - -caseBranch :: { (Separated (Binder ()), Guarded ()) } - : sep(binder1, ',') guardedCase { ($1, $2) } - -guardedDecl :: { Guarded () } - : '=' exprWhere { Unconditional $1 $2 } - | many(guardedDeclExpr) { Guarded $1 } - -guardedDeclExpr :: { GuardedExpr () } - : guard '=' exprWhere { uncurry GuardedExpr $1 $2 $3 } - -guardedCase :: { Guarded () } - : '->' exprWhere { Unconditional $1 $2 } - | many(guardedCaseExpr) { Guarded $1 } - -guardedCaseExpr :: { GuardedExpr () } - : guard '->' exprWhere { uncurry GuardedExpr $1 $2 $3 } - --- Do/Ado statements and pattern guards require unbounded lookahead due to many --- conflicts between `binder` and `expr` syntax. For example `Foo a b c` can --- either be a constructor `binder` or several `expr` applications, and we won't --- know until we see a `<-` or layout separator. --- --- One way to resolve this would be to parse a `binder` as an `expr` and then --- reassociate it after the fact. However this means we can't use the `binder` --- productions to parse it, so we'd have to maintain an ad-hoc handwritten --- parser which is very difficult to audit. --- --- As an alternative we introduce some backtracking. Using %partial parsers and --- monadic reductions, we can invoke productions manually and use the --- backtracking `tryPrefix` combinator. Binders are generally very short in --- comparison to expressions, so the cost is modest. --- --- doBlock --- : 'do' '\{' manySep(doStatement, '\;') '\}' --- --- doStatement --- : 'let' '\{' manySep(letBinding, '\;') '\}' --- | expr --- | binder '<-' expr --- --- guard --- : '|' sep(patternGuard, ',') --- --- patternGuard --- : expr1 --- | binder '<-' expr1 --- -doBlock :: { DoBlock () } - : 'do' '\{' - {%% revert $ do - res <- parseDoStatement - when (null res) $ addFailure [$2] ErrEmptyDo - pure $ DoBlock $1 $ NE.fromList res - } - -adoBlock :: { (SourceToken, [DoStatement ()]) } - : 'ado' '\{' '\}' { ($1, []) } - | 'ado' '\{' - {%% revert $ fmap ($1,) parseDoStatement } - -doStatement :: { [DoStatement ()] } - : 'let' '\{' manySep(letBinding, '\;') '\}' - {%^ revert $ fmap (DoLet $1 $3 :) parseDoNext } - | {- empty -} - {%^ revert $ do - stmt <- tryPrefix parseBinderAndArrow parseDoExpr - let - ctr = case stmt of - (Just (binder, sep), expr) -> - (DoBind binder sep expr :) - (Nothing, expr) -> - (DoDiscard expr :) - fmap ctr parseDoNext - } - -doExpr :: { Expr () } - : expr {%^ revert $ pure $1 } - -doNext :: { [DoStatement ()] } - : '\;' {%^ revert parseDoStatement } - | '\}' {%^ revert $ pure [] } - -guard :: { (SourceToken, Separated (PatternGuard ())) } - : '|' {%% revert $ fmap (($1,) . uncurry Separated) parseGuardStatement } - -guardStatement :: { (PatternGuard (), [(SourceToken, PatternGuard ())]) } - : {- empty -} - {%^ revert $ do - grd <- fmap (uncurry PatternGuard) $ tryPrefix parseBinderAndArrow parseGuardExpr - fmap (grd,) parseGuardNext - } - -guardExpr :: { Expr() } - : expr1 {%^ revert $ pure $1 } - -guardNext :: { [(SourceToken, PatternGuard ())] } - : ',' {%^ revert $ fmap (\(g, gs) -> ($1, g) : gs) parseGuardStatement } - | {- empty -} {%^ revert $ pure [] } - -binderAndArrow :: { (Binder (), SourceToken) } - : binder '<-' {%^ revert $ pure ($1, $2) } - -binder :: { Binder () } - : binder1 { $1 } - | binder1 '::' type { BinderTyped () $1 $2 $3 } - -binder1 :: { Binder () } - : binder2 { $1 } - | binder1 qualOp binder2 { BinderOp () $1 (getQualifiedOpName $2) $3 } - -binder2 :: { Binder () } - : many(binderAtom) {% toBinderConstructor $1 } - | '-' number { uncurry (BinderNumber () (Just $1)) $2 } - -binderAtom :: { Binder () } - : '_' { BinderWildcard () $1 } - | ident %shift { BinderVar () $1 } - | ident '@' binderAtom { BinderNamed () $1 $2 $3 } - | qualProperName { BinderConstructor () (getQualifiedProperName $1) [] } - | boolean { uncurry (BinderBoolean ()) $1 } - | char { uncurry (BinderChar ()) $1 } - | string { uncurry (BinderString ()) $1 } - | number { uncurry (BinderNumber () Nothing) $1 } - | delim('[', binder, ',', ']') { BinderArray () $1 } - | delim('{', recordBinder, ',', '}') { BinderRecord () $1 } - | '(' binder ')' { BinderParens () (Wrapped $1 $2 $3) } - -recordBinder :: { RecordLabeled (Binder ()) } - : label {% fmap RecordPun . toName Ident $ lblTok $1 } - | label '=' binder {% addFailure [$2] ErrRecordUpdateInCtr *> pure (RecordPun $ unexpectedName $ lblTok $1) } - | label ':' binder { RecordField $1 $2 $3 } - --- By splitting up the module header from the body, we can incrementally parse --- just the header, and then continue parsing the body while still sharing work. -moduleHeader :: { Module () } - : 'module' moduleName exports 'where' '\{' moduleImports - { (Module () $1 $2 $3 $4 $6 [] []) } - -moduleBody :: { ([Declaration ()], [Comment LineFeed]) } - : moduleDecls '\}' - {%^ \(SourceToken ann _) -> pure (snd $1, tokLeadingComments ann) } - -moduleImports :: { [ImportDecl ()] } - : importDecls importDecl '\}' - {%^ revert $ pushBack $3 *> pure (reverse ($2 : $1)) } - | importDecls - {%^ revert $ pure (reverse $1) } - -importDecls :: { [ImportDecl ()] } - : importDecls importDecl '\;' { $2 : $1 } - | {- empty -} { [] } - -moduleDecls :: { ([ImportDecl ()], [Declaration ()]) } - : manySep(moduleDecl, '\;') {% toModuleDecls $ NE.toList $1 } - | {- empty -} { ([], []) } - -moduleDecl :: { TmpModuleDecl () } - : importDecl { TmpImport $1 } - | sep(decl, declElse) { TmpChain $1 } - -declElse :: { SourceToken } - : 'else' { $1 } - | 'else' '\;' { $1 } - -exports :: { Maybe (DelimitedNonEmpty (Export ())) } - : {- empty -} { Nothing } - | '(' sep(export, ',') ')' { Just (Wrapped $1 $2 $3) } - -export :: { Export () } - : ident { ExportValue () $1 } - | symbol { ExportOp () (getOpName $1) } - | properName { ExportType () (getProperName $1) Nothing } - | properName dataMembers { ExportType () (getProperName $1) (Just $2) } - | 'type' symbol { ExportTypeOp () $1 (getOpName $2) } - | 'class' properName { ExportClass () $1 (getProperName $2) } - | 'module' moduleName { ExportModule () $1 $2 } - -dataMembers :: { (DataMembers ()) } - : '(..)' { DataAll () $1 } - | '(' ')' { DataEnumerated () (Wrapped $1 Nothing $2) } - | '(' sep(properName, ',') ')' { DataEnumerated () (Wrapped $1 (Just \$ getProperName <\$> $2) $3) } - -importDecl :: { ImportDecl () } - : 'import' moduleName imports { ImportDecl () $1 $2 $3 Nothing } - | 'import' moduleName imports 'as' moduleName { ImportDecl () $1 $2 $3 (Just ($4, $5)) } - -imports :: { Maybe (Maybe SourceToken, DelimitedNonEmpty (Import ())) } - : {- empty -} { Nothing } - | '(' sep(import, ',') ')' { Just (Nothing, Wrapped $1 $2 $3) } - | 'hiding' '(' sep(import, ',') ')' { Just (Just $1, Wrapped $2 $3 $4) } - -import :: { Import () } - : ident { ImportValue () $1 } - | symbol { ImportOp () (getOpName $1) } - | properName { ImportType () (getProperName $1) Nothing } - | properName dataMembers { ImportType () (getProperName $1) (Just $2) } - | 'type' symbol { ImportTypeOp () $1 (getOpName $2) } - | 'class' properName { ImportClass () $1 (getProperName $2) } - -decl :: { Declaration () } - : dataHead { DeclData () $1 Nothing } - | dataHead '=' sep(dataCtor, '|') { DeclData () $1 (Just ($2, $3)) } - | typeHead '=' type {% checkNoWildcards $3 *> pure (DeclType () $1 $2 $3) } - | newtypeHead '=' properName typeAtom {% checkNoWildcards $4 *> pure (DeclNewtype () $1 $2 (getProperName $3) $4) } - | classHead { either id (\h -> DeclClass () h Nothing) $1 } - | classHead 'where' '\{' manySep(classMember, '\;') '\}' {% either (const (parseError $2)) (\h -> pure $ DeclClass () h (Just ($2, $4))) $1 } - | instHead { DeclInstanceChain () (Separated (Instance $1 Nothing) []) } - | instHead 'where' '\{' manySep(instBinding, '\;') '\}' { DeclInstanceChain () (Separated (Instance $1 (Just ($2, $4))) []) } - | 'data' properName '::' type {% checkNoWildcards $4 *> pure (DeclKindSignature () $1 (Labeled (getProperName $2) $3 $4)) } - | 'newtype' properName '::' type {% checkNoWildcards $4 *> pure (DeclKindSignature () $1 (Labeled (getProperName $2) $3 $4)) } - | 'type' properName '::' type {% checkNoWildcards $4 *> pure (DeclKindSignature () $1 (Labeled (getProperName $2) $3 $4)) } - | 'derive' instHead { DeclDerive () $1 Nothing $2 } - | 'derive' 'newtype' instHead { DeclDerive () $1 (Just $2) $3 } - | ident '::' type { DeclSignature () (Labeled $1 $2 $3) } - | ident manyOrEmpty(binderAtom) guardedDecl { DeclValue () (ValueBindingFields $1 $2 $3) } - | fixity { DeclFixity () $1 } - | 'foreign' 'import' ident '::' type {% when (isConstrained $5) (addFailure ([$1, $2, nameTok $3, $4] <> toList (flattenType $5)) ErrConstraintInForeignImportSyntax) *> pure (DeclForeign () $1 $2 (ForeignValue (Labeled $3 $4 $5))) } - | 'foreign' 'import' 'data' properName '::' type { DeclForeign () $1 $2 (ForeignData $3 (Labeled (getProperName $4) $5 $6)) } - | 'type' 'role' properName many(role) { DeclRole () $1 $2 (getProperName $3) $4 } - -dataHead :: { DataHead () } - : 'data' properName manyOrEmpty(typeVarBindingPlain) { DataHead $1 (getProperName $2) $3 } - -typeHead :: { DataHead () } - : 'type' properName manyOrEmpty(typeVarBindingPlain) { DataHead $1 (getProperName $2) $3 } - -newtypeHead :: { DataHead () } - : 'newtype' properName manyOrEmpty(typeVarBindingPlain) { DataHead $1 (getProperName $2) $3 } - -dataCtor :: { DataCtor () } - : properName manyOrEmpty(typeAtom) - {% for_ $2 checkNoWildcards *> pure (DataCtor () (getProperName $1) $2) } - --- Class head syntax requires unbounded lookahead due to a conflict between --- row syntax and `typeVarBinding`. `(a :: B)` is either a row in `constraint` --- where `B` is a type or a `typeVarBinding` where `B` is a kind. We must see --- either a `<=`, `where`, or layout delimiter before deciding which it is. --- --- classHead --- : 'class' classNameAndFundeps --- | 'class' constraints '<=' classNameAndFundeps --- -classHead :: { Either (Declaration ()) (ClassHead ()) } - : 'class' - {%% revert $ oneOf $ NE.fromList - [ fmap (Left . DeclKindSignature () $1) parseClassSignature - , do - (super, (name, vars, fundeps)) <- tryPrefix parseClassSuper parseClassNameAndFundeps - let hd = ClassHead $1 super name vars fundeps - checkFundeps hd - pure $ Right hd - ] - } - -classSignature :: { Labeled (Name (N.ProperName 'N.TypeName)) (Type ()) } - : properName '::' type {%^ revert $ checkNoWildcards $3 *> pure (Labeled (getProperName $1) $2 $3) } - -classSuper :: { (OneOrDelimited (Constraint ()), SourceToken) } - : constraints '<=' {%^ revert $ pure ($1, $2) } - -classNameAndFundeps :: { (Name (N.ProperName 'N.ClassName), [TypeVarBinding ()], Maybe (SourceToken, Separated ClassFundep)) } - : properName manyOrEmpty(typeVarBindingPlain) fundeps {%^ revert $ pure (getProperName $1, $2, $3) } - -fundeps :: { Maybe (SourceToken, Separated ClassFundep) } - : {- empty -} { Nothing } - | '|' sep(fundep, ',') { Just ($1, $2) } - -fundep :: { ClassFundep } - : '->' many(ident) { FundepDetermined $1 $2 } - | many(ident) '->' many(ident) { FundepDetermines $1 $2 $3 } - -classMember :: { Labeled (Name Ident) (Type ()) } - : ident '::' type {% checkNoWildcards $3 *> pure (Labeled $1 $2 $3) } - -instHead :: { InstanceHead () } - : 'instance' constraints '=>' qualProperName manyOrEmpty(typeAtom) - { InstanceHead $1 Nothing (Just ($2, $3)) (getQualifiedProperName $4) $5 } - | 'instance' qualProperName manyOrEmpty(typeAtom) - { InstanceHead $1 Nothing Nothing (getQualifiedProperName $2) $3 } - | 'instance' ident '::' constraints '=>' qualProperName manyOrEmpty(typeAtom) - { InstanceHead $1 (Just ($2, $3)) (Just ($4, $5)) (getQualifiedProperName $6) $7 } - | 'instance' ident '::' qualProperName manyOrEmpty(typeAtom) - { InstanceHead $1 (Just ($2, $3)) Nothing (getQualifiedProperName $4) $5 } - -constraints :: { OneOrDelimited (Constraint ()) } - : constraint { One $1 } - | '(' sep(constraint, ',') ')' { Many (Wrapped $1 $2 $3) } - -constraint :: { Constraint () } - : qualProperName manyOrEmpty(typeAtom) {% for_ $2 checkNoWildcards *> for_ $2 checkNoForalls *> pure (Constraint () (getQualifiedProperName $1) $2) } - | '(' constraint ')' { ConstraintParens () (Wrapped $1 $2 $3) } - -instBinding :: { InstanceBinding () } - : ident '::' type { InstanceBindingSignature () (Labeled $1 $2 $3) } - | ident manyOrEmpty(binderAtom) guardedDecl { InstanceBindingName () (ValueBindingFields $1 $2 $3) } - -fixity :: { FixityFields } - : infix int qualIdent 'as' op { FixityFields $1 $2 (FixityValue (fmap Left $3) $4 (getOpName $5)) } - | infix int qualProperName 'as' op { FixityFields $1 $2 (FixityValue (fmap Right (getQualifiedProperName $3)) $4 (getOpName $5)) } - | infix int 'type' qualProperName 'as' op { FixityFields $1 $2 (FixityType $3 (getQualifiedProperName $4) $5 (getOpName $6)) } - -infix :: { (SourceToken, Fixity) } - : 'infix' { ($1, Infix) } - | 'infixl' { ($1, Infixl) } - | 'infixr' { ($1, Infixr) } - -role :: { Role } - : 'nominal' { Role $1 R.Nominal } - | 'representational' { Role $1 R.Representational } - | 'phantom' { Role $1 R.Phantom } - --- Partial parsers which can be combined with combinators for adhoc use. We need --- to revert the lookahead token so that it doesn't consume an extra token --- before succeeding. - -importDeclP :: { ImportDecl () } - : importDecl {%^ revert $ pure $1 } - -declP :: { Declaration () } - : decl {%^ revert $ pure $1 } - -exprP :: { Expr () } - : expr {%^ revert $ pure $1 } - -typeP :: { Type () } - : type {%^ revert $ pure $1 } - -moduleNameP :: { Name N.ModuleName } - : moduleName {%^ revert $ pure $1 } - -qualIdentP :: { QualifiedName Ident } - : qualIdent {%^ revert $ pure $1 } - -{ -lexer :: (SourceToken -> Parser a) -> Parser a -lexer k = munch >>= k - -parse :: Text -> ([ParserWarning], Either (NE.NonEmpty ParserError) (Module ())) -parse = either (([],) . Left) resFull . parseModule . lexModule - -data PartialResult a = PartialResult - { resPartial :: a - , resFull :: ([ParserWarning], Either (NE.NonEmpty ParserError) a) - } deriving (Functor) - -parseModule :: [LexResult] -> Either (NE.NonEmpty ParserError) (PartialResult (Module ())) -parseModule toks = fmap (\header -> PartialResult header (parseFull header)) headerRes - where - (st, headerRes) = - runParser (ParserState toks [] []) parseModuleHeader - - parseFull header = do - let (ParserState _ _ warnings, res) = runParser st parseModuleBody - (warnings, (\(decls, trailing) -> header { modDecls = decls, modTrailingComments = trailing }) <$> res) -} diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Positions.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Positions.hs deleted file mode 100644 index 20d57242..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Positions.hs +++ /dev/null @@ -1,338 +0,0 @@ --- | This module contains utilities for calculating positions and offsets. While --- tokens are annotated with ranges, CST nodes are not, but they can be --- dynamically derived with the functions in this module, which will return the --- first and last tokens for a given node. - -module Language.PureScript.CST.Positions where - -import Prelude - -import Data.Foldable (foldl') -import Data.List.NonEmpty qualified as NE -import Data.Maybe (fromMaybe) -import Data.Text (Text) -import Data.Void (Void) -import Data.Text qualified as Text -import Language.PureScript.CST.Types - -advanceToken :: SourcePos -> Token -> SourcePos -advanceToken pos = applyDelta pos . tokenDelta - -advanceLeading :: SourcePos -> [Comment LineFeed] -> SourcePos -advanceLeading = foldl' $ \a -> applyDelta a . commentDelta lineDelta - -advanceTrailing :: SourcePos -> [Comment Void] -> SourcePos -advanceTrailing = foldl' $ \a -> applyDelta a . commentDelta (const (0, 0)) - -tokenDelta :: Token -> (Int, Int) -tokenDelta = \case - TokLeftParen -> (0, 1) - TokRightParen -> (0, 1) - TokLeftBrace -> (0, 1) - TokRightBrace -> (0, 1) - TokLeftSquare -> (0, 1) - TokRightSquare -> (0, 1) - TokLeftArrow ASCII -> (0, 2) - TokLeftArrow Unicode -> (0, 1) - TokRightArrow ASCII -> (0, 2) - TokRightArrow Unicode -> (0, 1) - TokRightFatArrow ASCII -> (0, 2) - TokRightFatArrow Unicode -> (0, 1) - TokDoubleColon ASCII -> (0, 2) - TokDoubleColon Unicode -> (0, 1) - TokForall ASCII -> (0, 6) - TokForall Unicode -> (0, 1) - TokEquals -> (0, 1) - TokPipe -> (0, 1) - TokTick -> (0, 1) - TokDot -> (0, 1) - TokComma -> (0, 1) - TokUnderscore -> (0, 1) - TokBackslash -> (0, 1) - TokLowerName qual name -> (0, qualDelta qual + Text.length name) - TokUpperName qual name -> (0, qualDelta qual + Text.length name) - TokOperator qual sym -> (0, qualDelta qual + Text.length sym) - TokSymbolName qual sym -> (0, qualDelta qual + Text.length sym + 2) - TokSymbolArr Unicode -> (0, 3) - TokSymbolArr ASCII -> (0, 4) - TokHole hole -> (0, Text.length hole + 1) - TokChar raw _ -> (0, Text.length raw + 2) - TokInt raw _ -> (0, Text.length raw) - TokNumber raw _ -> (0, Text.length raw) - TokString raw _ -> multiLine 1 $ textDelta raw - TokRawString raw -> multiLine 3 $ textDelta raw - TokLayoutStart -> (0, 0) - TokLayoutSep -> (0, 0) - TokLayoutEnd -> (0, 0) - TokEof -> (0, 0) - -qualDelta :: [Text] -> Int -qualDelta = foldr ((+) . (+ 1) . Text.length) 0 - -multiLine :: Int -> (Int, Int) -> (Int, Int) -multiLine n (0, c) = (0, c + n + n) -multiLine n (l, c) = (l, c + n) - -commentDelta :: (a -> (Int, Int)) -> Comment a -> (Int, Int) -commentDelta k = \case - Comment raw -> textDelta raw - Space n -> (0, n) - Line a -> k a - -lineDelta :: LineFeed -> (Int, Int) -lineDelta _ = (1, 1) - -textDelta :: Text -> (Int, Int) -textDelta = Text.foldl' go (0, 0) - where - go (!l, !c) = \case - '\n' -> (l + 1, 1) - _ -> (l, c + 1) - -applyDelta :: SourcePos -> (Int, Int) -> SourcePos -applyDelta (SourcePos l c) = \case - (0, n) -> SourcePos l (c + n) - (k, d) -> SourcePos (l + k) d - -sepLast :: Separated a -> a -sepLast (Separated hd []) = hd -sepLast (Separated _ tl) = snd $ last tl - -type TokenRange = (SourceToken, SourceToken) - -toSourceRange :: TokenRange -> SourceRange -toSourceRange (a, b) = widen (srcRange a) (srcRange b) - -widen :: SourceRange -> SourceRange -> SourceRange -widen (SourceRange s1 _) (SourceRange _ e2) = SourceRange s1 e2 - -srcRange :: SourceToken -> SourceRange -srcRange = tokRange . tokAnn - -nameRange :: Name a -> TokenRange -nameRange a = (nameTok a, nameTok a) - -qualRange :: QualifiedName a -> TokenRange -qualRange a = (qualTok a, qualTok a) - -wrappedRange :: Wrapped a -> TokenRange -wrappedRange Wrapped { wrpOpen, wrpClose } = (wrpOpen, wrpClose) - -moduleRange :: Module a -> TokenRange -moduleRange Module { modKeyword, modWhere, modImports, modDecls } = - case (modImports, modDecls) of - ([], []) -> (modKeyword, modWhere) - (is, []) -> (modKeyword, snd . importDeclRange $ last is) - (_, ds) -> (modKeyword, snd . declRange $ last ds) - -exportRange :: Export a -> TokenRange -exportRange = \case - ExportValue _ a -> nameRange a - ExportOp _ a -> nameRange a - ExportType _ a b - | Just b' <- b -> (nameTok a, snd $ dataMembersRange b') - | otherwise -> nameRange a - ExportTypeOp _ a b -> (a, nameTok b) - ExportClass _ a b -> (a, nameTok b) - ExportModule _ a b -> (a, nameTok b) - -importDeclRange :: ImportDecl a -> TokenRange -importDeclRange ImportDecl { impKeyword, impModule, impNames, impQual } - | Just (_, modName) <- impQual = (impKeyword, nameTok modName) - | Just (_, imports) <- impNames = (impKeyword, wrpClose imports) - | otherwise = (impKeyword, nameTok impModule) - -importRange :: Import a -> TokenRange -importRange = \case - ImportValue _ a -> nameRange a - ImportOp _ a -> nameRange a - ImportType _ a b - | Just b' <- b -> (nameTok a, snd $ dataMembersRange b') - | otherwise -> nameRange a - ImportTypeOp _ a b -> (a, nameTok b) - ImportClass _ a b -> (a, nameTok b) - -dataMembersRange :: DataMembers a -> TokenRange -dataMembersRange = \case - DataAll _ a -> (a, a) - DataEnumerated _ (Wrapped a _ b) -> (a, b) - -declRange :: Declaration a -> TokenRange -declRange = \case - DeclData _ hd ctors - | Just (_, cs) <- ctors -> (fst start, snd . dataCtorRange $ sepLast cs) - | otherwise -> start - where start = dataHeadRange hd - DeclType _ a _ b -> (fst $ dataHeadRange a, snd $ typeRange b) - DeclNewtype _ a _ _ b -> (fst $ dataHeadRange a, snd $ typeRange b) - DeclClass _ hd body - | Just (_, ts) <- body -> (fst start, snd . typeRange . lblValue $ NE.last ts) - | otherwise -> start - where start = classHeadRange hd - DeclInstanceChain _ a -> (fst . instanceRange $ sepHead a, snd . instanceRange $ sepLast a) - DeclDerive _ a _ b -> (a, snd $ instanceHeadRange b) - DeclKindSignature _ a (Labeled _ _ b) -> (a, snd $ typeRange b) - DeclSignature _ (Labeled a _ b) -> (nameTok a, snd $ typeRange b) - DeclValue _ a -> valueBindingFieldsRange a - DeclFixity _ (FixityFields a _ (FixityValue _ _ b)) -> (fst a, nameTok b) - DeclFixity _ (FixityFields a _ (FixityType _ _ _ b)) -> (fst a, nameTok b) - DeclForeign _ a _ b -> (a, snd $ foreignRange b) - DeclRole _ a _ _ b -> (a, roleTok $ NE.last b) - -dataHeadRange :: DataHead a -> TokenRange -dataHeadRange (DataHead kw name vars) - | [] <- vars = (kw, nameTok name) - | otherwise = (kw, snd . typeVarBindingRange $ last vars) - -dataCtorRange :: DataCtor a -> TokenRange -dataCtorRange (DataCtor _ name fields) - | [] <- fields = nameRange name - | otherwise = (nameTok name, snd . typeRange $ last fields) - -classHeadRange :: ClassHead a -> TokenRange -classHeadRange (ClassHead kw _ name vars fdeps) - | Just (_, fs) <- fdeps = (kw, snd . classFundepRange $ sepLast fs) - | [] <- vars = (kw, snd $ nameRange name) - | otherwise = (kw, snd . typeVarBindingRange $ last vars) - -classFundepRange :: ClassFundep -> TokenRange -classFundepRange = \case - FundepDetermined arr bs -> (arr, nameTok $ NE.last bs) - FundepDetermines as _ bs -> (nameTok $ NE.head as, nameTok $ NE.last bs) - -instanceRange :: Instance a -> TokenRange -instanceRange (Instance hd bd) - | Just (_, ts) <- bd = (fst start, snd . instanceBindingRange $ NE.last ts) - | otherwise = start - where start = instanceHeadRange hd - -instanceHeadRange :: InstanceHead a -> TokenRange -instanceHeadRange (InstanceHead kw _ _ cls types) - | [] <- types = (kw, qualTok cls) - | otherwise = (kw, snd . typeRange $ last types) - -instanceBindingRange :: InstanceBinding a -> TokenRange -instanceBindingRange = \case - InstanceBindingSignature _ (Labeled a _ b) -> (nameTok a, snd $ typeRange b) - InstanceBindingName _ a -> valueBindingFieldsRange a - -foreignRange :: Foreign a -> TokenRange -foreignRange = \case - ForeignValue (Labeled a _ b) -> (nameTok a, snd $ typeRange b) - ForeignData a (Labeled _ _ b) -> (a, snd $ typeRange b) - ForeignKind a b -> (a, nameTok b) - -valueBindingFieldsRange :: ValueBindingFields a -> TokenRange -valueBindingFieldsRange (ValueBindingFields a _ b) = (nameTok a, snd $ guardedRange b) - -guardedRange :: Guarded a -> TokenRange -guardedRange = \case - Unconditional a b -> (a, snd $ whereRange b) - Guarded as -> (fst . guardedExprRange $ NE.head as, snd . guardedExprRange $ NE.last as) - -guardedExprRange :: GuardedExpr a -> TokenRange -guardedExprRange (GuardedExpr a _ _ b) = (a, snd $ whereRange b) - -whereRange :: Where a -> TokenRange -whereRange (Where a bs) - | Just (_, ls) <- bs = (fst $ exprRange a, snd . letBindingRange $ NE.last ls) - | otherwise = exprRange a - -typeRange :: Type a -> TokenRange -typeRange = \case - TypeVar _ a -> nameRange a - TypeConstructor _ a -> qualRange a - TypeWildcard _ a -> (a, a) - TypeHole _ a -> nameRange a - TypeString _ a _ -> (a, a) - TypeInt _ a b _ -> (fromMaybe b a, b) - TypeRow _ a -> wrappedRange a - TypeRecord _ a -> wrappedRange a - TypeForall _ a _ _ b -> (a, snd $ typeRange b) - TypeKinded _ a _ b -> (fst $ typeRange a, snd $ typeRange b) - TypeApp _ a b -> (fst $ typeRange a, snd $ typeRange b) - TypeOp _ a _ b -> (fst $ typeRange a, snd $ typeRange b) - TypeOpName _ a -> qualRange a - TypeArr _ a _ b -> (fst $ typeRange a, snd $ typeRange b) - TypeArrName _ a -> (a, a) - TypeConstrained _ a _ b -> (fst $ constraintRange a, snd $ typeRange b) - TypeParens _ a -> wrappedRange a - TypeUnaryRow _ a b -> (a, snd $ typeRange b) - -constraintRange :: Constraint a -> TokenRange -constraintRange = \case - Constraint _ name args - | [] <- args -> qualRange name - | otherwise -> (qualTok name, snd . typeRange $ last args) - ConstraintParens _ wrp -> wrappedRange wrp - -typeVarBindingRange :: TypeVarBinding a -> TokenRange -typeVarBindingRange = \case - TypeVarKinded a -> wrappedRange a - TypeVarName (atSign, a) -> (fromMaybe (nameTok a) atSign, nameTok a) - -exprRange :: Expr a -> TokenRange -exprRange = \case - ExprHole _ a -> nameRange a - ExprSection _ a -> (a, a) - ExprIdent _ a -> qualRange a - ExprConstructor _ a -> qualRange a - ExprBoolean _ a _ -> (a, a) - ExprChar _ a _ -> (a, a) - ExprString _ a _ -> (a, a) - ExprNumber _ a _ -> (a, a) - ExprArray _ a -> wrappedRange a - ExprRecord _ a -> wrappedRange a - ExprParens _ a -> wrappedRange a - ExprTyped _ a _ b -> (fst $ exprRange a, snd $ typeRange b) - ExprInfix _ a _ b -> (fst $ exprRange a, snd $ exprRange b) - ExprOp _ a _ b -> (fst $ exprRange a, snd $ exprRange b) - ExprOpName _ a -> qualRange a - ExprNegate _ a b -> (a, snd $ exprRange b) - ExprRecordAccessor _ (RecordAccessor a _ b) -> (fst $ exprRange a, lblTok $ sepLast b) - ExprRecordUpdate _ a b -> (fst $ exprRange a, snd $ wrappedRange b) - ExprApp _ a b -> (fst $ exprRange a, snd $ exprRange b) - ExprVisibleTypeApp _ a _ b -> (fst $ exprRange a, snd $ typeRange b) - ExprLambda _ (Lambda a _ _ b) -> (a, snd $ exprRange b) - ExprIf _ (IfThenElse a _ _ _ _ b) -> (a, snd $ exprRange b) - ExprCase _ (CaseOf a _ _ c) -> (a, snd . guardedRange . snd $ NE.last c) - ExprLet _ (LetIn a _ _ b) -> (a, snd $ exprRange b) - ExprDo _ (DoBlock a b) -> (a, snd . doStatementRange $ NE.last b) - ExprAdo _ (AdoBlock a _ _ b) -> (a, snd $ exprRange b) - -letBindingRange :: LetBinding a -> TokenRange -letBindingRange = \case - LetBindingSignature _ (Labeled a _ b) -> (nameTok a, snd $ typeRange b) - LetBindingName _ a -> valueBindingFieldsRange a - LetBindingPattern _ a _ b -> (fst $ binderRange a, snd $ whereRange b) - -doStatementRange :: DoStatement a -> TokenRange -doStatementRange = \case - DoLet a bs -> (a, snd . letBindingRange $ NE.last bs) - DoDiscard a -> exprRange a - DoBind a _ b -> (fst $ binderRange a, snd $ exprRange b) - -binderRange :: Binder a -> TokenRange -binderRange = \case - BinderWildcard _ a -> (a, a) - BinderVar _ a -> nameRange a - BinderNamed _ a _ b -> (nameTok a, snd $ binderRange b) - BinderConstructor _ a bs - | [] <- bs -> qualRange a - | otherwise -> (qualTok a, snd . binderRange $ last bs) - BinderBoolean _ a _ -> (a, a) - BinderChar _ a _ -> (a, a) - BinderString _ a _ -> (a, a) - BinderNumber _ a b _ - | Just a' <- a -> (a', b) - | otherwise -> (b, b) - BinderArray _ a -> wrappedRange a - BinderRecord _ a -> wrappedRange a - BinderParens _ a -> wrappedRange a - BinderTyped _ a _ b -> (fst $ binderRange a, snd $ typeRange b) - BinderOp _ a _ b -> (fst $ binderRange a, snd $ binderRange b) - -recordUpdateRange :: RecordUpdate a -> TokenRange -recordUpdateRange = \case - RecordUpdateLeaf a _ b -> (lblTok a, snd $ exprRange b) - RecordUpdateBranch a (Wrapped _ _ b) -> (lblTok a, b) diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Print.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Print.hs deleted file mode 100644 index f6d300ab..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Print.hs +++ /dev/null @@ -1,96 +0,0 @@ --- | This is just a simple token printer. It's not a full fledged formatter, but --- it is used by the layout golden tests. Printing each token in the tree with --- this printer will result in the exact input that was given to the lexer. - -module Language.PureScript.CST.Print - ( printToken - , printTokens - , printModule - , printLeadingComment - , printTrailingComment - ) where - -import Prelude - -import Data.DList qualified as DList -import Data.Text (Text) -import Data.Text qualified as Text -import Language.PureScript.CST.Types (Comment(..), LineFeed(..), Module, SourceStyle(..), SourceToken(..), Token(..), TokenAnn(..)) -import Language.PureScript.CST.Flatten (flattenModule) - -printToken :: Token -> Text -printToken = printToken' True - --- | Prints a given Token. The bool controls whether or not layout --- tokens should be printed. -printToken' :: Bool -> Token -> Text -printToken' showLayout = \case - TokLeftParen -> "(" - TokRightParen -> ")" - TokLeftBrace -> "{" - TokRightBrace -> "}" - TokLeftSquare -> "[" - TokRightSquare -> "]" - TokLeftArrow ASCII -> "<-" - TokLeftArrow Unicode -> "←" - TokRightArrow ASCII -> "->" - TokRightArrow Unicode -> "→" - TokRightFatArrow ASCII -> "=>" - TokRightFatArrow Unicode -> "⇒" - TokDoubleColon ASCII -> "::" - TokDoubleColon Unicode -> "∷" - TokForall ASCII -> "forall" - TokForall Unicode -> "∀" - TokEquals -> "=" - TokPipe -> "|" - TokTick -> "`" - TokDot -> "." - TokComma -> "," - TokUnderscore -> "_" - TokBackslash -> "\\" - TokLowerName qual name -> printQual qual <> name - TokUpperName qual name -> printQual qual <> name - TokOperator qual sym -> printQual qual <> sym - TokSymbolName qual sym -> printQual qual <> "(" <> sym <> ")" - TokSymbolArr Unicode -> "(→)" - TokSymbolArr ASCII -> "(->)" - TokHole hole -> "?" <> hole - TokChar raw _ -> "'" <> raw <> "'" - TokString raw _ -> "\"" <> raw <> "\"" - TokRawString raw -> "\"\"\"" <> raw <> "\"\"\"" - TokInt raw _ -> raw - TokNumber raw _ -> raw - TokLayoutStart -> if showLayout then "{" else "" - TokLayoutSep -> if showLayout then ";" else "" - TokLayoutEnd -> if showLayout then "}" else "" - TokEof -> if showLayout then "" else "" - -printQual :: [Text] -> Text -printQual = Text.concat . map (<> ".") - -printTokens :: [SourceToken] -> Text -printTokens = printTokens' True - -printTokens' :: Bool -> [SourceToken] -> Text -printTokens' showLayout toks = Text.concat (map pp toks) - where - pp (SourceToken (TokenAnn _ leading trailing) tok) = - Text.concat (map printLeadingComment leading) - <> printToken' showLayout tok - <> Text.concat (map printTrailingComment trailing) - -printModule :: Module a -> Text -printModule = printTokens' False . DList.toList . flattenModule - -printLeadingComment :: Comment LineFeed -> Text -printLeadingComment = \case - Comment raw -> raw - Space n -> Text.replicate n " " - Line LF -> "\n" - Line CRLF -> "\r\n" - -printTrailingComment :: Comment void -> Text -printTrailingComment = \case - Comment raw -> raw - Space n -> Text.replicate n " " - Line _ -> "" diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Traversals.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Traversals.hs deleted file mode 100644 index 23532915..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Traversals.hs +++ /dev/null @@ -1,11 +0,0 @@ -module Language.PureScript.CST.Traversals where - -import Prelude - -import Language.PureScript.CST.Types (Separated(..)) - -everythingOnSeparated :: (r -> r -> r) -> (a -> r) -> Separated a -> r -everythingOnSeparated op k (Separated hd tl) = go hd tl - where - go a [] = k a - go a (b : bs) = k a `op` go (snd b) bs diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Traversals/Type.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Traversals/Type.hs deleted file mode 100644 index c61e65ca..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Traversals/Type.hs +++ /dev/null @@ -1,41 +0,0 @@ -module Language.PureScript.CST.Traversals.Type where - -import Prelude - -import Language.PureScript.CST.Types (Constraint(..), Labeled(..), Row(..), Type(..), Wrapped(..)) -import Language.PureScript.CST.Traversals (everythingOnSeparated) - -everythingOnTypes :: (r -> r -> r) -> (Type a -> r) -> Type a -> r -everythingOnTypes op k = goTy - where - goTy ty = case ty of - TypeVar _ _ -> k ty - TypeConstructor _ _ -> k ty - TypeWildcard _ _ -> k ty - TypeHole _ _ -> k ty - TypeString _ _ _ -> k ty - TypeInt _ _ _ _ -> k ty - TypeRow _ (Wrapped _ row _) -> goRow ty row - TypeRecord _ (Wrapped _ row _) -> goRow ty row - TypeForall _ _ _ _ ty2 -> k ty `op` goTy ty2 - TypeKinded _ ty2 _ ty3 -> k ty `op` (goTy ty2 `op` goTy ty3) - TypeApp _ ty2 ty3 -> k ty `op` (goTy ty2 `op` goTy ty3) - TypeOp _ ty2 _ ty3 -> k ty `op` (goTy ty2 `op` goTy ty3) - TypeOpName _ _ -> k ty - TypeArr _ ty2 _ ty3 -> k ty `op` (goTy ty2 `op` goTy ty3) - TypeArrName _ _ -> k ty - TypeConstrained _ (constraintTys -> ty2) _ ty3 - | null ty2 -> k ty `op` goTy ty3 - | otherwise -> k ty `op` (foldr1 op (k <$> ty2) `op` goTy ty3) - TypeParens _ (Wrapped _ ty2 _) -> k ty `op` goTy ty2 - TypeUnaryRow _ _ ty2 -> k ty `op` goTy ty2 - - goRow ty = \case - Row Nothing Nothing -> k ty - Row Nothing (Just (_, ty2)) -> k ty `op` goTy ty2 - Row (Just lbls) Nothing -> k ty `op` everythingOnSeparated op (goTy . lblValue) lbls - Row (Just lbls) (Just (_, ty2)) -> k ty `op` (everythingOnSeparated op (goTy . lblValue) lbls `op` goTy ty2) - - constraintTys = \case - Constraint _ _ tys -> tys - ConstraintParens _ (Wrapped _ c _) -> constraintTys c diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Types.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Types.hs deleted file mode 100644 index ba90f7e9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Types.hs +++ /dev/null @@ -1,440 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} --- | This module contains data types for the entire PureScript surface language. Every --- token is represented in the tree, and every token is annotated with --- whitespace and comments (both leading and trailing). This means one can write --- an exact printer so that `print . parse = id`. Every constructor is laid out --- with tokens in left-to-right order. The core productions are given a slot for --- arbitrary annotations, however this is not used by the parser. - -module Language.PureScript.CST.Types where - -import Prelude - -import Control.DeepSeq (NFData) -import Data.List.NonEmpty (NonEmpty) -import Data.Text (Text) -import Data.Void (Void) -import GHC.Generics (Generic) -import Language.PureScript.Names qualified as N -import Language.PureScript.Roles qualified as R -import Language.PureScript.PSString (PSString) - -data SourcePos = SourcePos - { srcLine :: {-# UNPACK #-} !Int - , srcColumn :: {-# UNPACK #-} !Int - } deriving (Show, Eq, Ord, Generic, NFData) - -data SourceRange = SourceRange - { srcStart :: !SourcePos - , srcEnd :: !SourcePos - } deriving (Show, Eq, Ord, Generic, NFData) - -data Comment l - = Comment !Text - | Space {-# UNPACK #-} !Int - | Line !l - deriving (Show, Eq, Ord, Generic, Functor, NFData) - -data LineFeed = LF | CRLF - deriving (Show, Eq, Ord, Generic, NFData) - -data TokenAnn = TokenAnn - { tokRange :: !SourceRange - , tokLeadingComments :: ![Comment LineFeed] - , tokTrailingComments :: ![Comment Void] - } deriving (Show, Eq, Ord, Generic, NFData) - -data SourceStyle = ASCII | Unicode - deriving (Show, Eq, Ord, Generic, NFData) - -data Token - = TokLeftParen - | TokRightParen - | TokLeftBrace - | TokRightBrace - | TokLeftSquare - | TokRightSquare - | TokLeftArrow !SourceStyle - | TokRightArrow !SourceStyle - | TokRightFatArrow !SourceStyle - | TokDoubleColon !SourceStyle - | TokForall !SourceStyle - | TokEquals - | TokPipe - | TokTick - | TokDot - | TokComma - | TokUnderscore - | TokBackslash - | TokLowerName ![Text] !Text - | TokUpperName ![Text] !Text - | TokOperator ![Text] !Text - | TokSymbolName ![Text] !Text - | TokSymbolArr !SourceStyle - | TokHole !Text - | TokChar !Text !Char - | TokString !Text !PSString - | TokRawString !Text - | TokInt !Text !Integer - | TokNumber !Text !Double - | TokLayoutStart - | TokLayoutSep - | TokLayoutEnd - | TokEof - deriving (Show, Eq, Ord, Generic, NFData) - -data SourceToken = SourceToken - { tokAnn :: !TokenAnn - , tokValue :: !Token - } deriving (Show, Eq, Ord, Generic, NFData) - -data Ident = Ident - { getIdent :: Text - } deriving (Show, Eq, Ord, Generic) - -data Name a = Name - { nameTok :: SourceToken - , nameValue :: a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data QualifiedName a = QualifiedName - { qualTok :: SourceToken - , qualModule :: Maybe N.ModuleName - , qualName :: a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Label = Label - { lblTok :: SourceToken - , lblName :: PSString - } deriving (Show, Eq, Ord, Generic) - -data Wrapped a = Wrapped - { wrpOpen :: SourceToken - , wrpValue :: a - , wrpClose :: SourceToken - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Separated a = Separated - { sepHead :: a - , sepTail :: [(SourceToken, a)] - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Labeled a b = Labeled - { lblLabel :: a - , lblSep :: SourceToken - , lblValue :: b - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -type Delimited a = Wrapped (Maybe (Separated a)) -type DelimitedNonEmpty a = Wrapped (Separated a) - -data OneOrDelimited a - = One a - | Many (DelimitedNonEmpty a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Type a - = TypeVar a (Name Ident) - | TypeConstructor a (QualifiedName (N.ProperName 'N.TypeName)) - | TypeWildcard a SourceToken - | TypeHole a (Name Ident) - | TypeString a SourceToken PSString - | TypeInt a (Maybe SourceToken) SourceToken Integer - | TypeRow a (Wrapped (Row a)) - | TypeRecord a (Wrapped (Row a)) - | TypeForall a SourceToken (NonEmpty (TypeVarBinding a)) SourceToken (Type a) - | TypeKinded a (Type a) SourceToken (Type a) - | TypeApp a (Type a) (Type a) - | TypeOp a (Type a) (QualifiedName (N.OpName 'N.TypeOpName)) (Type a) - | TypeOpName a (QualifiedName (N.OpName 'N.TypeOpName)) - | TypeArr a (Type a) SourceToken (Type a) - | TypeArrName a SourceToken - | TypeConstrained a (Constraint a) SourceToken (Type a) - | TypeParens a (Wrapped (Type a)) - | TypeUnaryRow a SourceToken (Type a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data TypeVarBinding a - = TypeVarKinded (Wrapped (Labeled (Maybe SourceToken, Name Ident) (Type a))) - | TypeVarName (Maybe SourceToken, Name Ident) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Constraint a - = Constraint a (QualifiedName (N.ProperName 'N.ClassName)) [Type a] - | ConstraintParens a (Wrapped (Constraint a)) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Row a = Row - { rowLabels :: Maybe (Separated (Labeled Label (Type a))) - , rowTail :: Maybe (SourceToken, Type a) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Module a = Module - { modAnn :: a - , modKeyword :: SourceToken - , modNamespace :: Name N.ModuleName - , modExports :: Maybe (DelimitedNonEmpty (Export a)) - , modWhere :: SourceToken - , modImports :: [ImportDecl a] - , modDecls :: [Declaration a] - , modTrailingComments :: [Comment LineFeed] - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Export a - = ExportValue a (Name Ident) - | ExportOp a (Name (N.OpName 'N.ValueOpName)) - | ExportType a (Name (N.ProperName 'N.TypeName)) (Maybe (DataMembers a)) - | ExportTypeOp a SourceToken (Name (N.OpName 'N.TypeOpName)) - | ExportClass a SourceToken (Name (N.ProperName 'N.ClassName)) - | ExportModule a SourceToken (Name N.ModuleName) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data DataMembers a - = DataAll a SourceToken - | DataEnumerated a (Delimited (Name (N.ProperName 'N.ConstructorName))) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Declaration a - = DeclData a (DataHead a) (Maybe (SourceToken, Separated (DataCtor a))) - | DeclType a (DataHead a) SourceToken (Type a) - | DeclNewtype a (DataHead a) SourceToken (Name (N.ProperName 'N.ConstructorName)) (Type a) - | DeclClass a (ClassHead a) (Maybe (SourceToken, NonEmpty (Labeled (Name Ident) (Type a)))) - | DeclInstanceChain a (Separated (Instance a)) - | DeclDerive a SourceToken (Maybe SourceToken) (InstanceHead a) - | DeclKindSignature a SourceToken (Labeled (Name (N.ProperName 'N.TypeName)) (Type a)) - | DeclSignature a (Labeled (Name Ident) (Type a)) - | DeclValue a (ValueBindingFields a) - | DeclFixity a FixityFields - | DeclForeign a SourceToken SourceToken (Foreign a) - | DeclRole a SourceToken SourceToken (Name (N.ProperName 'N.TypeName)) (NonEmpty Role) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Instance a = Instance - { instHead :: InstanceHead a - , instBody :: Maybe (SourceToken, NonEmpty (InstanceBinding a)) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data InstanceBinding a - = InstanceBindingSignature a (Labeled (Name Ident) (Type a)) - | InstanceBindingName a (ValueBindingFields a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data ImportDecl a = ImportDecl - { impAnn :: a - , impKeyword :: SourceToken - , impModule :: Name N.ModuleName - , impNames :: Maybe (Maybe SourceToken, DelimitedNonEmpty (Import a)) - , impQual :: Maybe (SourceToken, Name N.ModuleName) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Import a - = ImportValue a (Name Ident) - | ImportOp a (Name (N.OpName 'N.ValueOpName)) - | ImportType a (Name (N.ProperName 'N.TypeName)) (Maybe (DataMembers a)) - | ImportTypeOp a SourceToken (Name (N.OpName 'N.TypeOpName)) - | ImportClass a SourceToken (Name (N.ProperName 'N.ClassName)) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data DataHead a = DataHead - { dataHdKeyword :: SourceToken - , dataHdName :: Name (N.ProperName 'N.TypeName) - , dataHdVars :: [TypeVarBinding a] - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data DataCtor a = DataCtor - { dataCtorAnn :: a - , dataCtorName :: Name (N.ProperName 'N.ConstructorName) - , dataCtorFields :: [Type a] - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data ClassHead a = ClassHead - { clsKeyword :: SourceToken - , clsSuper :: Maybe (OneOrDelimited (Constraint a), SourceToken) - , clsName :: Name (N.ProperName 'N.ClassName) - , clsVars :: [TypeVarBinding a] - , clsFundeps :: Maybe (SourceToken, Separated ClassFundep) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data ClassFundep - = FundepDetermined SourceToken (NonEmpty (Name Ident)) - | FundepDetermines (NonEmpty (Name Ident)) SourceToken (NonEmpty (Name Ident)) - deriving (Show, Eq, Ord, Generic) - -data InstanceHead a = InstanceHead - { instKeyword :: SourceToken - , instNameSep :: Maybe (Name Ident, SourceToken) - , instConstraints :: Maybe (OneOrDelimited (Constraint a), SourceToken) - , instClass :: QualifiedName (N.ProperName 'N.ClassName) - , instTypes :: [Type a] - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Fixity - = Infix - | Infixl - | Infixr - deriving (Show, Eq, Ord, Generic) - -data FixityOp - = FixityValue (QualifiedName (Either Ident (N.ProperName 'N.ConstructorName))) SourceToken (Name (N.OpName 'N.ValueOpName)) - | FixityType SourceToken (QualifiedName (N.ProperName 'N.TypeName)) SourceToken (Name (N.OpName 'N.TypeOpName)) - deriving (Show, Eq, Ord, Generic) - -data FixityFields = FixityFields - { fxtKeyword :: (SourceToken, Fixity) - , fxtPrec :: (SourceToken, Integer) - , fxtOp :: FixityOp - } deriving (Show, Eq, Ord, Generic) - -data ValueBindingFields a = ValueBindingFields - { valName :: Name Ident - , valBinders :: [Binder a] - , valGuarded :: Guarded a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Guarded a - = Unconditional SourceToken (Where a) - | Guarded (NonEmpty (GuardedExpr a)) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data GuardedExpr a = GuardedExpr - { grdBar :: SourceToken - , grdPatterns :: Separated (PatternGuard a) - , grdSep :: SourceToken - , grdWhere :: Where a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data PatternGuard a = PatternGuard - { patBinder :: Maybe (Binder a, SourceToken) - , patExpr :: Expr a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Foreign a - = ForeignValue (Labeled (Name Ident) (Type a)) - | ForeignData SourceToken (Labeled (Name (N.ProperName 'N.TypeName)) (Type a)) - | ForeignKind SourceToken (Name (N.ProperName 'N.TypeName)) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Role = Role - { roleTok :: SourceToken - , roleValue :: R.Role - } deriving (Show, Eq, Ord, Generic) - -data Expr a - = ExprHole a (Name Ident) - | ExprSection a SourceToken - | ExprIdent a (QualifiedName Ident) - | ExprConstructor a (QualifiedName (N.ProperName 'N.ConstructorName)) - | ExprBoolean a SourceToken Bool - | ExprChar a SourceToken Char - | ExprString a SourceToken PSString - | ExprNumber a SourceToken (Either Integer Double) - | ExprArray a (Delimited (Expr a)) - | ExprRecord a (Delimited (RecordLabeled (Expr a))) - | ExprParens a (Wrapped (Expr a)) - | ExprTyped a (Expr a) SourceToken (Type a) - | ExprInfix a (Expr a) (Wrapped (Expr a)) (Expr a) - | ExprOp a (Expr a) (QualifiedName (N.OpName 'N.ValueOpName)) (Expr a) - | ExprOpName a (QualifiedName (N.OpName 'N.ValueOpName)) - | ExprNegate a SourceToken (Expr a) - | ExprRecordAccessor a (RecordAccessor a) - | ExprRecordUpdate a (Expr a) (DelimitedNonEmpty (RecordUpdate a)) - | ExprApp a (Expr a) (Expr a) - | ExprVisibleTypeApp a (Expr a) SourceToken (Type a) - | ExprLambda a (Lambda a) - | ExprIf a (IfThenElse a) - | ExprCase a (CaseOf a) - | ExprLet a (LetIn a) - | ExprDo a (DoBlock a) - | ExprAdo a (AdoBlock a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data RecordLabeled a - = RecordPun (Name Ident) - | RecordField Label SourceToken a - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data RecordUpdate a - = RecordUpdateLeaf Label SourceToken (Expr a) - | RecordUpdateBranch Label (DelimitedNonEmpty (RecordUpdate a)) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data RecordAccessor a = RecordAccessor - { recExpr :: Expr a - , recDot :: SourceToken - , recPath :: Separated Label - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Lambda a = Lambda - { lmbSymbol :: SourceToken - , lmbBinders :: NonEmpty (Binder a) - , lmbArr :: SourceToken - , lmbBody :: Expr a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data IfThenElse a = IfThenElse - { iteIf :: SourceToken - , iteCond :: Expr a - , iteThen :: SourceToken - , iteTrue :: Expr a - , iteElse :: SourceToken - , iteFalse :: Expr a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data CaseOf a = CaseOf - { caseKeyword :: SourceToken - , caseHead :: Separated (Expr a) - , caseOf :: SourceToken - , caseBranches :: NonEmpty (Separated (Binder a), Guarded a) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data LetIn a = LetIn - { letKeyword :: SourceToken - , letBindings :: NonEmpty (LetBinding a) - , letIn :: SourceToken - , letBody :: Expr a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Where a = Where - { whereExpr :: Expr a - , whereBindings :: Maybe (SourceToken, NonEmpty (LetBinding a)) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data LetBinding a - = LetBindingSignature a (Labeled (Name Ident) (Type a)) - | LetBindingName a (ValueBindingFields a) - | LetBindingPattern a (Binder a) SourceToken (Where a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data DoBlock a = DoBlock - { doKeyword :: SourceToken - , doStatements :: NonEmpty (DoStatement a) - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data DoStatement a - = DoLet SourceToken (NonEmpty (LetBinding a)) - | DoDiscard (Expr a) - | DoBind (Binder a) SourceToken (Expr a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data AdoBlock a = AdoBlock - { adoKeyword :: SourceToken - , adoStatements :: [DoStatement a] - , adoIn :: SourceToken - , adoResult :: Expr a - } deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -data Binder a - = BinderWildcard a SourceToken - | BinderVar a (Name Ident) - | BinderNamed a (Name Ident) SourceToken (Binder a) - | BinderConstructor a (QualifiedName (N.ProperName 'N.ConstructorName)) [Binder a] - | BinderBoolean a SourceToken Bool - | BinderChar a SourceToken Char - | BinderString a SourceToken PSString - | BinderNumber a (Maybe SourceToken) SourceToken (Either Integer Double) - | BinderArray a (Delimited (Binder a)) - | BinderRecord a (Delimited (RecordLabeled (Binder a))) - | BinderParens a (Wrapped (Binder a)) - | BinderTyped a (Binder a) SourceToken (Type a) - | BinderOp a (Binder a) (QualifiedName (N.OpName 'N.ValueOpName)) (Binder a) - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) diff --git a/claude-help/original-compiler/src/Language/PureScript/CST/Utils.hs b/claude-help/original-compiler/src/Language/PureScript/CST/Utils.hs deleted file mode 100644 index 68dcf7d8..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CST/Utils.hs +++ /dev/null @@ -1,360 +0,0 @@ -module Language.PureScript.CST.Utils where - -import Prelude -import Protolude (headDef) - -import Control.Monad (unless) -import Data.Coerce (coerce) -import Data.Foldable (for_) -import Data.Functor (($>)) -import Data.List.NonEmpty qualified as NE -import Data.Set (Set) -import Data.Set qualified as Set -import Data.Text (Text) -import Data.Text qualified as Text -import Language.PureScript.CST.Errors (ParserErrorType(..)) -import Language.PureScript.CST.Monad (Parser, addFailure, parseFail, pushBack) -import Language.PureScript.CST.Positions (TokenRange, binderRange, importDeclRange, recordUpdateRange, typeRange) -import Language.PureScript.CST.Traversals.Type (everythingOnTypes) -import Language.PureScript.CST.Types -import Language.PureScript.Names qualified as N -import Language.PureScript.PSString (PSString, mkString) - --- | --- A newtype for a qualified proper name whose ProperNameType has not yet been determined. --- This is a workaround for Happy's limited support for polymorphism; it is used --- inside the parser to allow us to write just one parser for qualified proper names --- which can be used for all of the different ProperNameTypes --- (via a call to getQualifiedProperName). -newtype QualifiedProperName = - QualifiedProperName { getQualifiedProperName :: forall a. QualifiedName (N.ProperName a) } - -qualifiedProperName :: QualifiedName (N.ProperName a) -> QualifiedProperName -qualifiedProperName n = QualifiedProperName (N.coerceProperName <$> n) - --- | --- A newtype for a proper name whose ProperNameType has not yet been determined. --- This is a workaround for Happy's limited support for polymorphism; it is used --- inside the parser to allow us to write just one parser for proper names --- which can be used for all of the different ProperNameTypes --- (via a call to getProperName). -newtype ProperName = - ProperName { _getProperName :: forall a. Name (N.ProperName a) } - -properName :: Name (N.ProperName a) -> ProperName -properName n = ProperName (N.coerceProperName <$> n) - -getProperName :: forall a. ProperName -> Name (N.ProperName a) -getProperName pn = _getProperName pn -- eta expansion needed here due to simplified subsumption - --- | --- A newtype for a qualified operator name whose OpNameType has not yet been determined. --- This is a workaround for Happy's limited support for polymorphism; it is used --- inside the parser to allow us to write just one parser for qualified operator names --- which can be used for all of the different OpNameTypes --- (via a call to getQualifiedOpName). -newtype QualifiedOpName = - QualifiedOpName { getQualifiedOpName :: forall a. QualifiedName (N.OpName a) } - -qualifiedOpName :: QualifiedName (N.OpName a) -> QualifiedOpName -qualifiedOpName n = QualifiedOpName (N.coerceOpName <$> n) - --- | --- A newtype for a operator name whose OpNameType has not yet been determined. --- This is a workaround for Happy's limited support for polymorphism; it is used --- inside the parser to allow us to write just one parser for operator names --- which can be used for all of the different OpNameTypes --- (via a call to getOpName). -newtype OpName = - OpName { getOpName :: forall a. Name (N.OpName a) } - -opName :: Name (N.OpName a) -> OpName -opName n = OpName (N.coerceOpName <$> n) - -placeholder :: SourceToken -placeholder = SourceToken - { tokAnn = TokenAnn (SourceRange (SourcePos 0 0) (SourcePos 0 0)) [] [] - , tokValue = TokLowerName [] "" - } - -unexpectedName :: SourceToken -> Name Ident -unexpectedName tok = Name tok (Ident "") - -unexpectedQual :: SourceToken -> QualifiedName Ident -unexpectedQual tok = QualifiedName tok Nothing (Ident "") - -unexpectedLabel :: SourceToken -> Label -unexpectedLabel tok = Label tok "" - -unexpectedExpr :: Monoid a => [SourceToken] -> Expr a -unexpectedExpr toks = - ExprIdent mempty (unexpectedQual (headDef placeholder toks)) - -unexpectedBinder :: Monoid a => [SourceToken] -> Binder a -unexpectedBinder toks = - BinderVar mempty (unexpectedName (headDef placeholder toks)) - -unexpectedRecordUpdate :: Monoid a => [SourceToken] -> RecordUpdate a -unexpectedRecordUpdate toks = - RecordUpdateLeaf (unexpectedLabel (headDef placeholder toks)) (headDef placeholder toks) (unexpectedExpr toks) - -unexpectedRecordLabeled :: [SourceToken] -> RecordLabeled a -unexpectedRecordLabeled toks = - RecordPun (unexpectedName (headDef placeholder toks)) - -rangeToks :: TokenRange -> [SourceToken] -rangeToks (a, b) = [a, b] - -unexpectedToks :: (a -> TokenRange) -> ([SourceToken] -> b) -> ParserErrorType -> (a -> Parser b) -unexpectedToks toRange toCst err old = do - let toks = rangeToks $ toRange old - addFailure toks err - pure $ toCst toks - -separated :: [(SourceToken, a)] -> Separated a -separated = go [] - where - go accum [(_, a)] = Separated a accum - go accum (x : xs) = go (x : accum) xs - go _ [] = internalError "Separated should not be empty" - -internalError :: String -> a -internalError = error . ("Internal parser error: " <>) - -toModuleName :: SourceToken -> [Text] -> Parser (Maybe N.ModuleName) -toModuleName _ [] = pure Nothing -toModuleName tok ns = do - unless (all isValidModuleNamespace ns) $ addFailure [tok] ErrModuleName - pure . Just . N.ModuleName $ Text.intercalate "." ns - -upperToModuleName :: SourceToken -> Parser (Name N.ModuleName) -upperToModuleName tok = case tokValue tok of - TokUpperName q a -> do - let ns = q <> [a] - unless (all isValidModuleNamespace ns) $ addFailure [tok] ErrModuleName - pure . Name tok . N.ModuleName $ Text.intercalate "." ns - _ -> internalError $ "Invalid upper name: " <> show tok - -toQualifiedName :: (Text -> a) -> SourceToken -> Parser (QualifiedName a) -toQualifiedName k tok = case tokValue tok of - TokLowerName q a - | not (Set.member a reservedNames) -> flip (QualifiedName tok) (k a) <$> toModuleName tok q - | otherwise -> addFailure [tok] ErrKeywordVar $> QualifiedName tok Nothing (k "") - TokUpperName q a -> flip (QualifiedName tok) (k a) <$> toModuleName tok q - TokSymbolName q a -> flip (QualifiedName tok) (k a) <$> toModuleName tok q - TokOperator q a -> flip (QualifiedName tok) (k a) <$> toModuleName tok q - _ -> internalError $ "Invalid qualified name: " <> show tok - -toName :: (Text -> a) -> SourceToken -> Parser (Name a) -toName k tok = case tokValue tok of - TokLowerName [] a - | not (Set.member a reservedNames) -> pure $ Name tok (k a) - | otherwise -> addFailure [tok] ErrKeywordVar $> Name tok (k "") - TokString _ _ -> parseFail tok ErrQuotedPun - TokRawString _ -> parseFail tok ErrQuotedPun - TokUpperName [] a -> pure $ Name tok (k a) - TokSymbolName [] a -> pure $ Name tok (k a) - TokOperator [] a -> pure $ Name tok (k a) - TokHole a -> pure $ Name tok (k a) - _ -> internalError $ "Invalid name: " <> show tok - -toLabel :: SourceToken -> Label -toLabel tok = case tokValue tok of - TokLowerName [] a -> Label tok $ mkString a - TokString _ a -> Label tok a - TokRawString a -> Label tok $ mkString a - TokForall ASCII -> Label tok $ mkString "forall" - _ -> internalError $ "Invalid label: " <> show tok - -toString :: SourceToken -> (SourceToken, PSString) -toString tok = case tokValue tok of - TokString _ a -> (tok, a) - TokRawString a -> (tok, mkString a) - _ -> internalError $ "Invalid string literal: " <> show tok - -toChar :: SourceToken -> (SourceToken, Char) -toChar tok = case tokValue tok of - TokChar _ a -> (tok, a) - _ -> internalError $ "Invalid char literal: " <> show tok - -toNumber :: SourceToken -> (SourceToken, Either Integer Double) -toNumber tok = case tokValue tok of - TokInt _ a -> (tok, Left a) - TokNumber _ a -> (tok, Right a) - _ -> internalError $ "Invalid number literal: " <> show tok - -toInt :: SourceToken -> (SourceToken, Integer) -toInt tok = case tokValue tok of - TokInt _ a -> (tok, a) - _ -> internalError $ "Invalid integer literal: " <> show tok - -toBoolean :: SourceToken -> (SourceToken, Bool) -toBoolean tok = case tokValue tok of - TokLowerName [] "true" -> (tok, True) - TokLowerName [] "false" -> (tok, False) - _ -> internalError $ "Invalid boolean literal: " <> show tok - -toConstraint :: forall a. Monoid a => Type a -> Parser (Constraint a) -toConstraint = convertParens - where - convertParens :: Type a -> Parser (Constraint a) - convertParens = \case - TypeParens a (Wrapped b c d) -> do - c' <- convertParens c - pure $ ConstraintParens a (Wrapped b c' d) - ty -> convert mempty [] ty - - convert :: a -> [Type a] -> Type a -> Parser (Constraint a) - convert ann acc = \case - TypeApp a lhs rhs -> convert (a <> ann) (rhs : acc) lhs - TypeConstructor a name -> do - for_ acc checkNoForalls - pure $ Constraint (a <> ann) (coerce name) acc - ty -> do - let (tok1, tok2) = typeRange ty - addFailure [tok1, tok2] ErrTypeInConstraint - pure $ Constraint mempty (QualifiedName tok1 Nothing (N.ProperName " Bool -isConstrained = everythingOnTypes (||) $ \case - TypeConstrained{} -> True - _ -> False - -toBinderConstructor :: Monoid a => NE.NonEmpty (Binder a) -> Parser (Binder a) -toBinderConstructor = \case - BinderConstructor a name [] NE.:| bs -> - pure $ BinderConstructor a name bs - a NE.:| [] -> pure a - a NE.:| _ -> unexpectedToks binderRange unexpectedBinder ErrExprInBinder a - -toRecordFields - :: Monoid a - => Separated (Either (RecordLabeled (Expr a)) (RecordUpdate a)) - -> Parser (Either (Separated (RecordLabeled (Expr a))) (Separated (RecordUpdate a))) -toRecordFields = \case - Separated (Left a) as -> - Left . Separated a <$> traverse (traverse unLeft) as - Separated (Right a) as -> - Right . Separated a <$> traverse (traverse unRight) as - where - unLeft (Left tok) = pure tok - unLeft (Right tok) = - unexpectedToks recordUpdateRange unexpectedRecordLabeled ErrRecordUpdateInCtr tok - - unRight (Right tok) = pure tok - unRight (Left (RecordPun (Name tok _))) = do - addFailure [tok] ErrRecordPunInUpdate - pure $ unexpectedRecordUpdate [tok] - unRight (Left (RecordField _ tok _)) = do - addFailure [tok] ErrRecordCtrInUpdate - pure $ unexpectedRecordUpdate [tok] - -checkFundeps :: ClassHead a -> Parser () -checkFundeps (ClassHead _ _ _ _ Nothing) = pure () -checkFundeps (ClassHead _ _ _ vars (Just (_, fundeps))) = do - let - k (TypeVarKinded (Wrapped _ (Labeled (_, a) _ _) _)) = getIdent $ nameValue a - k (TypeVarName (_, a)) = getIdent $ nameValue a - names = k <$> vars - check a - | getIdent (nameValue a) `elem` names = pure () - | otherwise = addFailure [nameTok a] ErrUnknownFundep - for_ fundeps $ \case - FundepDetermined _ bs -> for_ bs check - FundepDetermines as _ bs -> do - for_ as check - for_ bs check - -data TmpModuleDecl a - = TmpImport (ImportDecl a) - | TmpChain (Separated (Declaration a)) - deriving (Show) - -toModuleDecls :: Monoid a => [TmpModuleDecl a] -> Parser ([ImportDecl a], [Declaration a]) -toModuleDecls = goImport [] - where - goImport acc (TmpImport x : xs) = goImport (x : acc) xs - goImport acc xs = (reverse acc,) <$> goDecl [] xs - - goDecl acc [] = pure $ reverse acc - goDecl acc (TmpChain (Separated x []) : xs) = goDecl (x : acc) xs - goDecl acc (TmpChain (Separated (DeclInstanceChain a (Separated h t)) t') : xs) = do - (a', instances) <- goChain (getName h) a [] t' - goDecl (DeclInstanceChain a' (Separated h (t <> instances)) : acc) xs - goDecl acc (TmpChain (Separated _ t) : xs) = do - for_ t $ \(tok, _) -> addFailure [tok] ErrElseInDecl - goDecl acc xs - goDecl acc (TmpImport imp : xs) = do - unexpectedToks importDeclRange (const ()) ErrImportInDecl imp - goDecl acc xs - - goChain _ ann acc [] = pure (ann, reverse acc) - goChain name ann acc ((tok, DeclInstanceChain a (Separated h t)) : xs) - | eqName (getName h) name = goChain name (ann <> a) (reverse ((tok, h) : t) <> acc) xs - | otherwise = do - addFailure [qualTok $ getName h] ErrInstanceNameMismatch - goChain name ann acc xs - goChain name ann acc ((tok, _) : xs) = do - addFailure [tok] ErrElseInDecl - goChain name ann acc xs - - getName = instClass . instHead - eqName (QualifiedName _ a b) (QualifiedName _ c d) = a == c && b == d - -checkNoWildcards :: Type a -> Parser () -checkNoWildcards ty = do - let - k = \case - TypeWildcard _ a -> [addFailure [a] ErrWildcardInType] - TypeHole _ a -> [addFailure [nameTok a] ErrHoleInType] - _ -> [] - sequence_ $ everythingOnTypes (<>) k ty - -checkNoForalls :: Type a -> Parser () -checkNoForalls ty = do - let - k = \case - TypeForall _ a _ _ _ -> [addFailure [a] ErrToken] - _ -> [] - sequence_ $ everythingOnTypes (<>) k ty - -revert :: Parser a -> SourceToken -> Parser a -revert p lk = pushBack lk *> p - -reservedNames :: Set Text -reservedNames = Set.fromList - [ "ado" - , "case" - , "class" - , "data" - , "derive" - , "do" - , "else" - , "false" - , "forall" - , "foreign" - , "import" - , "if" - , "in" - , "infix" - , "infixl" - , "infixr" - , "instance" - , "let" - , "module" - , "newtype" - , "of" - , "true" - , "type" - , "where" - ] - -isValidModuleNamespace :: Text -> Bool -isValidModuleNamespace = Text.null . snd . Text.span (\c -> c /= '_' && c /= '\'') - --- | This is to keep the @Parser.y@ file ASCII, otherwise @happy@ will break --- in non-unicode locales. --- --- Related GHC issue: https://gitlab.haskell.org/ghc/ghc/issues/8167 -isLeftFatArrow :: Text -> Bool -isLeftFatArrow str = str == "<=" || str == "⇐" diff --git a/claude-help/original-compiler/src/Language/PureScript/CodeGen.hs b/claude-help/original-compiler/src/Language/PureScript/CodeGen.hs deleted file mode 100644 index 02edf9ec..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CodeGen.hs +++ /dev/null @@ -1,8 +0,0 @@ --- | --- A collection of modules related to code generation: --- --- [@Language.PureScript.CodeGen.JS@] Code generator for JavaScript --- -module Language.PureScript.CodeGen (module C) where - -import Language.PureScript.CodeGen.JS as C diff --git a/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS.hs b/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS.hs deleted file mode 100644 index 890cc1cd..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS.hs +++ /dev/null @@ -1,519 +0,0 @@ --- | This module generates code in the core imperative representation from --- elaborated PureScript code. -module Language.PureScript.CodeGen.JS - ( module AST - , module Common - , moduleToJs - ) where - -import Prelude -import Protolude (ordNub, headDef) - -import Control.Monad (forM, replicateM, void) -import Control.Monad.Except (MonadError, throwError) -import Control.Monad.Reader (MonadReader, asks) -import Control.Monad.Supply.Class (MonadSupply, freshName) -import Control.Monad.Writer (MonadWriter, runWriterT, writer) - -import Data.Bifunctor (first) -import Data.List ((\\), intersect) -import Data.List.NonEmpty qualified as NEL (nonEmpty) -import Data.Foldable qualified as F -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Maybe (fromMaybe, mapMaybe, maybeToList) -import Data.Monoid (Any(..)) -import Data.String (fromString) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.AST.SourcePos (SourceSpan, displayStartEndPos) -import Language.PureScript.CodeGen.JS.Common as Common -import Language.PureScript.CoreImp.AST (AST, InitializerEffects(..), everywhere, everywhereTopDownM, withSourceSpan) -import Language.PureScript.CoreImp.AST qualified as AST -import Language.PureScript.CoreImp.Module qualified as AST -import Language.PureScript.CoreImp.Optimizer (optimize) -import Language.PureScript.CoreFn (Ann, Bind(..), Binder(..), CaseAlternative(..), ConstructorType(..), Expr(..), Guard, Literal(..), Meta(..), Module(..), extractAnn, extractBinderAnn, modifyAnn, removeComments) -import Language.PureScript.CoreFn.Laziness (applyLazinessTransform) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (ErrorMessageHint(..), SimpleErrorMessage(..), - MultipleErrors(..), rethrow, errorMessage, - errorMessage', rethrowWithPosition, addHint) -import Language.PureScript.Names (Ident(..), ModuleName, ProperName(..), Qualified(..), QualifiedBy(..), runIdent, runModuleName, showIdent, showQualified) -import Language.PureScript.Options (CodegenTarget(..), Options(..)) -import Language.PureScript.PSString (PSString, mkString) -import Language.PureScript.Traversals (sndM) -import Language.PureScript.Constants.Prim qualified as C - -import System.FilePath.Posix (()) - --- | Generate code in the simplified JavaScript intermediate representation for all declarations in a --- module. -moduleToJs - :: forall m - . (MonadReader Options m, MonadSupply m, MonadError MultipleErrors m) - => Module Ann - -> Maybe PSString - -> m AST.Module -moduleToJs (Module _ coms mn _ imps exps reExps foreigns decls) foreignInclude = - rethrow (addHint (ErrorInModule mn)) $ do - let usedNames = concatMap getNames decls - let imps' = ordNub $ map snd imps - let mnLookup = renameImports usedNames imps' - (jsDecls, Any needRuntimeLazy) <- runWriterT $ mapM (moduleBindToJs mn) decls - optimized <- fmap (fmap (fmap annotatePure)) . optimize (map identToJs exps) $ if needRuntimeLazy then [runtimeLazy] : jsDecls else jsDecls - F.traverse_ (F.traverse_ checkIntegers) optimized - comments <- not <$> asks optionsNoComments - let header = if comments then coms else [] - let foreign' = maybe [] (pure . AST.Import FFINamespace) $ if null foreigns then Nothing else foreignInclude - let moduleBody = concat optimized - let (S.union (M.keysSet reExps) -> usedModuleNames, renamedModuleBody) = traverse (replaceModuleAccessors mnLookup) moduleBody - let jsImports - = map (importToJs mnLookup) - . filter (flip S.member usedModuleNames) - $ (\\ (mn : C.primModules)) imps' - let foreignExps = exps `intersect` foreigns - let standardExps = exps \\ foreignExps - let reExps' = M.toList (M.withoutKeys reExps (S.fromList C.primModules)) - let jsExports - = (maybeToList . exportsToJs foreignInclude $ foreignExps) - ++ (maybeToList . exportsToJs Nothing $ standardExps) - ++ mapMaybe reExportsToJs reExps' - return $ AST.Module header (foreign' ++ jsImports) renamedModuleBody jsExports - - where - -- Adds purity annotations to top-level values for bundlers. - -- The semantics here derive from treating top-level module evaluation as pure, which lets - -- us remove any unreferenced top-level declarations. To achieve this, we wrap any non-trivial - -- top-level values in an IIFE marked with a pure annotation. - annotatePure :: AST -> AST - annotatePure = annotateOrWrap - where - annotateOrWrap = liftA2 fromMaybe pureIife maybePure - - -- If the JS is potentially effectful (in the eyes of a bundler that - -- doesn't know about PureScript), return Nothing. Otherwise, return Just - -- the JS with any needed pure annotations added, and, in the case of a - -- variable declaration, an IIFE to be annotated. - maybePure :: AST -> Maybe AST - maybePure = maybePureGen False - - -- Like maybePure, but doesn't add a pure annotation to App. This exists - -- to prevent from doubling up on annotation comments on curried - -- applications; from experimentation, it turns out that a comment on the - -- outermost App is sufficient for the entire curried chain to be - -- considered effect-free. - maybePure' :: AST -> Maybe AST - maybePure' = maybePureGen True - - maybePureGen alreadyAnnotated = \case - AST.VariableIntroduction ss name j -> Just (AST.VariableIntroduction ss name (fmap annotateOrWrap <$> j)) - AST.App ss f args -> (if alreadyAnnotated then AST.App else pureApp) ss <$> maybePure' f <*> traverse maybePure args - AST.ArrayLiteral ss jss -> AST.ArrayLiteral ss <$> traverse maybePure jss - AST.ObjectLiteral ss props -> AST.ObjectLiteral ss <$> traverse (traverse maybePure) props - AST.Comment c js -> AST.Comment c <$> maybePure js - - js@(AST.Indexer _ _ (AST.Var _ FFINamespace)) -> Just js - - js@AST.NumericLiteral{} -> Just js - js@AST.StringLiteral{} -> Just js - js@AST.BooleanLiteral{} -> Just js - js@AST.Function{} -> Just js - js@AST.Var{} -> Just js - js@AST.ModuleAccessor{} -> Just js - - _ -> Nothing - - pureIife :: AST -> AST - pureIife val = pureApp Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing [AST.Return Nothing val])) [] - - pureApp :: Maybe SourceSpan -> AST -> [AST] -> AST - pureApp ss f = AST.Comment AST.PureAnnotation . AST.App ss f - - -- Extracts all declaration names from a binding group. - getNames :: Bind Ann -> [Ident] - getNames (NonRec _ ident _) = [ident] - getNames (Rec vals) = map (snd . fst) vals - - -- Creates alternative names for each module to ensure they don't collide - -- with declaration names. - renameImports :: [Ident] -> [ModuleName] -> M.Map ModuleName Text - renameImports = go M.empty - where - go :: M.Map ModuleName Text -> [Ident] -> [ModuleName] -> M.Map ModuleName Text - go acc used (mn' : mns') = - let mnj = moduleNameToJs mn' - in if mn' /= mn && Ident mnj `elem` used - then let newName = freshModuleName 1 mnj used - in go (M.insert mn' newName acc) (Ident newName : used) mns' - else go (M.insert mn' mnj acc) used mns' - go acc _ [] = acc - - freshModuleName :: Integer -> Text -> [Ident] -> Text - freshModuleName i mn' used = - let newName = mn' <> "_" <> T.pack (show i) - in if Ident newName `elem` used - then freshModuleName (i + 1) mn' used - else newName - - -- Generates JavaScript code for a module import, binding the required module - -- to the alternative - importToJs :: M.Map ModuleName Text -> ModuleName -> AST.Import - importToJs mnLookup mn' = - let mnSafe = fromMaybe (internalError "Missing value in mnLookup") $ M.lookup mn' mnLookup - in AST.Import mnSafe (moduleImportPath mn') - - -- Generates JavaScript code for exporting at least one identifier, - -- eventually from another module. - exportsToJs :: Maybe PSString -> [Ident] -> Maybe AST.Export - exportsToJs from = fmap (flip AST.Export from) . NEL.nonEmpty . fmap runIdent - - -- Generates JavaScript code for re-exporting at least one identifier from - -- from another module. - reExportsToJs :: (ModuleName, [Ident]) -> Maybe AST.Export - reExportsToJs = uncurry exportsToJs . first (Just . moduleImportPath) - - moduleImportPath :: ModuleName -> PSString - moduleImportPath mn' = fromString (".." T.unpack (runModuleName mn') "index.js") - - -- Replaces the `ModuleAccessor`s in the AST with `Indexer`s, ensuring that - -- the generated code refers to the collision-avoiding renamed module - -- imports. Also returns set of used module names. - replaceModuleAccessors :: M.Map ModuleName Text -> AST -> (S.Set ModuleName, AST) - replaceModuleAccessors mnLookup = everywhereTopDownM $ \case - AST.ModuleAccessor _ mn' name -> - let mnSafe = fromMaybe (internalError "Missing value in mnLookup") $ M.lookup mn' mnLookup - in (S.singleton mn', accessorString name $ AST.Var Nothing mnSafe) - other -> pure other - - -- Check that all integers fall within the valid int range for JavaScript. - checkIntegers :: AST -> m () - checkIntegers = void . everywhereTopDownM go - where - go :: AST -> m AST - go (AST.Unary _ AST.Negate (AST.NumericLiteral ss (Left i))) = - -- Move the negation inside the literal; since this is a top-down - -- traversal doing this replacement will stop the next case from raising - -- the error when attempting to use -2147483648, as if left unrewritten - -- the value is `Unary Negate (NumericLiteral (Left 2147483648))`, and - -- 2147483648 is larger than the maximum allowed int. - return $ AST.NumericLiteral ss (Left (-i)) - go js@(AST.NumericLiteral ss (Left i)) = - let minInt = -2147483648 - maxInt = 2147483647 - in if i < minInt || i > maxInt - then throwError . maybe errorMessage errorMessage' ss $ IntOutOfRange i "JavaScript" minInt maxInt - else return js - go other = return other - - runtimeLazy :: AST - runtimeLazy = - AST.VariableIntroduction Nothing "$runtime_lazy" . Just . (UnknownEffects, ) . AST.Function Nothing Nothing ["name", "moduleName", "init"] . AST.Block Nothing $ - [ AST.VariableIntroduction Nothing "state" . Just . (UnknownEffects, ) . AST.NumericLiteral Nothing $ Left 0 - , AST.VariableIntroduction Nothing "val" Nothing - , AST.Return Nothing . AST.Function Nothing Nothing ["lineNumber"] . AST.Block Nothing $ - [ AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing "state") (AST.NumericLiteral Nothing (Left 2))) (AST.Return Nothing $ AST.Var Nothing "val") Nothing - , AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing "state") (AST.NumericLiteral Nothing (Left 1))) (AST.Throw Nothing $ AST.Unary Nothing AST.New (AST.App Nothing (AST.Var Nothing "ReferenceError") [foldl1 (AST.Binary Nothing AST.Add) - [ AST.Var Nothing "name" - , AST.StringLiteral Nothing " was needed before it finished initializing (module " - , AST.Var Nothing "moduleName" - , AST.StringLiteral Nothing ", line " - , AST.Var Nothing "lineNumber" - , AST.StringLiteral Nothing ")" - ], AST.Var Nothing "moduleName", AST.Var Nothing "lineNumber"])) Nothing - , AST.Assignment Nothing (AST.Var Nothing "state") . AST.NumericLiteral Nothing $ Left 1 - , AST.Assignment Nothing (AST.Var Nothing "val") $ AST.App Nothing (AST.Var Nothing "init") [] - , AST.Assignment Nothing (AST.Var Nothing "state") . AST.NumericLiteral Nothing $ Left 2 - , AST.Return Nothing $ AST.Var Nothing "val" - ] - ] - - -moduleBindToJs - :: forall m - . (MonadReader Options m, MonadSupply m, MonadWriter Any m, MonadError MultipleErrors m) - => ModuleName - -> Bind Ann - -> m [AST] -moduleBindToJs mn = bindToJs - where - -- Generate code in the simplified JavaScript intermediate representation for a declaration - bindToJs :: Bind Ann -> m [AST] - bindToJs (NonRec (_, _, Just IsTypeClassConstructor) _ _) = pure [] - -- Unlike other newtype constructors, type class constructors are only - -- ever applied; it's not possible to use them as values. So it's safe to - -- erase them. - bindToJs (NonRec ann ident val) = return <$> nonRecToJS ann ident val - bindToJs (Rec vals) = writer (applyLazinessTransform mn vals) >>= traverse (uncurry . uncurry $ nonRecToJS) - - -- Generate code in the simplified JavaScript intermediate representation for a single non-recursive - -- declaration. - -- - -- The main purpose of this function is to handle code generation for comments. - nonRecToJS :: Ann -> Ident -> Expr Ann -> m AST - nonRecToJS a i e@(extractAnn -> (_, com, _)) | not (null com) = do - withoutComment <- asks optionsNoComments - if withoutComment - then nonRecToJS a i (modifyAnn removeComments e) - else AST.Comment (AST.SourceComments com) <$> nonRecToJS a i (modifyAnn removeComments e) - nonRecToJS (ss, _, _) ident val = do - js <- valueToJs val - withPos ss $ AST.VariableIntroduction Nothing (identToJs ident) (Just (guessEffects val, js)) - - guessEffects :: Expr Ann -> AST.InitializerEffects - guessEffects = \case - Var _ (Qualified (BySourcePos _) _) -> NoEffects - App (_, _, Just IsSyntheticApp) _ _ -> NoEffects - _ -> UnknownEffects - - withPos :: SourceSpan -> AST -> m AST - withPos ss js = do - withSM <- asks (elem JSSourceMap . optionsCodegenTargets) - return $ if withSM - then withSourceSpan ss js - else js - - -- Generate code in the simplified JavaScript intermediate representation for a variable based on a - -- PureScript identifier. - var :: Ident -> AST - var = AST.Var Nothing . identToJs - - -- Generate code in the simplified JavaScript intermediate representation for a value or expression. - valueToJs :: Expr Ann -> m AST - valueToJs e = - let (ss, _, _) = extractAnn e in - withPos ss =<< valueToJs' e - - valueToJs' :: Expr Ann -> m AST - valueToJs' (Literal (pos, _, _) l) = - rethrowWithPosition pos $ literalToValueJS pos l - valueToJs' (Var (_, _, Just (IsConstructor _ [])) name) = - return $ accessorString "value" $ qualifiedToJS id name - valueToJs' (Var (_, _, Just (IsConstructor _ _)) name) = - return $ accessorString "create" $ qualifiedToJS id name - valueToJs' (Accessor _ prop val) = - accessorString prop <$> valueToJs val - valueToJs' (ObjectUpdate (pos, _, _) o copy ps) = do - obj <- valueToJs o - sts <- mapM (sndM valueToJs) ps - case copy of - Nothing -> extendObj obj sts - Just names -> pure $ AST.ObjectLiteral (Just pos) (map f names ++ sts) - where f name = (name, accessorString name obj) - valueToJs' (Abs _ arg val) = do - ret <- valueToJs val - let jsArg = case arg of - UnusedIdent -> [] - _ -> [identToJs arg] - return $ AST.Function Nothing Nothing jsArg (AST.Block Nothing [AST.Return Nothing ret]) - valueToJs' e@App{} = do - let (f, args) = unApp e [] - args' <- mapM valueToJs args - case f of - Var (_, _, Just IsNewtype) _ -> - return (headDef (internalError "Newtype constructor without constructor name") args') - Var (_, _, Just (IsConstructor _ fields)) name | length args == length fields -> - return $ AST.Unary Nothing AST.New $ AST.App Nothing (qualifiedToJS id name) args' - _ -> flip (foldl (\fn a -> AST.App Nothing fn [a])) args' <$> valueToJs f - where - unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann]) - unApp (App _ val arg) args = unApp val (arg : args) - unApp other args = (other, args) - valueToJs' (Var (_, _, Just IsForeign) qi@(Qualified (ByModuleName mn') ident)) = - return $ if mn' == mn - then foreignIdent ident - else varToJs qi - valueToJs' (Var (_, _, Just IsForeign) ident) = - internalError $ "Encountered an unqualified reference to a foreign ident " ++ T.unpack (showQualified showIdent ident) - valueToJs' (Var _ ident) = return $ varToJs ident - valueToJs' (Case (ss, _, _) values binders) = do - vals <- mapM valueToJs values - bindersToJs ss binders vals - valueToJs' (Let _ ds val) = do - ds' <- concat <$> mapM bindToJs ds - ret <- valueToJs val - return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (ds' ++ [AST.Return Nothing ret]))) [] - valueToJs' (Constructor (_, _, Just IsNewtype) _ ctor _) = - return $ AST.VariableIntroduction Nothing (properToJs ctor) (Just . (UnknownEffects, ) $ - AST.ObjectLiteral Nothing [("create", - AST.Function Nothing Nothing ["value"] - (AST.Block Nothing [AST.Return Nothing $ AST.Var Nothing "value"]))]) - valueToJs' (Constructor _ _ ctor []) = - return $ iife (properToJs ctor) [ AST.Function Nothing (Just (properToJs ctor)) [] (AST.Block Nothing []) - , AST.Assignment Nothing (accessorString "value" (AST.Var Nothing (properToJs ctor))) - (AST.Unary Nothing AST.New $ AST.App Nothing (AST.Var Nothing (properToJs ctor)) []) ] - valueToJs' (Constructor _ _ ctor fields) = - let constructor = - let body = [ AST.Assignment Nothing ((accessorString $ mkString $ identToJs f) (AST.Var Nothing "this")) (var f) | f <- fields ] - in AST.Function Nothing (Just (properToJs ctor)) (identToJs `map` fields) (AST.Block Nothing body) - createFn = - let body = AST.Unary Nothing AST.New $ AST.App Nothing (AST.Var Nothing (properToJs ctor)) (var `map` fields) - in foldr (\f inner -> AST.Function Nothing Nothing [identToJs f] (AST.Block Nothing [AST.Return Nothing inner])) body fields - in return $ iife (properToJs ctor) [ constructor - , AST.Assignment Nothing (accessorString "create" (AST.Var Nothing (properToJs ctor))) createFn - ] - - iife :: Text -> [AST] -> AST - iife v exprs = AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing $ exprs ++ [AST.Return Nothing $ AST.Var Nothing v])) [] - - literalToValueJS :: SourceSpan -> Literal (Expr Ann) -> m AST - literalToValueJS ss (NumericLiteral (Left i)) = return $ AST.NumericLiteral (Just ss) (Left i) - literalToValueJS ss (NumericLiteral (Right n)) = return $ AST.NumericLiteral (Just ss) (Right n) - literalToValueJS ss (StringLiteral s) = return $ AST.StringLiteral (Just ss) s - literalToValueJS ss (CharLiteral c) = return $ AST.StringLiteral (Just ss) (fromString [c]) - literalToValueJS ss (BooleanLiteral b) = return $ AST.BooleanLiteral (Just ss) b - literalToValueJS ss (ArrayLiteral xs) = AST.ArrayLiteral (Just ss) <$> mapM valueToJs xs - literalToValueJS ss (ObjectLiteral ps) = AST.ObjectLiteral (Just ss) <$> mapM (sndM valueToJs) ps - - -- Shallow copy an object. - extendObj :: AST -> [(PSString, AST)] -> m AST - extendObj obj sts = do - newObj <- freshName - key <- freshName - evaluatedObj <- freshName - let - jsKey = AST.Var Nothing key - jsNewObj = AST.Var Nothing newObj - jsEvaluatedObj = AST.Var Nothing evaluatedObj - block = AST.Block Nothing (evaluate:objAssign:copy:extend ++ [AST.Return Nothing jsNewObj]) - evaluate = AST.VariableIntroduction Nothing evaluatedObj (Just (UnknownEffects, obj)) - objAssign = AST.VariableIntroduction Nothing newObj (Just (NoEffects, AST.ObjectLiteral Nothing [])) - copy = AST.ForIn Nothing key jsEvaluatedObj $ AST.Block Nothing [AST.IfElse Nothing cond assign Nothing] - cond = AST.App Nothing (accessorString "call" (accessorString "hasOwnProperty" (AST.ObjectLiteral Nothing []))) [jsEvaluatedObj, jsKey] - assign = AST.Block Nothing [AST.Assignment Nothing (AST.Indexer Nothing jsKey jsNewObj) (AST.Indexer Nothing jsKey jsEvaluatedObj)] - stToAssign (s, js) = AST.Assignment Nothing (accessorString s jsNewObj) js - extend = map stToAssign sts - return $ AST.App Nothing (AST.Function Nothing Nothing [] block) [] - - -- Generate code in the simplified JavaScript intermediate representation for a reference to a - -- variable. - varToJs :: Qualified Ident -> AST - varToJs (Qualified (BySourcePos _) ident) = var ident - varToJs qual = qualifiedToJS id qual - - -- Generate code in the simplified JavaScript intermediate representation for a reference to a - -- variable that may have a qualified name. - qualifiedToJS :: (a -> Ident) -> Qualified a -> AST - qualifiedToJS f (Qualified (ByModuleName C.M_Prim) a) = AST.Var Nothing . runIdent $ f a - qualifiedToJS f (Qualified (ByModuleName mn') a) | mn /= mn' = AST.ModuleAccessor Nothing mn' . mkString . T.concatMap identCharToText . runIdent $ f a - qualifiedToJS f (Qualified _ a) = AST.Var Nothing $ identToJs (f a) - - foreignIdent :: Ident -> AST - foreignIdent ident = accessorString (mkString $ runIdent ident) (AST.Var Nothing FFINamespace) - - -- Generate code in the simplified JavaScript intermediate representation for pattern match binders - -- and guards. - bindersToJs :: SourceSpan -> [CaseAlternative Ann] -> [AST] -> m AST - bindersToJs ss binders vals = do - valNames <- replicateM (length vals) freshName - let assignments = zipWith (AST.VariableIntroduction Nothing) valNames (map (Just . (UnknownEffects, )) vals) - jss <- forM binders $ \(CaseAlternative bs result) -> do - ret <- guardsToJs result - go valNames ret bs - return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (assignments ++ concat jss ++ [AST.Throw Nothing $ failedPatternError valNames]))) - [] - where - go :: [Text] -> [AST] -> [Binder Ann] -> m [AST] - go _ done [] = return done - go (v:vs) done' (b:bs) = do - done'' <- go vs done' bs - binderToJs v done'' b - go _ _ _ = internalError "Invalid arguments to bindersToJs" - - failedPatternError :: [Text] -> AST - failedPatternError names = AST.Unary Nothing AST.New $ AST.App Nothing (AST.Var Nothing "Error") [AST.Binary Nothing AST.Add (AST.StringLiteral Nothing $ mkString failedPatternMessage) (AST.ArrayLiteral Nothing $ zipWith valueError names vals)] - - failedPatternMessage :: Text - failedPatternMessage = "Failed pattern match at " <> runModuleName mn <> " " <> displayStartEndPos ss <> ": " - - valueError :: Text -> AST -> AST - valueError _ l@(AST.NumericLiteral _ _) = l - valueError _ l@(AST.StringLiteral _ _) = l - valueError _ l@(AST.BooleanLiteral _ _) = l - valueError s _ = accessorString "name" . accessorString "constructor" $ AST.Var Nothing s - - guardsToJs :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [AST] - guardsToJs (Left gs) = traverse genGuard gs where - genGuard (cond, val) = do - cond' <- valueToJs cond - val' <- valueToJs val - return - (AST.IfElse Nothing cond' - (AST.Block Nothing [AST.Return Nothing val']) Nothing) - - guardsToJs (Right v) = return . AST.Return Nothing <$> valueToJs v - - binderToJs :: Text -> [AST] -> Binder Ann -> m [AST] - binderToJs s done binder = - let (ss, _, _) = extractBinderAnn binder in - traverse (withPos ss) =<< binderToJs' s done binder - - -- Generate code in the simplified JavaScript intermediate representation for a pattern match - -- binder. - binderToJs' :: Text -> [AST] -> Binder Ann -> m [AST] - binderToJs' _ done NullBinder{} = return done - binderToJs' varName done (LiteralBinder _ l) = - literalToBinderJS varName done l - binderToJs' varName done (VarBinder _ ident) = - return (AST.VariableIntroduction Nothing (identToJs ident) (Just (NoEffects, AST.Var Nothing varName)) : done) - binderToJs' varName done (ConstructorBinder (_, _, Just IsNewtype) _ _ [b]) = - binderToJs varName done b - binderToJs' varName done (ConstructorBinder (_, _, Just (IsConstructor ctorType fields)) _ ctor bs) = do - js <- go (zip fields bs) done - return $ case ctorType of - ProductType -> js - SumType -> - [AST.IfElse Nothing (AST.InstanceOf Nothing (AST.Var Nothing varName) (qualifiedToJS (Ident . runProperName) ctor)) - (AST.Block Nothing js) - Nothing] - where - go :: [(Ident, Binder Ann)] -> [AST] -> m [AST] - go [] done' = return done' - go ((field, binder) : remain) done' = do - argVar <- freshName - done'' <- go remain done' - js <- binderToJs argVar done'' binder - return (AST.VariableIntroduction Nothing argVar (Just (UnknownEffects, accessorString (mkString $ identToJs field) $ AST.Var Nothing varName)) : js) - binderToJs' _ _ ConstructorBinder{} = - internalError "binderToJs: Invalid ConstructorBinder in binderToJs" - binderToJs' varName done (NamedBinder _ ident binder) = do - js <- binderToJs varName done binder - return (AST.VariableIntroduction Nothing (identToJs ident) (Just (NoEffects, AST.Var Nothing varName)) : js) - - literalToBinderJS :: Text -> [AST] -> Literal (Binder Ann) -> m [AST] - literalToBinderJS varName done (NumericLiteral num) = - return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.NumericLiteral Nothing num)) (AST.Block Nothing done) Nothing] - literalToBinderJS varName done (CharLiteral c) = - return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing (fromString [c]))) (AST.Block Nothing done) Nothing] - literalToBinderJS varName done (StringLiteral str) = - return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing str)) (AST.Block Nothing done) Nothing] - literalToBinderJS varName done (BooleanLiteral True) = - return [AST.IfElse Nothing (AST.Var Nothing varName) (AST.Block Nothing done) Nothing] - literalToBinderJS varName done (BooleanLiteral False) = - return [AST.IfElse Nothing (AST.Unary Nothing AST.Not (AST.Var Nothing varName)) (AST.Block Nothing done) Nothing] - literalToBinderJS varName done (ObjectLiteral bs) = go done bs - where - go :: [AST] -> [(PSString, Binder Ann)] -> m [AST] - go done' [] = return done' - go done' ((prop, binder):bs') = do - propVar <- freshName - done'' <- go done' bs' - js <- binderToJs propVar done'' binder - return (AST.VariableIntroduction Nothing propVar (Just (UnknownEffects, accessorString prop (AST.Var Nothing varName))) : js) - literalToBinderJS varName done (ArrayLiteral bs) = do - js <- go done 0 bs - return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (accessorString "length" (AST.Var Nothing varName)) (AST.NumericLiteral Nothing (Left (fromIntegral $ length bs)))) (AST.Block Nothing js) Nothing] - where - go :: [AST] -> Integer -> [Binder Ann] -> m [AST] - go done' _ [] = return done' - go done' index (binder:bs') = do - elVar <- freshName - done'' <- go done' (index + 1) bs' - js <- binderToJs elVar done'' binder - return (AST.VariableIntroduction Nothing elVar (Just (UnknownEffects, AST.Indexer Nothing (AST.NumericLiteral Nothing (Left index)) (AST.Var Nothing varName))) : js) - -accessorString :: PSString -> AST -> AST -accessorString prop = AST.Indexer Nothing (AST.StringLiteral Nothing prop) - -pattern FFINamespace :: Text -pattern FFINamespace = "$foreign" diff --git a/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS/Common.hs b/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS/Common.hs deleted file mode 100644 index e0294689..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS/Common.hs +++ /dev/null @@ -1,249 +0,0 @@ --- | Common code generation utility functions -module Language.PureScript.CodeGen.JS.Common where - -import Prelude - -import Data.Char (isAlpha, isAlphaNum, isDigit, ord) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Crash (internalError) -import Language.PureScript.Names (Ident(..), InternalIdentData(..), ModuleName(..), ProperName(..), unusedIdent) - -moduleNameToJs :: ModuleName -> Text -moduleNameToJs (ModuleName mn) = - let name = T.replace "." "_" mn - in if nameIsJsBuiltIn name then "$$" <> name else name - --- | Convert an 'Ident' into a valid JavaScript identifier: --- --- * Alphanumeric characters are kept unmodified. --- --- * Reserved javascript identifiers and identifiers starting with digits are --- prefixed with '$$'. -identToJs :: Ident -> Text -identToJs (Ident name) - | not (T.null name) && isDigit (T.head name) = "$$" <> T.concatMap identCharToText name - | otherwise = anyNameToJs name -identToJs (GenIdent _ _) = internalError "GenIdent in identToJs" -identToJs UnusedIdent = unusedIdent -identToJs (InternalIdent RuntimeLazyFactory) = "$runtime_lazy" -identToJs (InternalIdent (Lazy name)) = "$lazy_" <> anyNameToJs name - --- | Convert a 'ProperName' into a valid JavaScript identifier: --- --- * Alphanumeric characters are kept unmodified. --- --- * Reserved javascript identifiers are prefixed with '$$'. -properToJs :: ProperName a -> Text -properToJs = anyNameToJs . runProperName - --- | Convert any name into a valid JavaScript identifier. --- --- Note that this function assumes that the argument is a valid PureScript --- identifier (either an 'Ident' or a 'ProperName') to begin with; as such it --- will not produce valid JavaScript identifiers if the argument e.g. begins --- with a digit. Prefer 'identToJs' or 'properToJs' where possible. -anyNameToJs :: Text -> Text -anyNameToJs name - | nameIsJsReserved name || nameIsJsBuiltIn name = "$$" <> name - | otherwise = T.concatMap identCharToText name - --- | Test if a string is a valid JavaScript identifier as-is. Note that, while --- a return value of 'True' guarantees that the string is a valid JS --- identifier, a return value of 'False' does not guarantee that the string is --- not a valid JS identifier. That is, this check is more conservative than --- absolutely necessary. -isValidJsIdentifier :: Text -> Bool -isValidJsIdentifier s = - not (T.null s) && - isAlpha (T.head s) && - s == anyNameToJs s - --- | Attempts to find a human-readable name for a symbol, if none has been specified returns the --- ordinal value. -identCharToText :: Char -> Text -identCharToText c | isAlphaNum c = T.singleton c -identCharToText '_' = "_" -identCharToText '.' = "$dot" -identCharToText '$' = "$dollar" -identCharToText '~' = "$tilde" -identCharToText '=' = "$eq" -identCharToText '<' = "$less" -identCharToText '>' = "$greater" -identCharToText '!' = "$bang" -identCharToText '#' = "$hash" -identCharToText '%' = "$percent" -identCharToText '^' = "$up" -identCharToText '&' = "$amp" -identCharToText '|' = "$bar" -identCharToText '*' = "$times" -identCharToText '/' = "$div" -identCharToText '+' = "$plus" -identCharToText '-' = "$minus" -identCharToText ':' = "$colon" -identCharToText '\\' = "$bslash" -identCharToText '?' = "$qmark" -identCharToText '@' = "$at" -identCharToText '\'' = "$prime" -identCharToText c = '$' `T.cons` T.pack (show (ord c)) - --- | Checks whether an identifier name is reserved in JavaScript. -nameIsJsReserved :: Text -> Bool -nameIsJsReserved name = - name `elem` jsAnyReserved - --- | Checks whether a name matches a built-in value in JavaScript. -nameIsJsBuiltIn :: Text -> Bool -nameIsJsBuiltIn name = - name `elem` - [ "arguments" - , "Array" - , "ArrayBuffer" - , "Boolean" - , "DataView" - , "Date" - , "decodeURI" - , "decodeURIComponent" - , "encodeURI" - , "encodeURIComponent" - , "Error" - , "escape" - , "eval" - , "EvalError" - , "Float32Array" - , "Float64Array" - , "Function" - , "Infinity" - , "Int16Array" - , "Int32Array" - , "Int8Array" - , "Intl" - , "isFinite" - , "isNaN" - , "JSON" - , "Map" - , "Math" - , "NaN" - , "Number" - , "Object" - , "parseFloat" - , "parseInt" - , "Promise" - , "Proxy" - , "RangeError" - , "ReferenceError" - , "Reflect" - , "RegExp" - , "Set" - , "SIMD" - , "String" - , "Symbol" - , "SyntaxError" - , "TypeError" - , "Uint16Array" - , "Uint32Array" - , "Uint8Array" - , "Uint8ClampedArray" - , "undefined" - , "unescape" - , "URIError" - , "WeakMap" - , "WeakSet" - ] - -jsAnyReserved :: [Text] -jsAnyReserved = - concat - [ jsKeywords - , jsSometimesReserved - , jsFutureReserved - , jsFutureReservedStrict - , jsOldReserved - , jsLiterals - ] - -jsKeywords :: [Text] -jsKeywords = - [ "break" - , "case" - , "catch" - , "class" - , "const" - , "continue" - , "debugger" - , "default" - , "delete" - , "do" - , "else" - , "export" - , "extends" - , "finally" - , "for" - , "function" - , "if" - , "import" - , "in" - , "instanceof" - , "new" - , "return" - , "super" - , "switch" - , "this" - , "throw" - , "try" - , "typeof" - , "var" - , "void" - , "while" - , "with" - ] - -jsSometimesReserved :: [Text] -jsSometimesReserved = - [ "await" - , "let" - , "static" - , "yield" - ] - -jsFutureReserved :: [Text] -jsFutureReserved = - [ "enum" ] - -jsFutureReservedStrict :: [Text] -jsFutureReservedStrict = - [ "implements" - , "interface" - , "package" - , "private" - , "protected" - , "public" - ] - -jsOldReserved :: [Text] -jsOldReserved = - [ "abstract" - , "boolean" - , "byte" - , "char" - , "double" - , "final" - , "float" - , "goto" - , "int" - , "long" - , "native" - , "short" - , "synchronized" - , "throws" - , "transient" - , "volatile" - ] - -jsLiterals :: [Text] -jsLiterals = - [ "null" - , "true" - , "false" - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS/Printer.hs b/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS/Printer.hs deleted file mode 100644 index 6740e2a7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CodeGen/JS/Printer.hs +++ /dev/null @@ -1,310 +0,0 @@ --- | Pretty printer for the JavaScript AST -module Language.PureScript.CodeGen.JS.Printer - ( prettyPrintJS - , prettyPrintJSWithSourceMaps - ) where - -import Prelude - -import Control.Arrow ((<+>)) -import Control.Monad (forM, mzero) -import Control.Monad.State (StateT, evalStateT) -import Control.PatternArrows (Operator(..), OperatorTable(..), Pattern(..), buildPrettyPrinter, mkPattern, mkPattern') -import Control.Arrow qualified as A - -import Data.Maybe (fromMaybe) -import Data.Text (Text) -import Data.Text qualified as T -import Data.List.NonEmpty qualified as NEL (toList) - -import Language.PureScript.AST (SourceSpan(..)) -import Language.PureScript.CodeGen.JS.Common (identCharToText, isValidJsIdentifier, nameIsJsBuiltIn, nameIsJsReserved) -import Language.PureScript.CoreImp.AST (AST(..), BinaryOperator(..), CIComments(..), UnaryOperator(..), getSourceSpan) -import Language.PureScript.CoreImp.Module (Export(..), Import(..), Module(..)) -import Language.PureScript.Comments (Comment(..)) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Pretty.Common (Emit(..), PrinterState(..), SMap, StrPos(..), addMapping', currentIndent, intercalate, parensPos, runPlainString, withIndent) -import Language.PureScript.PSString (PSString, decodeString, prettyPrintStringJS) - --- TODO (Christoph): Get rid of T.unpack / pack - -literals :: (Emit gen) => Pattern PrinterState AST gen -literals = mkPattern' match' - where - match' :: (Emit gen) => AST -> StateT PrinterState Maybe gen - match' js = (addMapping' (getSourceSpan js) <>) <$> match js - - match :: (Emit gen) => AST -> StateT PrinterState Maybe gen - match (NumericLiteral _ n) = return $ emit $ T.pack $ either show show n - match (StringLiteral _ s) = return $ emit $ prettyPrintStringJS s - match (BooleanLiteral _ True) = return $ emit "true" - match (BooleanLiteral _ False) = return $ emit "false" - match (ArrayLiteral _ xs) = mconcat <$> sequence - [ return $ emit "[ " - , intercalate (emit ", ") <$> forM xs prettyPrintJS' - , return $ emit " ]" - ] - match (ObjectLiteral _ []) = return $ emit "{}" - match (ObjectLiteral _ ps) = mconcat <$> sequence - [ return $ emit "{\n" - , withIndent $ do - jss <- forM ps $ \(key, value) -> fmap ((objectPropertyToString key <> emit ": ") <>) . prettyPrintJS' $ value - indentString <- currentIndent - return $ intercalate (emit ",\n") $ map (indentString <>) jss - , return $ emit "\n" - , currentIndent - , return $ emit "}" - ] - where - objectPropertyToString :: (Emit gen) => PSString -> gen - objectPropertyToString s = - emit $ case decodeString s of - Just s' | isValidJsIdentifier s' -> - s' - _ -> - prettyPrintStringJS s - match (Block _ sts) = mconcat <$> sequence - [ return $ emit "{\n" - , withIndent $ prettyStatements sts - , return $ emit "\n" - , currentIndent - , return $ emit "}" - ] - match (Var _ ident) = return $ emit ident - match (VariableIntroduction _ ident value) = mconcat <$> sequence - [ return $ emit $ "var " <> ident - , maybe (return mempty) (fmap (emit " = " <>) . prettyPrintJS' . snd) value - ] - match (Assignment _ target value) = mconcat <$> sequence - [ prettyPrintJS' target - , return $ emit " = " - , prettyPrintJS' value - ] - match (While _ cond sts) = mconcat <$> sequence - [ return $ emit "while (" - , prettyPrintJS' cond - , return $ emit ") " - , prettyPrintJS' sts - ] - match (For _ ident start end sts) = mconcat <$> sequence - [ return $ emit $ "for (var " <> ident <> " = " - , prettyPrintJS' start - , return $ emit $ "; " <> ident <> " < " - , prettyPrintJS' end - , return $ emit $ "; " <> ident <> "++) " - , prettyPrintJS' sts - ] - match (ForIn _ ident obj sts) = mconcat <$> sequence - [ return $ emit $ "for (var " <> ident <> " in " - , prettyPrintJS' obj - , return $ emit ") " - , prettyPrintJS' sts - ] - match (IfElse _ cond thens elses) = mconcat <$> sequence - [ return $ emit "if (" - , prettyPrintJS' cond - , return $ emit ") " - , prettyPrintJS' thens - , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintJS') elses - ] - match (Return _ value) = mconcat <$> sequence - [ return $ emit "return " - , prettyPrintJS' value - ] - match (ReturnNoResult _) = return $ emit "return" - match (Throw _ value) = mconcat <$> sequence - [ return $ emit "throw " - , prettyPrintJS' value - ] - match (Comment (SourceComments com) js) = mconcat <$> sequence - [ return $ emit "\n" - , mconcat <$> forM com comment - , prettyPrintJS' js - ] - match (Comment PureAnnotation js) = mconcat <$> sequence - [ return $ emit "/* #__PURE__ */ " - , prettyPrintJS' js - ] - match _ = mzero - -comment :: (Emit gen) => Comment -> StateT PrinterState Maybe gen -comment (LineComment com) = mconcat <$> sequence - [ currentIndent - , return $ emit "//" <> emit com <> emit "\n" - ] -comment (BlockComment com) = fmap mconcat $ sequence $ - [ currentIndent - , return $ emit "/**\n" - ] ++ - map asLine (T.lines com) ++ - [ currentIndent - , return $ emit " */\n" - , currentIndent - ] - where - asLine :: (Emit gen) => Text -> StateT PrinterState Maybe gen - asLine s = do - i <- currentIndent - return $ i <> emit " * " <> (emit . removeComments) s <> emit "\n" - - removeComments :: Text -> Text - removeComments t = - case T.stripPrefix "*/" t of - Just rest -> removeComments rest - Nothing -> case T.uncons t of - Just (x, xs) -> x `T.cons` removeComments xs - Nothing -> "" - -prettyImport :: (Emit gen) => Import -> StateT PrinterState Maybe gen -prettyImport (Import ident from) = - return . emit $ - "import * as " <> ident <> " from " <> prettyPrintStringJS from <> ";" - -prettyExport :: (Emit gen) => Export -> StateT PrinterState Maybe gen -prettyExport (Export idents from) = - mconcat <$> sequence - [ return $ emit "export {\n" - , withIndent $ do - let exportsStrings = emit . exportedIdentToString from <$> idents - indentString <- currentIndent - return . intercalate (emit ",\n") . NEL.toList $ (indentString <>) <$> exportsStrings - , return $ emit "\n" - , currentIndent - , return . emit $ "}" <> maybe "" ((" from " <>) . prettyPrintStringJS) from <> ";" - ] - where - exportedIdentToString Nothing ident - | nameIsJsReserved ident || nameIsJsBuiltIn ident - = "$$" <> ident <> " as " <> ident - exportedIdentToString _ "$main" - = T.concatMap identCharToText "$main" <> " as $main" - exportedIdentToString _ ident - = T.concatMap identCharToText ident - -accessor :: Pattern PrinterState AST (Text, AST) -accessor = mkPattern match - where - match (Indexer _ (StringLiteral _ prop) val) = - case decodeString prop of - Just s | isValidJsIdentifier s -> Just (s, val) - _ -> Nothing - match _ = Nothing - -indexer :: (Emit gen) => Pattern PrinterState AST (gen, AST) -indexer = mkPattern' match - where - match (Indexer _ index val) = (,) <$> prettyPrintJS' index <*> pure val - match _ = mzero - -lam :: Pattern PrinterState AST ((Maybe Text, [Text], Maybe SourceSpan), AST) -lam = mkPattern match - where - match (Function ss name args ret) = Just ((name, args, ss), ret) - match _ = Nothing - -app :: (Emit gen) => Pattern PrinterState AST (gen, AST) -app = mkPattern' match - where - match (App _ val args) = do - jss <- traverse prettyPrintJS' args - return (intercalate (emit ", ") jss, val) - match _ = mzero - -instanceOf :: Pattern PrinterState AST (AST, AST) -instanceOf = mkPattern match - where - match (InstanceOf _ val ty) = Just (val, ty) - match _ = Nothing - -unary' :: (Emit gen) => UnaryOperator -> (AST -> Text) -> Operator PrinterState AST gen -unary' op mkStr = Wrap match (<>) - where - match :: (Emit gen) => Pattern PrinterState AST (gen, AST) - match = mkPattern match' - where - match' (Unary _ op' val) | op' == op = Just (emit $ mkStr val, val) - match' _ = Nothing - -unary :: (Emit gen) => UnaryOperator -> Text -> Operator PrinterState AST gen -unary op str = unary' op (const str) - -negateOperator :: (Emit gen) => Operator PrinterState AST gen -negateOperator = unary' Negate (\v -> if isNegate v then "- " else "-") - where - isNegate (Unary _ Negate _) = True - isNegate _ = False - -binary :: (Emit gen) => BinaryOperator -> Text -> Operator PrinterState AST gen -binary op str = AssocL match (\v1 v2 -> v1 <> emit (" " <> str <> " ") <> v2) - where - match :: Pattern PrinterState AST (AST, AST) - match = mkPattern match' - where - match' (Binary _ op' v1 v2) | op' == op = Just (v1, v2) - match' _ = Nothing - -prettyStatements :: (Emit gen) => [AST] -> StateT PrinterState Maybe gen -prettyStatements sts = do - jss <- forM sts prettyPrintJS' - indentString <- currentIndent - return $ intercalate (emit "\n") $ map ((<> emit ";") . (indentString <>)) jss - -prettyModule :: (Emit gen) => Module -> StateT PrinterState Maybe gen -prettyModule Module{..} = do - header <- mconcat <$> traverse comment modHeader - imps <- traverse prettyImport modImports - body <- prettyStatements modBody - exps <- traverse prettyExport modExports - pure $ header <> intercalate (emit "\n") (imps ++ body : exps) - --- | Generate a pretty-printed string representing a collection of JavaScript expressions at the same indentation level -prettyPrintJSWithSourceMaps :: Module -> (Text, [SMap]) -prettyPrintJSWithSourceMaps js = - let StrPos (_, s, mp) = (fromMaybe (internalError "Incomplete pattern") . flip evalStateT (PrinterState 0) . prettyModule) js - in (s, mp) - -prettyPrintJS :: Module -> Text -prettyPrintJS = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyModule - --- | Generate an indented, pretty-printed string representing a JavaScript expression -prettyPrintJS' :: (Emit gen) => AST -> StateT PrinterState Maybe gen -prettyPrintJS' = A.runKleisli $ runPattern matchValue - where - matchValue :: (Emit gen) => Pattern PrinterState AST gen - matchValue = buildPrettyPrinter operators (literals <+> fmap parensPos matchValue) - operators :: (Emit gen) => OperatorTable PrinterState AST gen - operators = - OperatorTable [ [ Wrap indexer $ \index val -> val <> emit "[" <> index <> emit "]" ] - , [ Wrap accessor $ \prop val -> val <> emit "." <> emit prop ] - , [ Wrap app $ \args val -> val <> emit "(" <> args <> emit ")" ] - , [ unary New "new " ] - , [ Wrap lam $ \(name, args, ss) ret -> addMapping' ss <> - emit ("function " - <> fromMaybe "" name - <> "(" <> intercalate ", " args <> ") ") - <> ret ] - , [ unary Not "!" - , unary BitwiseNot "~" - , unary Positive "+" - , negateOperator ] - , [ binary Multiply "*" - , binary Divide "/" - , binary Modulus "%" ] - , [ binary Add "+" - , binary Subtract "-" ] - , [ binary ShiftLeft "<<" - , binary ShiftRight ">>" - , binary ZeroFillShiftRight ">>>" ] - , [ binary LessThan "<" - , binary LessThanOrEqualTo "<=" - , binary GreaterThan ">" - , binary GreaterThanOrEqualTo ">=" - , AssocR instanceOf $ \v1 v2 -> v1 <> emit " instanceof " <> v2 ] - , [ binary EqualTo "===" - , binary NotEqualTo "!==" ] - , [ binary BitwiseAnd "&" ] - , [ binary BitwiseXor "^" ] - , [ binary BitwiseOr "|" ] - , [ binary And "&&" ] - , [ binary Or "||" ] - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/Comments.hs b/claude-help/original-compiler/src/Language/PureScript/Comments.hs deleted file mode 100644 index ee05cd9c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Comments.hs +++ /dev/null @@ -1,24 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - --- | --- Defines the types of source code comments --- -module Language.PureScript.Comments where - -import Prelude -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Data.Text (Text) -import GHC.Generics (Generic) - -import Data.Aeson.TH (Options(..), SumEncoding(..), defaultOptions, deriveJSON) - -data Comment - = LineComment Text - | BlockComment Text - deriving (Show, Eq, Ord, Generic) - -instance NFData Comment -instance Serialise Comment - -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''Comment) diff --git a/claude-help/original-compiler/src/Language/PureScript/Constants/Libs.hs b/claude-help/original-compiler/src/Language/PureScript/Constants/Libs.hs deleted file mode 100644 index 0b44d3e4..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Constants/Libs.hs +++ /dev/null @@ -1,279 +0,0 @@ -{-# LANGUAGE BlockArguments #-} -{-# LANGUAGE TemplateHaskell #-} - --- | Various constants which refer to things in the Prelude and other core libraries -module Language.PureScript.Constants.Libs where - -import Protolude qualified as P - -import Data.String (IsString) -import Language.PureScript.Constants.TH qualified as TH -import Language.PureScript.PSString (PSString) -import Language.PureScript.Names (Ident (..), Qualified (..), QualifiedBy (..)) - --- Core lib values - -stRefValue :: forall a. IsString a => a -stRefValue = "value" - --- Type Class Dictionary Names - -data EffectDictionaries = EffectDictionaries - { edApplicativeDict :: PSString - , edBindDict :: PSString - , edMonadDict :: PSString - , edWhile :: PSString - , edUntil :: PSString - } - -effDictionaries :: EffectDictionaries -effDictionaries = EffectDictionaries - { edApplicativeDict = "applicativeEff" - , edBindDict = "bindEff" - , edMonadDict = "monadEff" - , edWhile = "whileE" - , edUntil = "untilE" - } - -effectDictionaries :: EffectDictionaries -effectDictionaries = EffectDictionaries - { edApplicativeDict = "applicativeEffect" - , edBindDict = "bindEffect" - , edMonadDict = "monadEffect" - , edWhile = "whileE" - , edUntil = "untilE" - } - -stDictionaries :: EffectDictionaries -stDictionaries = EffectDictionaries - { edApplicativeDict = "applicativeST" - , edBindDict = "bindST" - , edMonadDict = "monadST" - , edWhile = "while" - , edUntil = "until" - } - -$(TH.declare do - - -- purescript-prelude - - TH.mod "Control.Apply" do - TH.asIdent do TH.asString do TH.var "apply" - - TH.mod "Control.Applicative" do - TH.asIdent do TH.asPair do TH.asString do TH.var "pure" - - TH.mod "Control.Bind" do - TH.asPair do - TH.asString do - TH.var "bind" - TH.cls "Discard" ; TH.var "discard" - - TH.var "discardUnit" - - TH.mod "Control.Category" do - TH.asPair do - TH.asIdent do TH.var "identity" - - TH.var "categoryFn" - - TH.mod "Control.Semigroupoid" do - TH.asPair do - TH.vars ["compose", "composeFlipped"] - TH.var "semigroupoidFn" - - TH.mod "Data.Bounded" do - TH.asPair do - TH.vars ["bottom", "top"] - TH.var "boundedBoolean" - - TH.mod "Data.Eq" do - TH.cls "Eq" ; TH.asIdent do TH.asPair do TH.asString do TH.var "eq" - TH.cls "Eq1" ; TH.asIdent do TH.asString do TH.var "eq1" - TH.asPair do - TH.var "notEq" - - TH.var "eqBoolean" - TH.var "eqChar" - TH.var "eqInt" - TH.var "eqNumber" - TH.var "eqString" - - TH.mod "Data.EuclideanRing" do - TH.asPair do - TH.var "div" - - TH.var "euclideanRingNumber" - - TH.mod "Data.Function" do - TH.asIdent do - TH.prefixWith "function" do TH.vars ["apply", "applyFlipped"] - TH.var "const" - TH.var "flip" - - TH.mod "Data.Functor" do - TH.cls "Functor" ; TH.asIdent do TH.asString do TH.var "map" - - TH.mod "Data.Generic.Rep" do - TH.cls "Generic" ; TH.asIdent do TH.vars ["from", "to"] - TH.ntys ["Argument", "Constructor", "NoArguments", "NoConstructors", "Product"] - TH.dty "Sum" ["Inl", "Inr"] - - TH.mod "Data.HeytingAlgebra" do - TH.asPair do - TH.asIdent do TH.vars ["conj", "disj", "not"] - - TH.var "heytingAlgebraBoolean" - - TH.mod "Data.Monoid" do - TH.asIdent do TH.var "mempty" - - TH.mod "Data.Ord" do - TH.cls "Ord" ; TH.asIdent do TH.asString do TH.var "compare" - TH.cls "Ord1" ; TH.asIdent do TH.asString do TH.var "compare1" - TH.asPair do - TH.vars ["greaterThan", "greaterThanOrEq", "lessThan", "lessThanOrEq"] - - TH.var "ordBoolean" - TH.var "ordChar" - TH.var "ordInt" - TH.var "ordNumber" - TH.var "ordString" - - TH.mod "Data.Ordering" do - TH.dty "Ordering" ["EQ", "GT", "LT"] - - TH.mod "Data.Reflectable" do - TH.cls "Reflectable" - - TH.mod "Data.Ring" do - TH.asPair do - TH.asString do TH.vars ["negate", "sub"] - - TH.var "ringInt" - TH.var "ringNumber" - - TH.mod "Data.Semigroup" do - TH.asPair do - TH.asIdent do TH.var "append" - - TH.var "semigroupString" - - TH.mod "Data.Semiring" do - TH.asPair do - TH.vars ["add", "mul", "one", "zero"] - - TH.var "semiringInt" - TH.var "semiringNumber" - - TH.mod "Data.Symbol" do - TH.cls "IsSymbol" - TH.asIdent do TH.var "IsSymbol" - - -- purescript-arrays - - TH.mod "Data.Array" do - TH.asPair do TH.var "unsafeIndex" - - -- purescript-bifunctors - - TH.mod "Data.Bifunctor" do - TH.cls "Bifunctor" ; TH.asIdent do TH.asString do TH.var "bimap" - TH.asIdent do TH.vars ["lmap", "rmap"] - - -- purescript-contravariant - - TH.mod "Data.Functor.Contravariant" do - TH.cls "Contravariant" ; TH.asIdent do TH.asString do TH.var "cmap" - - -- purescript-eff - - TH.mod "Control.Monad.Eff" (P.pure ()) - - TH.mod "Control.Monad.Eff.Uncurried" do - TH.asPair do TH.vars ["mkEffFn", "runEffFn"] - - -- purescript-effect - - TH.mod "Effect" (P.pure ()) - - TH.mod "Effect.Uncurried" do - TH.asPair do TH.vars ["mkEffectFn", "runEffectFn"] - - -- purescript-foldable-traversable - - TH.mod "Data.Bifoldable" do - TH.cls "Bifoldable" ; TH.asIdent do TH.asString do TH.vars ["bifoldMap", "bifoldl", "bifoldr"] - - TH.mod "Data.Bitraversable" do - TH.cls "Bitraversable" ; TH.asString do TH.asIdent (TH.var "bitraverse"); TH.var "bisequence" - TH.asIdent do - TH.vars ["ltraverse", "rtraverse"] - - TH.mod "Data.Foldable" do - TH.cls "Foldable" ; TH.asIdent do TH.asString do TH.vars ["foldMap", "foldl", "foldr"] - - TH.mod "Data.Traversable" do - TH.cls "Traversable" ; TH.asString do TH.asIdent (TH.var "traverse") ; TH.var "sequence" - - -- purescript-functions - - TH.mod "Data.Function.Uncurried" do - TH.asPair do TH.asString do TH.vars ["mkFn", "runFn"] - - -- purescript-integers - - TH.mod "Data.Int.Bits" do - TH.asPair do - TH.var "and" - TH.var "complement" - TH.var "or" - TH.var "shl" - TH.var "shr" - TH.var "xor" - TH.var "zshr" - - -- purescript-newtype - - TH.mod "Data.Newtype" do - TH.cls "Newtype" - - -- purescript-partial - - TH.mod "Partial.Unsafe" do - TH.asIdent do TH.asPair do TH.var "unsafePartial" - - -- purescript-profunctor - - TH.mod "Data.Profunctor" do - TH.cls "Profunctor" ; TH.asIdent do TH.asString do TH.var "dimap" - TH.asIdent do - TH.var "lcmap" - TH.prefixWith "profunctor" do TH.var "rmap" - - -- purescript-st - - TH.mod "Control.Monad.ST.Internal" do - TH.asPair do TH.vars ["modify", "new", "read", "run", "write"] - - TH.mod "Control.Monad.ST.Uncurried" do - TH.asPair do TH.vars ["mkSTFn", "runSTFn"] - - -- purescript-unsafe-coerce - - TH.mod "Unsafe.Coerce" do - TH.asPair do TH.var "unsafeCoerce" - - TH.mod "Type.Proxy" do - TH.dty "Proxy" ["Proxy"] - TH.asIdent do - TH.var "Proxy" - TH.mod "Data.Record" do - TH.asIdent do - TH.var "getField" - TH.var "hasFieldRecord" - - ) - -pattern IsSymbolDict :: Qualified Ident -pattern IsSymbolDict = Qualified (ByModuleName M_Data_Symbol) (Ident "IsSymbol$Dict") diff --git a/claude-help/original-compiler/src/Language/PureScript/Constants/Prim.hs b/claude-help/original-compiler/src/Language/PureScript/Constants/Prim.hs deleted file mode 100644 index 08391155..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Constants/Prim.hs +++ /dev/null @@ -1,57 +0,0 @@ -{-# LANGUAGE BlockArguments #-} -{-# LANGUAGE TemplateHaskell #-} --- | Various constants which refer to things in Prim -module Language.PureScript.Constants.Prim where - -import Language.PureScript.Names (ModuleName) -import Language.PureScript.Constants.TH qualified as TH - -$(TH.declare do - TH.mod "Prim" do - TH.cls "Partial" - TH.ty "Array" - TH.ty "Boolean" - TH.ty "Char" - TH.ty "Constraint" - TH.ty "Function" - TH.ty "Int" - TH.ty "Number" - TH.ty "Record" - TH.ty "Row" - TH.ty "String" - TH.ty "Symbol" - TH.ty "Type" - TH.asIdent do TH.asString do TH.var "undefined" - - TH.mod "Prim.Boolean" do - TH.tys ["False", "True"] - - TH.mod "Prim.Coerce" do - TH.cls "Coercible" - - TH.mod "Prim.Int" do - TH.prefixWith "Int" do TH.clss ["Add", "Compare", "Mul", "ToString"] - - TH.mod "Prim.Ordering" do - TH.prefixWith "Type" do TH.ty "Ordering" - TH.tys ["EQ", "GT", "LT"] - - TH.mod "Prim.Row" do - TH.prefixWith "Row" do TH.clss ["Cons", "Lacks", "Nub", "Union"] - - TH.mod "Prim.RowList" do - TH.ty "RowList" - TH.cls "RowToList" - TH.prefixWith "RowList" do TH.tys ["Cons", "Nil"] - - TH.mod "Prim.Symbol" do - TH.prefixWith "Symbol" do TH.clss ["Append", "Compare", "Cons"] - - TH.mod "Prim.TypeError" do - TH.clss ["Fail", "Warn"] - TH.tys ["Above", "Beside", "Doc", "Quote", "QuoteLabel", "Text"] - - ) - -primModules :: [ModuleName] -primModules = [M_Prim, M_Prim_Boolean, M_Prim_Coerce, M_Prim_Ordering, M_Prim_Row, M_Prim_RowList, M_Prim_Symbol, M_Prim_Int, M_Prim_TypeError] diff --git a/claude-help/original-compiler/src/Language/PureScript/Constants/TH.hs b/claude-help/original-compiler/src/Language/PureScript/Constants/TH.hs deleted file mode 100644 index 2bc8a56d..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Constants/TH.hs +++ /dev/null @@ -1,224 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} --- | This module implements an eDSL for compactly declaring pattern synonyms --- representing known PureScript modules and their members. --- --- The following example assumes this module is imported qualified as TH and --- the BlockArguments extension is used, both of which I recommend. --- --- > $(TH.declare do --- > TH.mod "Data.Foo" do --- > TH.ty "SomeType" --- > TH.asIdent do --- > TH.var "someVariable" --- > ) --- --- will become: --- --- > pattern M_Data_Foo :: ModuleName --- > pattern M_Data_Foo = ModuleName "Data.Foo" --- > --- > pattern SomeType :: Qualified (ProperName 'TypeName) --- > pattern SomeType = Qualified (ByModuleName M_Data_Foo) (ProperName "SomeType") --- > --- > pattern I_someVariable :: Qualified Ident --- > pattern I_someVariable = Qualified (ByModuleName M_Data_Foo) (Ident "someVariable") --- --- All pattern synonyms must start with an uppercase letter. To prevent --- namespace collisions, different types of pattern are distinguished by a sort --- of Hungarian notation convention: --- --- @ --- SomeType -- a type or class name --- C_Ctor -- a constructor name --- I_name -- a Qualified Ident --- M_Data_Foo -- a module name --- P_name -- a (module name, polymorphic string) pair --- S_name -- a lone polymorphic string (this doesn't contain any module information) --- @ --- --- I_, P_, and S_ patterns are all optional and have to be enabled with --- `asIdent`, `asPair`, and `asString` modifiers respectively. --- --- Finally, to disambiguate between identifiers with the same name (such as --- Data.Function.apply and Data.Apply.apply), the `prefixWith` modifier will --- modify the names of the patterns created within it. --- --- > TH.mod "Data.Function" do --- > TH.prefixWith "function" do --- > TH.asIdent do --- > TH.var "apply" --- --- results in: --- --- > pattern I_functionApply :: Qualified Ident --- > pattern I_functionApply = Qualified (ByModuleName (M_Data_Function) (Ident "apply") --- -module Language.PureScript.Constants.TH - ( declare - , mod - , cls, clss - , dty - , nty, ntys - , ty, tys - , var, vars - , prefixWith - , asIdent - , asPair - , asString - ) where - -import Protolude hiding (Type, mod) - -import Control.Lens (over, _head) -import Control.Monad.Trans.RWS (RWS, execRWS) -import Control.Monad.Trans.Writer (Writer, execWriter) -import Control.Monad.Writer.Class (tell) -import Data.String (String) -import Language.Haskell.TH (Dec, Name, Pat, Q, Type, conP, implBidir, litP, mkName, patSynD, patSynSigD, prefixPatSyn, stringL) -import Language.PureScript.Names (Ident(..), ModuleName(..), ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..)) - --- | Generate pattern synonyms corresponding to the provided PureScript --- declarations. -declare :: Writer (Q [Dec]) () -> Q [Dec] -declare = execWriter - --- | Declare a module. -mod :: String -> ModDecs -> Writer (Q [Dec]) () -mod mnStr inner = do - -- pattern M_Data_Foo :: ModuleName - -- pattern M_Data_Foo = ModuleName "Data.Foo" - let mn = mkModuleName mnStr - tell $ typedPatSyn mn [t| ModuleName |] [p| ModuleName $(litP $ stringL mnStr) |] - tell $ snd $ execRWS inner (mn, "", []) () - --- | Declare a type class. The resulting pattern will use the name of the class --- and have type `Qualified (ProperName 'ClassName)`. -cls :: String -> ModDecs -cls cn = ask >>= \(mn, prefix, _) -> tell $ mkPnPat [t| 'ClassName |] mn prefix cn - --- | Declare a list of type classes; shorthand for repeatedly calling `cls`. -clss :: [String] -> ModDecs -clss = traverse_ cls - --- | Declare a data type, given the name of the type and a list of constructor --- names. A pattern will be created using the name of the type and have type --- `Qualified (ProperName 'TypeName)`. A pattern will also be created for each --- constructor prefixed with "C_", having type `Qualified (ProperName --- 'ConstructorName)`. -dty :: String -> [String] -> ModDecs -dty dn ctors = ask >>= \(mn, prefix, _) -> do - tell $ mkPnPat [t| 'TypeName |] mn prefix dn - tell $ map fold $ traverse (mkPnPat [t| 'ConstructorName |] mn $ "C_" <> prefix) ctors - --- | Declare a data type with a singular constructor named the same as the --- type, as is commonly the case with newtypes (but this does not require the --- type to be a newtype in reality). Shorthand for calling `dty`. -nty :: String -> ModDecs -nty tn = dty tn [tn] - --- | Declare a list of data types with singular constructors; shorthand for --- repeatedly calling `nty`, which itself is shorthand for `dty`. -ntys :: [String] -> ModDecs -ntys = traverse_ nty - --- | Declare a type. The resulting pattern will use the name of the type and have --- type `Qualified (ProperName 'TypeName)`. -ty :: String -> ModDecs -ty tn = ask >>= \(mn, prefix, _) -> tell $ mkPnPat [t| 'TypeName |] mn prefix tn - --- | Declare a list of types; shorthand for repeatedly calling `ty`. -tys :: [String] -> ModDecs -tys = traverse_ ty - --- | Declare a variable, function, named instance, or generally a lower-case --- value member of a module. The patterns created depend on which of `asPair`, --- `asIdent`, or `asString` are used in the enclosing context. -var :: String -> ModDecs -var nm = ask >>= \(mn, prefix, vtds) -> tell $ foldMap (\f -> f mn prefix nm) vtds - --- | Declare a list of variables; shorthand for repeatedly calling `var`. -vars :: [String] -> ModDecs -vars = traverse_ var - --- | For every variable declared within, create a pattern synonym prefixed --- with "P_" having type `forall a. (Eq a, IsString a) => (ModuleName, a)`. -asPair :: ModDecs -> ModDecs -asPair = local $ addToVars mkPairDec - --- | For every variable declared within, cerate a pattern synonym prefixed --- with "I_" having type `Qualified Ident`. -asIdent :: ModDecs -> ModDecs -asIdent = local $ addToVars mkIdentDec - --- | For every variable declared within, cerate a pattern synonym prefixed --- with "S_" having type `forall a. (Eq a, IsString a) => a`. -asString :: ModDecs -> ModDecs -asString = local $ addToVars mkStringDec - --- | Prefix the names of all enclosed declarations with the provided string, to --- prevent collisions with other identifiers. For example, --- `prefixWith "function"` would turn `I_apply` into `I_functionApply`, and --- `C_Example` into `C_FunctionExample`. -prefixWith :: String -> ModDecs -> ModDecs -prefixWith = local . applyPrefix - --- Internals start here - -type ModDecs = RWS (Name, String, [VarToDec]) (Q [Dec]) () () -type VarToDec = Name -> String -> String -> Q [Dec] - -addToVars :: VarToDec -> (a, b, [VarToDec]) -> (a, b, [VarToDec]) -addToVars f (a, b, fs) = (a, b, f : fs) - -applyPrefix :: String -> (a, String, c) -> (a, String, c) -applyPrefix prefix (a, prefix', c) = (a, camelAppend prefix' prefix, c) - -cap :: String -> String -cap = over _head toUpper - -camelAppend :: String -> String -> String -camelAppend l r = if null l then r else l <> cap r - --- "Data.Foo" -> M_Data_Foo -mkModuleName :: String -> Name -mkModuleName = mkName . ("M_" <>) . map (\case '.' -> '_'; other -> other) - --- "I_" -> "fn" -> "foo" -> I_fnFoo --- "I_" -> "" -> "foo" -> I_foo -mkPrefixedName :: String -> String -> String -> Name -mkPrefixedName tag prefix = mkName . (tag <>) . camelAppend prefix - --- 'TypeName -> M_Data_Foo -> "Function" -> "Foo" -> --- pattern FunctionFoo :: Qualified (ProperName 'TypeName) --- pattern FunctionFoo = Qualified (ByModuleName M_Data_Foo) (ProperName "Foo") -mkPnPat :: Q Type -> VarToDec -mkPnPat pnType mn prefix str = typedPatSyn (mkName $ cap prefix <> str) - [t| Qualified (ProperName $pnType) |] - [p| Qualified (ByModuleName $(conP mn [])) (ProperName $(litP $ stringL str)) |] - --- M_Data_Foo -> "function" -> "foo" -> --- pattern I_functionFoo :: Qualified Ident --- pattern I_functionFoo = Qualified (ByModuleName M_Data_Foo) (Ident "foo") -mkIdentDec :: VarToDec -mkIdentDec mn prefix str = typedPatSyn (mkPrefixedName "I_" prefix str) - [t| Qualified Ident |] - [p| Qualified (ByModuleName $(conP mn [])) (Ident $(litP $ stringL str)) |] - --- M_Data_Foo -> "function" -> "foo" -> --- pattern P_functionFoo :: forall a. (Eq a, IsString a) => (ModuleName, a) --- pattern P_functionFoo = (M_Data_Foo, "foo") -mkPairDec :: VarToDec -mkPairDec mn prefix str = typedPatSyn (mkPrefixedName "P_" prefix str) - [t| forall a. (Eq a, IsString a) => (ModuleName, a) |] - [p| ($(conP mn []), $(litP $ stringL str)) |] - --- _ -> "function" -> "foo" -> --- pattern S_functionFoo :: forall a. (Eq a, IsString a) => a --- pattern S_functionFoo = "foo" -mkStringDec :: VarToDec -mkStringDec _ prefix str = typedPatSyn (mkPrefixedName "S_" prefix str) - [t| forall a. (Eq a, IsString a) => a |] - (litP $ stringL str) - -typedPatSyn :: Name -> Q Type -> Q Pat -> Q [Dec] -typedPatSyn nm t p = sequence [patSynSigD nm t, patSynD nm (prefixPatSyn []) implBidir p] diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn.hs deleted file mode 100644 index b2b73343..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn.hs +++ /dev/null @@ -1,16 +0,0 @@ --- | --- The core functional representation --- -module Language.PureScript.CoreFn ( - module C -) where - -import Language.PureScript.AST.Literals as C -import Language.PureScript.CoreFn.Ann as C -import Language.PureScript.CoreFn.Binders as C -import Language.PureScript.CoreFn.Desugar as C -import Language.PureScript.CoreFn.Expr as C -import Language.PureScript.CoreFn.Meta as C -import Language.PureScript.CoreFn.Module as C -import Language.PureScript.CoreFn.Optimizer as C -import Language.PureScript.CoreFn.Traversals as C diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Ann.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Ann.hs deleted file mode 100644 index 185f8beb..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Ann.hs +++ /dev/null @@ -1,24 +0,0 @@ -module Language.PureScript.CoreFn.Ann where - -import Prelude - -import Language.PureScript.AST.SourcePos (SourceSpan) -import Language.PureScript.Comments (Comment) -import Language.PureScript.CoreFn.Meta (Meta) - --- | --- Type alias for basic annotations --- -type Ann = (SourceSpan, [Comment], Maybe Meta) - --- | --- An annotation empty of metadata aside from a source span. --- -ssAnn :: SourceSpan -> Ann -ssAnn ss = (ss, [], Nothing) - --- | --- Remove the comments from an annotation --- -removeComments :: Ann -> Ann -removeComments (ss, _, meta) = (ss, [], meta) diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Binders.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Binders.hs deleted file mode 100644 index 4b64b97c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Binders.hs +++ /dev/null @@ -1,42 +0,0 @@ --- | --- The core functional representation for binders --- -module Language.PureScript.CoreFn.Binders where - -import Prelude - -import Language.PureScript.AST.Literals (Literal) -import Language.PureScript.Names (Ident, ProperName, ProperNameType(..), Qualified) - --- | --- Data type for binders --- -data Binder a - -- | - -- Wildcard binder - -- - = NullBinder a - -- | - -- A binder which matches a literal value - -- - | LiteralBinder a (Literal (Binder a)) - -- | - -- A binder which binds an identifier - -- - | VarBinder a Ident - -- | - -- A binder which matches a data constructor - -- - | ConstructorBinder a (Qualified (ProperName 'TypeName)) (Qualified (ProperName 'ConstructorName)) [Binder a] - -- | - -- A binder which binds its input to an identifier - -- - | NamedBinder a Ident (Binder a) deriving (Eq, Ord, Show, Functor) - - -extractBinderAnn :: Binder a -> a -extractBinderAnn (NullBinder a) = a -extractBinderAnn (LiteralBinder a _) = a -extractBinderAnn (VarBinder a _) = a -extractBinderAnn (ConstructorBinder a _ _ _) = a -extractBinderAnn (NamedBinder a _ _) = a diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/CSE.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/CSE.hs deleted file mode 100644 index e3e59bdd..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/CSE.hs +++ /dev/null @@ -1,442 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} --- | This module performs limited common subexpression elimination -module Language.PureScript.CoreFn.CSE (optimizeCommonSubexpressions) where - -import Protolude hiding (pass) - -import Control.Lens (At(..), makeLenses, non, view, (%~), (.=), (.~), (<>~), (^.)) -import Control.Monad.Supply (Supply) -import Control.Monad.Supply.Class (MonadSupply) -import Control.Monad.RWS (MonadWriter, RWST, censor, evalRWST, listen, pass, tell) -import Data.Bitraversable (bitraverse) -import Data.Functor.Compose (Compose(..)) -import Data.IntMap.Monoidal qualified as IM -import Data.IntSet qualified as IS -import Data.Map.Strict qualified as M -import Data.Maybe (fromJust) -import Data.Semigroup (Min(..)) -import Data.Semigroup.Generic (GenericSemigroupMonoid(..)) - -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.AST.SourcePos (nullSourceSpan) -import Language.PureScript.Constants.Libs qualified as C -import Language.PureScript.CoreFn.Ann (Ann) -import Language.PureScript.CoreFn.Binders (Binder(..)) -import Language.PureScript.CoreFn.Expr (Bind(..), CaseAlternative(..), Expr(..)) -import Language.PureScript.CoreFn.Meta (Meta(IsSyntheticApp)) -import Language.PureScript.CoreFn.Traversals (everywhereOnValues, traverseCoreFn) -import Language.PureScript.Environment (dictTypeName) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, ProperName(..), Qualified(..), QualifiedBy(..), freshIdent, runIdent, toMaybeModuleName) -import Language.PureScript.PSString (decodeString) - --- | --- `discuss f m` is an action that listens to the output of `m`, passes that --- and its value through `f`, and uses (only) the value of the result to set --- the new value and output. (Any output produced via the monad in `f` is --- ignored, though other monadic effects will hold.) --- -discuss :: MonadWriter w m => ((a, w) -> m (b, w)) -> m a -> m b -discuss f = pass . fmap (second const) . (f <=< listen) - --- | --- Modify the target of an optic in the state with a monadic computation that --- returns some extra information of type `r` in a tuple. --- --- I would prefer that this be a named function, but I don't know what to name --- it. I went with symbols instead because the function that this operator most --- resembles is `(%%=)`, which doesn't have a textual name as far as I know. --- Compare the following (approximate) types: --- --- @ --- (%%=) :: MonadState s m => Lens s s a b -> (a -> (r, b)) -> m r --- (%%<~) :: MonadState s m => Lens s s a b -> (a -> m (r, b)) -> m r --- @ --- --- Replacing the `=` with `<~` was inspired by analogy with the following pair: --- --- @ --- (.=) :: MonadState s m => Lens s s a b -> b -> m () --- (<~) :: MonadState s m => Lens s s a b -> m b -> m () --- @ --- --- I regret any confusion that ensues. --- --- Note that there are two interpretations that could reasonably be expected --- for this type. --- --- @ --- (%%<~) :: MonadState s m => Lens s s a b -> (a -> m (r, b)) -> m r --- @ --- --- One is: --- * Get the focused `a` value from the monad --- * Run the computation --- * Get the new state from the returned monad --- * Take the returned `b` value and set it in the new state --- --- The other is: --- * Get the focused `a` value from the monad --- * Run the computation --- * Take the returned `b` value and set it in the *original* state --- * Put the result into the returned monad --- --- This operator corresponds to the second interpretation. The purpose of this, --- and part of the purpose of having this operator at all instead of composing --- simpler operators, is to enable using the lens only once (on the original --- state) instead of twice (for a get and a set on different states). --- -(%%<~) - :: MonadState s m - => ((a -> Compose m ((,) r) b) -> s -> Compose m ((,) r) s) - -- ^ please read as Lens s s a b - -> (a -> m (r, b)) - -> m r -l %%<~ f = get >>= getCompose . l (Compose . f) >>= state . const -infix 4 %%<~ - --- | --- A PluralityMap is like a weaker multiset: like a multiset, it can hold --- several of the same value, but instead of keeping track of their exact --- counts, it only records whether there is one (False) or more than one --- (True). --- -newtype PluralityMap k = PluralityMap { getPluralityMap :: M.Map k Bool } - -instance Ord k => Semigroup (PluralityMap k) where - PluralityMap l <> PluralityMap r = - let - l' = M.mapWithKey (\k -> (|| k `M.member` r)) l - in PluralityMap $ l' `M.union` r - -instance Ord k => Monoid (PluralityMap k) where - mempty = PluralityMap M.empty - -data BindingType = NonRecursive | Recursive deriving Eq - --- | --- Record summary data about an expression. --- -data CSESummary = CSESummary - { _scopesUsed :: IS.IntSet - -- ^ set of the scope numbers used in this expression - , _noFloatWithin :: Maybe (Min Int) - -- ^ optionally a scope within which this expression is not to be floated - -- (because the expression uses an identifier bound recursively in that - -- scope) - , _plurality :: PluralityMap Ident - -- ^ which floated identifiers are used more than once in this expression - -- (note that a single use inside an Abs will be considered multiple uses, - -- as this pass doesn't know when/how many times an Abs will be executed) - , _newBindings :: IM.MonoidalIntMap [(Ident, (PluralityMap Ident, Expr Ann))] - -- ^ floated bindings, organized by scope number - , _toBeReinlined :: M.Map Ident (Expr Ann) - -- ^ a map of floated identifiers that did not end up getting bound and - -- will need to be reinlined at the end of the pass - } - deriving Generic - deriving (Semigroup, Monoid) via GenericSemigroupMonoid CSESummary - --- | --- Append a value at a given scope depth. --- -addToScope :: Semigroup v => Int -> v -> IM.MonoidalIntMap v -> IM.MonoidalIntMap v -addToScope depth v - = IM.alter (Just . maybe v (<> v)) depth - --- | --- Remove and return an entire scope from a map of bindings. --- -popScope :: Monoid v => Int -> IM.MonoidalIntMap v -> (v, IM.MonoidalIntMap v) -popScope depth - = first fold . IM.updateLookupWithKey (\_ _ -> Nothing) depth - --- | --- Describe the context of an expression. --- -data CSEEnvironment = CSEEnvironment - { _depth :: Int - -- ^ number of enclosing binding scopes (this includes not only Abs, but - -- Let and CaseAlternative bindings) - , _deepestTopLevelScope :: Int - -- ^ number of enclosing binding scopes outside the first Abs; used to - -- decide whether to qualify floated identifiers - , _bound :: M.Map Ident (Int, BindingType) - -- ^ map from identifiers to depth in which they are bound and whether - -- or not the binding is recursive - } - -makeLenses ''CSESummary -makeLenses ''CSEEnvironment - --- | --- Map from the shape of an expression to an identifier created to represent --- that expression, organized by scope depth. --- -type CSEState = IM.MonoidalIntMap (M.Map (Expr ()) Ident) - --- | --- The monad in which CSE takes place. --- -type CSEMonad a = RWST CSEEnvironment CSESummary CSEState Supply a - -type HasCSEReader = MonadReader CSEEnvironment -type HasCSEWriter = MonadWriter CSESummary -type HasCSEState = MonadState CSEState - --- | --- Run a CSEMonad computation; the return value is augmented with a map of --- identifiers that should be replaced in the final expression because they --- didn't end up needing to be floated. --- -runCSEMonad :: CSEMonad a -> Supply (a, M.Map Ident (Expr Ann)) -runCSEMonad x = second (^. toBeReinlined) <$> evalRWST x (CSEEnvironment 0 0 M.empty) IM.empty - --- | --- Mark all expressions floated out of this computation as "plural". This pass --- assumes that any given Abs may be invoked multiple times, so any expressions --- inside the Abs but floated out of it also count as having multiple uses, --- even if they only appear once within the Abs. Consequently, any expressions --- that can be floated out of an Abs won't be reinlined at the end. --- -enterAbs :: HasCSEWriter m => m a -> m a -enterAbs = censor $ plurality %~ PluralityMap . fmap (const True) . getPluralityMap - --- | --- Run the provided computation in a new scope. --- -newScope :: (HasCSEReader m, HasCSEWriter m) => Bool -> (Int -> m a) -> m a -newScope isTopLevel body = local goDeeper $ do - d <- view depth - censor (filterToDepth d) (body d) - where - filterToDepth d - = (scopesUsed %~ IS.filter (< d)) - . (noFloatWithin %~ find (< Min d)) - goDeeper env@CSEEnvironment{..} = - if isTopLevel - then env{ _depth = depth', _deepestTopLevelScope = depth' } - else env{ _depth = depth' } - where - depth' = succ _depth - --- | --- Record a list of identifiers as being bound in the given scope. --- -withBoundIdents :: HasCSEReader m => [Ident] -> (Int, BindingType) -> m a -> m a -withBoundIdents idents t = local (bound %~ flip (foldl' (flip (flip M.insert t))) idents) - --- | --- Run the provided computation in a new scope in which the provided --- identifiers are bound non-recursively. --- -newScopeWithIdents :: (HasCSEReader m, HasCSEWriter m) => Bool -> [Ident] -> m a -> m a -newScopeWithIdents isTopLevel idents = newScope isTopLevel . flip (withBoundIdents idents . (, NonRecursive)) - --- | --- Produce, or retrieve from the state, an identifier for referencing the given --- expression, at and below the given depth. --- -generateIdentFor :: (HasCSEState m, MonadSupply m) => Int -> Expr () -> m (Bool, Ident) -generateIdentFor d e = at d . non mempty . at e %%<~ \case - Nothing -> freshIdent (nameHint e) <&> \ident -> ((True, ident), Just ident) - Just ident -> pure ((False, ident), Just ident) - -- A reminder: as with %%=, the first element of the returned pair is the - -- final result of the expression, and the second element is the value to - -- stuff back through the lens into the state. (The difference is that %%<~ - -- enables doing monadic work in the RHS, namely `freshIdent` here.) - where - nameHint = \case - App _ v1 v2 - | Var _ n <- v1 - , fmap (ProperName . runIdent) n == fmap dictTypeName C.IsSymbol - , Literal _ (ObjectLiteral [(_, Abs _ _ (Literal _ (StringLiteral str)))]) <- v2 - , Just decodedStr <- decodeString str - -> decodedStr <> "IsSymbol" - | otherwise - -> nameHint v1 - Var _ (Qualified _ ident) - | Ident name <- ident -> name - | GenIdent (Just name) _ <- ident -> name - Accessor _ prop _ - | Just decodedProp <- decodeString prop -> decodedProp - _ -> "ref" - -nullAnn :: Ann -nullAnn = (nullSourceSpan, [], Nothing) - --- | --- Use a map to substitute local Vars in a list of Binds. --- -replaceLocals :: M.Map Ident (Expr Ann) -> [Bind Ann] -> [Bind Ann] -replaceLocals m = if M.null m then identity else map f' where - (f', g', _) = everywhereOnValues identity f identity - f e@(Var _ (Qualified _ ident)) = maybe e g' $ ident `M.lookup` m - f e = e - --- | --- Store in the monad a new binding for the given expression, returning a Var --- referencing it. The provided CSESummary will be transformed to reflect the --- replacement. --- -floatExpr - :: (HasCSEReader m, HasCSEState m, MonadSupply m) - => QualifiedBy - -> (Expr Ann, CSESummary) - -> m (Expr Ann, CSESummary) -floatExpr topLevelQB = \case - (e, w@CSESummary{ _noFloatWithin = Nothing, .. }) -> do - let deepestScope = if IS.null _scopesUsed then 0 else IS.findMax _scopesUsed - (isNew, ident) <- generateIdentFor deepestScope (void e) - topLevel <- view deepestTopLevelScope - let qb = if deepestScope > topLevel then ByNullSourcePos else topLevelQB - let w' = w - & (if isNew then newBindings %~ addToScope deepestScope [(ident, (_plurality, e))] else identity) - & plurality .~ PluralityMap (M.singleton ident False) - pure (Var nullAnn (Qualified qb ident), w') - (e, w) -> pure (e, w) - --- | --- Take possession of the Binds intended to be added to the current scope, --- removing them from the state, and return the list of Binds along with --- whatever value is returned by the provided computation. --- -getNewBinds - :: (HasCSEReader m, HasCSEState m, HasCSEWriter m) - => m a - -> m ([Bind Ann], a) -getNewBinds = - discuss $ \(a, w) -> do - d <- view depth - at d .= Nothing - let (floatedHere, w') = newBindings (popScope d) w - pure $ first (, a) $ foldr handleFloat ([], w') floatedHere - where - handleFloat (ident, (p, e)) (bs, w) = - if fromJust . M.lookup ident . getPluralityMap $ w ^. plurality - then (NonRec nullAnn ident e : bs, w') - else (bs, w' & toBeReinlined %~ M.insert ident e) - where w' = w & plurality <>~ p - --- | --- Like getNewBinds, but also stores the Binds in a Let wrapping the provided --- expression. If said expression is already a Let, adds these Binds to that --- Let instead. --- -getNewBindsAsLet - :: (HasCSEReader m, HasCSEWriter m, HasCSEState m) - => m (Expr Ann) - -> m (Expr Ann) -getNewBindsAsLet = fmap (uncurry go) . getNewBinds where - go bs = if null bs then identity else \case - Let a bs' e' -> Let a (bs ++ bs') e' - e' -> Let nullAnn bs e' - --- | --- Feed the Writer part of the monad with the requirements of this name. --- -summarizeName - :: (HasCSEReader m, HasCSEWriter m) - => ModuleName - -> Qualified Ident - -> m () -summarizeName mn (Qualified mn' ident) = do - m <- view bound - let (s, bt) = - fromMaybe (0, NonRecursive) $ - guard (all (== mn) (toMaybeModuleName mn')) *> ident `M.lookup` m - tell $ mempty - & scopesUsed .~ IS.singleton s - & noFloatWithin .~ (guard (bt == Recursive) $> Min s) - --- | --- Collect all the Idents put in scope by a list of Binders. --- -identsFromBinders :: [Binder a] -> [Ident] -identsFromBinders = foldMap identsFromBinder where - identsFromBinder = \case - LiteralBinder _ (ArrayLiteral xs) -> identsFromBinders xs - LiteralBinder _ (ObjectLiteral xs) -> identsFromBinders (map snd xs) - VarBinder _ ident -> [ident] - ConstructorBinder _ _ _ xs -> identsFromBinders xs - NamedBinder _ ident x -> ident : identsFromBinder x - LiteralBinder _ BooleanLiteral{} -> [] - LiteralBinder _ CharLiteral{} -> [] - LiteralBinder _ NumericLiteral{} -> [] - LiteralBinder _ StringLiteral{} -> [] - NullBinder{} -> [] - --- | --- Float synthetic Apps (right now, the only Apps marked as synthetic are type --- class dictionaries being fed to functions with constraints, superclass --- accessors, and instances of IsSymbol) to a new or existing Let as close to --- the top level as possible. --- -optimizeCommonSubexpressions :: ModuleName -> [Bind Ann] -> Supply [Bind Ann] -optimizeCommonSubexpressions mn - = fmap (uncurry (flip replaceLocals)) - . runCSEMonad - . fmap (uncurry (++)) - . getNewBinds - . fmap fst - . handleBinds True (pure ()) - - where - - -- This is the one place (I think?) that keeps this from being a general - -- common subexpression elimination pass. - shouldFloatExpr :: Expr Ann -> Bool - shouldFloatExpr = \case - App (_, _, Just IsSyntheticApp) e _ -> isSimple e - _ -> False - - isSimple :: Expr Ann -> Bool - isSimple = \case - Var{} -> True - Accessor _ _ e -> isSimple e - _ -> False - - handleAndWrapExpr :: Expr Ann -> CSEMonad (Expr Ann) - handleAndWrapExpr = getNewBindsAsLet . handleExpr - - (handleBind, handleExprDefault, handleBinder, _) = traverseCoreFn handleBind handleExpr handleBinder handleCaseAlternative - - topLevelQB = ByModuleName mn - - handleExpr :: Expr Ann -> CSEMonad (Expr Ann) - handleExpr = discuss (ifM (shouldFloatExpr . fst) (floatExpr topLevelQB) pure) . \case - Abs a ident e -> enterAbs $ Abs a ident <$> newScopeWithIdents False [ident] (handleAndWrapExpr e) - v@(Var _ qname) -> summarizeName mn qname $> v - Let a bs e -> uncurry (Let a) <$> handleBinds False (handleExpr e) bs - x -> handleExprDefault x - - handleCaseAlternative :: CaseAlternative Ann -> CSEMonad (CaseAlternative Ann) - handleCaseAlternative (CaseAlternative bs x) = CaseAlternative bs <$> do - newScopeWithIdents False (identsFromBinders bs) $ - bitraverse (traverse $ bitraverse handleAndWrapExpr handleAndWrapExpr) handleAndWrapExpr x - - handleBinds :: forall a. Bool -> CSEMonad a -> [Bind Ann] -> CSEMonad ([Bind Ann], a) - handleBinds isTopLevel = foldr go . fmap pure where - go :: Bind Ann -> CSEMonad ([Bind Ann], a) -> CSEMonad ([Bind Ann], a) - go b inner = case b of - -- For a NonRec Bind, traverse the bound expression in the current scope - -- and then create a new scope for any remaining Binds and/or whatever - -- inner thing all these Binds are applied to. - NonRec a ident e -> do - e' <- handleExpr e - newScopeWithIdents isTopLevel [ident] $ - prependToNewBindsFromInner $ NonRec a ident e' - Rec es -> - -- For a Rec Bind, the bound expressions need a new scope in which all - -- these identifiers are bound recursively; then the remaining Binds - -- and the inner thing can be traversed in the same scope with the same - -- identifiers now bound non-recursively. - newScope isTopLevel $ \d -> do - let idents = map (snd . fst) es - es' <- withBoundIdents idents (d, Recursive) $ traverse (traverse handleExpr) es - withBoundIdents idents (d, NonRecursive) $ - prependToNewBindsFromInner $ Rec es' - - where - - prependToNewBindsFromInner :: Bind Ann -> CSEMonad ([Bind Ann], a) - prependToNewBindsFromInner hd = first (hd :) . join <$> getNewBinds inner diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Desugar.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Desugar.hs deleted file mode 100644 index 34bf08f1..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Desugar.hs +++ /dev/null @@ -1,272 +0,0 @@ -module Language.PureScript.CoreFn.Desugar (moduleToCoreFn) where - -import Prelude -import Protolude (ordNub, orEmpty) - -import Control.Arrow (second) - -import Data.Function (on) -import Data.Maybe (mapMaybe) -import Data.Tuple (swap) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M - -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.AST.SourcePos (pattern NullSourceSpan, SourceSpan(..)) -import Language.PureScript.AST.Traversals (everythingOnValues) -import Language.PureScript.Comments (Comment) -import Language.PureScript.CoreFn.Ann (Ann, ssAnn) -import Language.PureScript.CoreFn.Binders (Binder(..)) -import Language.PureScript.CoreFn.Expr (Bind(..), CaseAlternative(..), Expr(..), Guard) -import Language.PureScript.CoreFn.Meta (ConstructorType(..), Meta(..)) -import Language.PureScript.CoreFn.Module (Module(..)) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType(..), Environment(..), NameKind(..), isDictTypeName, lookupConstructor, lookupValue) -import Language.PureScript.Label (Label(..)) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), getQual) -import Language.PureScript.PSString (PSString) -import Language.PureScript.Types (pattern REmptyKinded, SourceType, Type(..)) -import Language.PureScript.AST qualified as A -import Language.PureScript.Constants.Prim qualified as C - --- | Desugars a module from AST to CoreFn representation. -moduleToCoreFn :: Environment -> A.Module -> Module Ann -moduleToCoreFn _ (A.Module _ _ _ _ Nothing) = - internalError "Module exports were not elaborated before moduleToCoreFn" -moduleToCoreFn env (A.Module modSS coms mn decls (Just exps)) = - let imports = mapMaybe importToCoreFn decls ++ fmap (ssAnn modSS,) (findQualModules decls) - imports' = dedupeImports imports - exps' = ordNub $ concatMap exportToCoreFn exps - reExps = M.map ordNub $ M.unionsWith (++) (mapMaybe (fmap reExportsToCoreFn . toReExportRef) exps) - externs = ordNub $ mapMaybe externToCoreFn decls - decls' = concatMap declToCoreFn decls - in Module modSS coms mn (spanName modSS) imports' exps' reExps externs decls' - where - -- Creates a map from a module name to the re-export references defined in - -- that module. - reExportsToCoreFn :: (ModuleName, A.DeclarationRef) -> M.Map ModuleName [Ident] - reExportsToCoreFn (mn', ref') = M.singleton mn' (exportToCoreFn ref') - - toReExportRef :: A.DeclarationRef -> Maybe (ModuleName, A.DeclarationRef) - toReExportRef (A.ReExportRef _ src ref) = - fmap - (, ref) - (A.exportSourceImportedFrom src) - toReExportRef _ = Nothing - - -- Remove duplicate imports - dedupeImports :: [(Ann, ModuleName)] -> [(Ann, ModuleName)] - dedupeImports = fmap swap . M.toList . M.fromListWith const . fmap swap - - ssA :: SourceSpan -> Ann - ssA ss = (ss, [], Nothing) - - -- Desugars member declarations from AST to CoreFn representation. - declToCoreFn :: A.Declaration -> [Bind Ann] - declToCoreFn (A.DataDeclaration (ss, com) Newtype _ _ [ctor]) = - [NonRec (ss, [], declMeta) (properToIdent $ A.dataCtorName ctor) $ - Abs (ss, com, Just IsNewtype) (Ident "x") (Var (ssAnn ss) $ Qualified ByNullSourcePos (Ident "x"))] - where - declMeta = isDictTypeName (A.dataCtorName ctor) `orEmpty` IsTypeClassConstructor - declToCoreFn d@(A.DataDeclaration _ Newtype _ _ _) = - error $ "Found newtype with multiple constructors: " ++ show d - declToCoreFn (A.DataDeclaration (ss, com) Data tyName _ ctors) = - flip fmap ctors $ \ctorDecl -> - let - ctor = A.dataCtorName ctorDecl - (_, _, _, fields) = lookupConstructor env (Qualified (ByModuleName mn) ctor) - in NonRec (ssA ss) (properToIdent ctor) $ Constructor (ss, com, Nothing) tyName ctor fields - declToCoreFn (A.DataBindingGroupDeclaration ds) = - concatMap declToCoreFn ds - declToCoreFn (A.ValueDecl (ss, com) name _ _ [A.MkUnguarded e]) = - [NonRec (ssA ss) name (exprToCoreFn ss com Nothing e)] - declToCoreFn (A.BindingGroupDeclaration ds) = - [Rec . NEL.toList $ fmap (\(((ss, com), name), _, e) -> ((ssA ss, name), exprToCoreFn ss com Nothing e)) ds] - declToCoreFn _ = [] - - -- Desugars expressions from AST to CoreFn representation. - exprToCoreFn :: SourceSpan -> [Comment] -> Maybe SourceType -> A.Expr -> Expr Ann - exprToCoreFn _ com _ (A.Literal ss lit) = - Literal (ss, com, Nothing) (fmap (exprToCoreFn ss com Nothing) lit) - exprToCoreFn ss com _ (A.Accessor name v) = - Accessor (ss, com, Nothing) name (exprToCoreFn ss [] Nothing v) - exprToCoreFn ss com ty (A.ObjectUpdate obj vs) = - ObjectUpdate (ss, com, Nothing) (exprToCoreFn ss [] Nothing obj) (ty >>= unchangedRecordFields (fmap fst vs)) $ fmap (second (exprToCoreFn ss [] Nothing)) vs - where - -- Return the unchanged labels of a closed record, or Nothing for other types or open records. - unchangedRecordFields :: [PSString] -> Type a -> Maybe [PSString] - unchangedRecordFields updated (TypeApp _ (TypeConstructor _ C.Record) row) = - collect row - where - collect :: Type a -> Maybe [PSString] - collect (REmptyKinded _ _) = Just [] - collect (RCons _ (Label l) _ r) = (if l `elem` updated then id else (l :)) <$> collect r - collect _ = Nothing - unchangedRecordFields _ _ = Nothing - exprToCoreFn ss com _ (A.Abs (A.VarBinder _ name) v) = - Abs (ss, com, Nothing) name (exprToCoreFn ss [] Nothing v) - exprToCoreFn _ _ _ (A.Abs _ _) = - internalError "Abs with Binder argument was not desugared before exprToCoreFn mn" - exprToCoreFn ss com _ (A.App v1 v2) = - App (ss, com, (isDictCtor v1 || isSynthetic v2) `orEmpty` IsSyntheticApp) v1' v2' - where - v1' = exprToCoreFn ss [] Nothing v1 - v2' = exprToCoreFn ss [] Nothing v2 - isDictCtor = \case - A.Constructor _ (Qualified _ name) -> isDictTypeName name - _ -> False - isSynthetic = \case - A.App v3 v4 -> isDictCtor v3 || isSynthetic v3 && isSynthetic v4 - A.Accessor _ v3 -> isSynthetic v3 - A.Var NullSourceSpan _ -> True - A.Unused{} -> True - _ -> False - exprToCoreFn ss com _ (A.Unused _) = - Var (ss, com, Nothing) C.I_undefined - exprToCoreFn _ com _ (A.Var ss ident) = - Var (ss, com, getValueMeta ident) ident - exprToCoreFn ss com _ (A.IfThenElse v1 v2 v3) = - Case (ss, com, Nothing) [exprToCoreFn ss [] Nothing v1] - [ CaseAlternative [LiteralBinder (ssAnn ss) $ BooleanLiteral True] - (Right $ exprToCoreFn ss [] Nothing v2) - , CaseAlternative [NullBinder (ssAnn ss)] - (Right $ exprToCoreFn ss [] Nothing v3) ] - exprToCoreFn _ com _ (A.Constructor ss name) = - Var (ss, com, Just $ getConstructorMeta name) $ fmap properToIdent name - exprToCoreFn ss com _ (A.Case vs alts) = - Case (ss, com, Nothing) (fmap (exprToCoreFn ss [] Nothing) vs) (fmap (altToCoreFn ss) alts) - exprToCoreFn ss com _ (A.TypedValue _ v ty) = - exprToCoreFn ss com (Just ty) v - exprToCoreFn ss com _ (A.Let w ds v) = - Let (ss, com, getLetMeta w) (concatMap declToCoreFn ds) (exprToCoreFn ss [] Nothing v) - exprToCoreFn _ com ty (A.PositionedValue ss com1 v) = - exprToCoreFn ss (com ++ com1) ty v - exprToCoreFn _ _ _ e = - error $ "Unexpected value in exprToCoreFn mn: " ++ show e - - -- Desugars case alternatives from AST to CoreFn representation. - altToCoreFn :: SourceSpan -> A.CaseAlternative -> CaseAlternative Ann - altToCoreFn ss (A.CaseAlternative bs vs) = CaseAlternative (map (binderToCoreFn ss []) bs) (go vs) - where - go :: [A.GuardedExpr] -> Either [(Guard Ann, Expr Ann)] (Expr Ann) - go [A.MkUnguarded e] - = Right (exprToCoreFn ss [] Nothing e) - go gs - = Left [ (exprToCoreFn ss [] Nothing cond, exprToCoreFn ss [] Nothing e) - | A.GuardedExpr g e <- gs - , let cond = guardToExpr g - ] - - guardToExpr [A.ConditionGuard cond] = cond - guardToExpr _ = internalError "Guard not correctly desugared" - - -- Desugars case binders from AST to CoreFn representation. - binderToCoreFn :: SourceSpan -> [Comment] -> A.Binder -> Binder Ann - binderToCoreFn _ com (A.LiteralBinder ss lit) = - LiteralBinder (ss, com, Nothing) (fmap (binderToCoreFn ss com) lit) - binderToCoreFn ss com A.NullBinder = - NullBinder (ss, com, Nothing) - binderToCoreFn _ com (A.VarBinder ss name) = - VarBinder (ss, com, Nothing) name - binderToCoreFn _ com (A.ConstructorBinder ss dctor@(Qualified mn' _) bs) = - let (_, tctor, _, _) = lookupConstructor env dctor - in ConstructorBinder (ss, com, Just $ getConstructorMeta dctor) (Qualified mn' tctor) dctor (fmap (binderToCoreFn ss []) bs) - binderToCoreFn _ com (A.NamedBinder ss name b) = - NamedBinder (ss, com, Nothing) name (binderToCoreFn ss [] b) - binderToCoreFn _ com (A.PositionedBinder ss com1 b) = - binderToCoreFn ss (com ++ com1) b - binderToCoreFn ss com (A.TypedBinder _ b) = - binderToCoreFn ss com b - binderToCoreFn _ _ A.OpBinder{} = - internalError "OpBinder should have been desugared before binderToCoreFn" - binderToCoreFn _ _ A.BinaryNoParensBinder{} = - internalError "BinaryNoParensBinder should have been desugared before binderToCoreFn" - binderToCoreFn _ _ A.ParensInBinder{} = - internalError "ParensInBinder should have been desugared before binderToCoreFn" - - -- Gets metadata for let bindings. - getLetMeta :: A.WhereProvenance -> Maybe Meta - getLetMeta A.FromWhere = Just IsWhere - getLetMeta A.FromLet = Nothing - - -- Gets metadata for values. - getValueMeta :: Qualified Ident -> Maybe Meta - getValueMeta name = - case lookupValue env name of - Just (_, External, _) -> Just IsForeign - _ -> Nothing - - -- Gets metadata for data constructors. - getConstructorMeta :: Qualified (ProperName 'ConstructorName) -> Meta - getConstructorMeta ctor = - case lookupConstructor env ctor of - (Newtype, _, _, _) -> IsNewtype - dc@(Data, _, _, fields) -> - let constructorType = if numConstructors (ctor, dc) == 1 then ProductType else SumType - in IsConstructor constructorType fields - where - - numConstructors - :: (Qualified (ProperName 'ConstructorName), (DataDeclType, ProperName 'TypeName, SourceType, [Ident])) - -> Int - numConstructors ty = length $ filter (((==) `on` typeConstructor) ty) $ M.toList $ dataConstructors env - - typeConstructor - :: (Qualified (ProperName 'ConstructorName), (DataDeclType, ProperName 'TypeName, SourceType, [Ident])) - -> (ModuleName, ProperName 'TypeName) - typeConstructor (Qualified (ByModuleName mn') _, (_, tyCtor, _, _)) = (mn', tyCtor) - typeConstructor _ = internalError "Invalid argument to typeConstructor" - --- | Find module names from qualified references to values. This is used to --- ensure instances are imported from any module that is referenced by the --- current module, not just from those that are imported explicitly (#667). -findQualModules :: [A.Declaration] -> [ModuleName] -findQualModules decls = - let (f, _, _, _, _) = everythingOnValues (++) fqDecls fqValues fqBinders (const []) (const []) - in f `concatMap` decls - where - fqDecls :: A.Declaration -> [ModuleName] - fqDecls (A.TypeInstanceDeclaration _ _ _ _ _ _ q _ _) = getQual' q - fqDecls (A.ValueFixityDeclaration _ _ q _) = getQual' q - fqDecls (A.TypeFixityDeclaration _ _ q _) = getQual' q - fqDecls _ = [] - - fqValues :: A.Expr -> [ModuleName] - fqValues (A.Var _ q) = getQual' q - fqValues (A.Constructor _ q) = getQual' q - fqValues _ = [] - - fqBinders :: A.Binder -> [ModuleName] - fqBinders (A.ConstructorBinder _ q _) = getQual' q - fqBinders _ = [] - - getQual' :: Qualified a -> [ModuleName] - getQual' = maybe [] return . getQual - --- | Desugars import declarations from AST to CoreFn representation. -importToCoreFn :: A.Declaration -> Maybe (Ann, ModuleName) -importToCoreFn (A.ImportDeclaration (ss, com) name _ _) = Just ((ss, com, Nothing), name) -importToCoreFn _ = Nothing - --- | Desugars foreign declarations from AST to CoreFn representation. -externToCoreFn :: A.Declaration -> Maybe Ident -externToCoreFn (A.ExternDeclaration _ name _) = Just name -externToCoreFn _ = Nothing - --- | Desugars export declarations references from AST to CoreFn representation. --- CoreFn modules only export values, so all data constructors, instances and --- values are flattened into one list. -exportToCoreFn :: A.DeclarationRef -> [Ident] -exportToCoreFn (A.TypeRef _ _ (Just dctors)) = fmap properToIdent dctors -exportToCoreFn (A.TypeRef _ _ Nothing) = [] -exportToCoreFn (A.TypeOpRef _ _) = [] -exportToCoreFn (A.ValueRef _ name) = [name] -exportToCoreFn (A.ValueOpRef _ _) = [] -exportToCoreFn (A.TypeClassRef _ _) = [] -exportToCoreFn (A.TypeInstanceRef _ name _) = [name] -exportToCoreFn (A.ModuleRef _ _) = [] -exportToCoreFn (A.ReExportRef _ _ _) = [] - --- | Converts a ProperName to an Ident. -properToIdent :: ProperName a -> Ident -properToIdent = Ident . runProperName diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Expr.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Expr.hs deleted file mode 100644 index 20ab3330..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Expr.hs +++ /dev/null @@ -1,122 +0,0 @@ --- | --- The core functional representation --- -module Language.PureScript.CoreFn.Expr where - -import Prelude - -import Control.Arrow ((***)) - -import Language.PureScript.AST.Literals (Literal) -import Language.PureScript.CoreFn.Binders (Binder) -import Language.PureScript.Names (Ident, ProperName, ProperNameType(..), Qualified) -import Language.PureScript.PSString (PSString) - --- | --- Data type for expressions and terms --- -data Expr a - -- | - -- A literal value - -- - = Literal a (Literal (Expr a)) - -- | - -- A data constructor (type name, constructor name, field names) - -- - | Constructor a (ProperName 'TypeName) (ProperName 'ConstructorName) [Ident] - -- | - -- A record property accessor - -- - | Accessor a PSString (Expr a) - -- | - -- Partial record update (original value, fields to copy (if known), fields to update) - -- - | ObjectUpdate a (Expr a) (Maybe [PSString]) [(PSString, Expr a)] - -- | - -- Function introduction - -- - | Abs a Ident (Expr a) - -- | - -- Function application - -- - | App a (Expr a) (Expr a) - -- | - -- Variable - -- - | Var a (Qualified Ident) - -- | - -- A case expression - -- - | Case a [Expr a] [CaseAlternative a] - -- | - -- A let binding - -- - | Let a [Bind a] (Expr a) - deriving (Eq, Ord, Show, Functor) - --- | --- A let or module binding. --- -data Bind a - -- | - -- Non-recursive binding for a single value - -- - = NonRec a Ident (Expr a) - -- | - -- Mutually recursive binding group for several values - -- - | Rec [((a, Ident), Expr a)] deriving (Eq, Ord, Show, Functor) - --- | --- A guard is just a boolean-valued expression that appears alongside a set of binders --- -type Guard a = Expr a - --- | --- An alternative in a case statement --- -data CaseAlternative a = CaseAlternative - { -- | - -- A collection of binders with which to match the inputs - -- - caseAlternativeBinders :: [Binder a] - -- | - -- The result expression or a collect of guarded expressions - -- - , caseAlternativeResult :: Either [(Guard a, Expr a)] (Expr a) - } deriving (Eq, Ord, Show) - -instance Functor CaseAlternative where - - fmap f (CaseAlternative cabs car) = CaseAlternative - (fmap (fmap f) cabs) - (either (Left . fmap (fmap f *** fmap f)) (Right . fmap f) car) - --- | --- Extract the annotation from a term --- -extractAnn :: Expr a -> a -extractAnn (Literal a _) = a -extractAnn (Constructor a _ _ _) = a -extractAnn (Accessor a _ _) = a -extractAnn (ObjectUpdate a _ _ _) = a -extractAnn (Abs a _ _) = a -extractAnn (App a _ _) = a -extractAnn (Var a _) = a -extractAnn (Case a _ _) = a -extractAnn (Let a _ _) = a - - --- | --- Modify the annotation on a term --- -modifyAnn :: (a -> a) -> Expr a -> Expr a -modifyAnn f (Literal a b) = Literal (f a) b -modifyAnn f (Constructor a b c d) = Constructor (f a) b c d -modifyAnn f (Accessor a b c) = Accessor (f a) b c -modifyAnn f (ObjectUpdate a b c d) = ObjectUpdate (f a) b c d -modifyAnn f (Abs a b c) = Abs (f a) b c -modifyAnn f (App a b c) = App (f a) b c -modifyAnn f (Var a b) = Var (f a) b -modifyAnn f (Case a b c) = Case (f a) b c -modifyAnn f (Let a b c) = Let (f a) b c diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/FromJSON.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/FromJSON.hs deleted file mode 100644 index d0426b6f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/FromJSON.hs +++ /dev/null @@ -1,319 +0,0 @@ --- | --- Read the core functional representation from JSON format --- - -module Language.PureScript.CoreFn.FromJSON - ( moduleFromJSON - , parseVersion' - ) where - -import Prelude - -import Control.Applicative ((<|>)) - -import Data.Aeson (FromJSON(..), Object, Value(..), withObject, withText, (.:)) -import Data.Aeson.Types (Parser, listParser) -import Data.Map.Strict qualified as M -import Data.Text (Text) -import Data.Text qualified as T -import Data.Vector qualified as V -import Data.Version (Version, parseVersion) - -import Language.PureScript.AST.SourcePos (SourceSpan(..)) -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.CoreFn.Ann (Ann) -import Language.PureScript.CoreFn (Bind(..), Binder(..), CaseAlternative(..), ConstructorType(..), Expr(..), Guard, Meta(..), Module(..)) -import Language.PureScript.Names (Ident(..), ModuleName(..), ProperName(..), Qualified(..), QualifiedBy(..), unusedIdent) -import Language.PureScript.PSString (PSString) - -import Text.ParserCombinators.ReadP (readP_to_S) - -parseVersion' :: String -> Maybe Version -parseVersion' str = - case filter (null . snd) $ readP_to_S parseVersion str of - [(vers, "")] -> Just vers - _ -> Nothing - -constructorTypeFromJSON :: Value -> Parser ConstructorType -constructorTypeFromJSON v = do - t <- parseJSON v - case t of - "ProductType" -> return ProductType - "SumType" -> return SumType - _ -> fail ("not recognized ConstructorType: " ++ T.unpack t) - -metaFromJSON :: Value -> Parser (Maybe Meta) -metaFromJSON Null = return Nothing -metaFromJSON v = withObject "Meta" metaFromObj v - where - metaFromObj o = do - type_ <- o .: "metaType" - case type_ of - "IsConstructor" -> isConstructorFromJSON o - "IsNewtype" -> return $ Just IsNewtype - "IsTypeClassConstructor" - -> return $ Just IsTypeClassConstructor - "IsForeign" -> return $ Just IsForeign - "IsWhere" -> return $ Just IsWhere - "IsSyntheticApp" - -> return $ Just IsSyntheticApp - _ -> fail ("not recognized Meta: " ++ T.unpack type_) - - isConstructorFromJSON o = do - ct <- o .: "constructorType" >>= constructorTypeFromJSON - is <- o .: "identifiers" >>= listParser identFromJSON - return $ Just (IsConstructor ct is) - -annFromJSON :: FilePath -> Value -> Parser Ann -annFromJSON modulePath = withObject "Ann" annFromObj - where - annFromObj o = do - ss <- o .: "sourceSpan" >>= sourceSpanFromJSON modulePath - mm <- o .: "meta" >>= metaFromJSON - return (ss, [], mm) - -sourceSpanFromJSON :: FilePath -> Value -> Parser SourceSpan -sourceSpanFromJSON modulePath = withObject "SourceSpan" $ \o -> - SourceSpan modulePath <$> - o .: "start" <*> - o .: "end" - -literalFromJSON :: (Value -> Parser a) -> Value -> Parser (Literal a) -literalFromJSON t = withObject "Literal" literalFromObj - where - literalFromObj o = do - type_ <- o .: "literalType" :: Parser Text - case type_ of - "IntLiteral" -> NumericLiteral . Left <$> o .: "value" - "NumberLiteral" -> NumericLiteral . Right <$> o .: "value" - "StringLiteral" -> StringLiteral <$> o .: "value" - "CharLiteral" -> CharLiteral <$> o .: "value" - "BooleanLiteral" -> BooleanLiteral <$> o .: "value" - "ArrayLiteral" -> parseArrayLiteral o - "ObjectLiteral" -> parseObjectLiteral o - _ -> fail ("error parsing Literal: " ++ show o) - - parseArrayLiteral o = do - val <- o .: "value" - as <- mapM t (V.toList val) - return $ ArrayLiteral as - - parseObjectLiteral o = do - val <- o .: "value" - ObjectLiteral <$> recordFromJSON t val - -identFromJSON :: Value -> Parser Ident -identFromJSON = withText "Ident" $ \case - ident | ident == unusedIdent -> pure UnusedIdent - | otherwise -> pure $ Ident ident - -properNameFromJSON :: Value -> Parser (ProperName a) -properNameFromJSON = fmap ProperName . parseJSON - -qualifiedFromJSON :: (Text -> a) -> Value -> Parser (Qualified a) -qualifiedFromJSON f = withObject "Qualified" qualifiedFromObj - where - qualifiedFromObj o = - qualifiedByModuleFromObj o <|> qualifiedBySourcePosFromObj o - qualifiedByModuleFromObj o = do - mn <- o .: "moduleName" >>= moduleNameFromJSON - i <- o .: "identifier" >>= withText "Ident" (return . f) - pure $ Qualified (ByModuleName mn) i - qualifiedBySourcePosFromObj o = do - ss <- o .: "sourcePos" - i <- o .: "identifier" >>= withText "Ident" (return . f) - pure $ Qualified (BySourcePos ss) i - -moduleNameFromJSON :: Value -> Parser ModuleName -moduleNameFromJSON v = ModuleName . T.intercalate "." <$> listParser parseJSON v - -moduleFromJSON :: Value -> Parser (Version, Module Ann) -moduleFromJSON = withObject "Module" moduleFromObj - where - moduleFromObj o = do - version <- o .: "builtWith" >>= versionFromJSON - moduleName <- o .: "moduleName" >>= moduleNameFromJSON - modulePath <- o .: "modulePath" - moduleSourceSpan <- o .: "sourceSpan" >>= sourceSpanFromJSON modulePath - moduleImports <- o .: "imports" >>= listParser (importFromJSON modulePath) - moduleExports <- o .: "exports" >>= listParser identFromJSON - moduleReExports <- o .: "reExports" >>= reExportsFromJSON - moduleDecls <- o .: "decls" >>= listParser (bindFromJSON modulePath) - moduleForeign <- o .: "foreign" >>= listParser identFromJSON - moduleComments <- o .: "comments" >>= listParser parseJSON - return (version, Module {..}) - - versionFromJSON :: String -> Parser Version - versionFromJSON v = - case parseVersion' v of - Just r -> return r - Nothing -> fail "failed parsing purs version" - - importFromJSON :: FilePath -> Value -> Parser (Ann, ModuleName) - importFromJSON modulePath = withObject "Import" - (\o -> do - ann <- o .: "annotation" >>= annFromJSON modulePath - mn <- o .: "moduleName" >>= moduleNameFromJSON - return (ann, mn)) - - reExportsFromJSON :: Value -> Parser (M.Map ModuleName [Ident]) - reExportsFromJSON = fmap (M.map (map Ident)) . parseJSON - -bindFromJSON :: FilePath -> Value -> Parser (Bind Ann) -bindFromJSON modulePath = withObject "Bind" bindFromObj - where - bindFromObj :: Object -> Parser (Bind Ann) - bindFromObj o = do - type_ <- o .: "bindType" :: Parser Text - case type_ of - "NonRec" -> (uncurry . uncurry) NonRec <$> bindFromObj' o - "Rec" -> Rec <$> (o .: "binds" >>= listParser (withObject "Bind" bindFromObj')) - _ -> fail ("not recognized bind type \"" ++ T.unpack type_ ++ "\"") - - bindFromObj' :: Object -> Parser ((Ann, Ident), Expr Ann) - bindFromObj' o = do - a <- o .: "annotation" >>= annFromJSON modulePath - i <- o .: "identifier" >>= identFromJSON - e <- o .: "expression" >>= exprFromJSON modulePath - return ((a, i), e) - -recordFromJSON :: (Value -> Parser a) -> Value -> Parser [(PSString, a)] -recordFromJSON p = listParser parsePair - where - parsePair v = do - (l, v') <- parseJSON v :: Parser (PSString, Value) - a <- p v' - return (l, a) - -exprFromJSON :: FilePath -> Value -> Parser (Expr Ann) -exprFromJSON modulePath = withObject "Expr" exprFromObj - where - exprFromObj o = do - type_ <- o .: "type" - case type_ of - "Var" -> varFromObj o - "Literal" -> literalExprFromObj o - "Constructor" -> constructorFromObj o - "Accessor" -> accessorFromObj o - "ObjectUpdate" -> objectUpdateFromObj o - "Abs" -> absFromObj o - "App" -> appFromObj o - "Case" -> caseFromObj o - "Let" -> letFromObj o - _ -> fail ("not recognized expression type: \"" ++ T.unpack type_ ++ "\"") - - varFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - qi <- o .: "value" >>= qualifiedFromJSON Ident - return $ Var ann qi - - literalExprFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - lit <- o .: "value" >>= literalFromJSON (exprFromJSON modulePath) - return $ Literal ann lit - - constructorFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - tyn <- o .: "typeName" >>= properNameFromJSON - con <- o .: "constructorName" >>= properNameFromJSON - is <- o .: "fieldNames" >>= listParser identFromJSON - return $ Constructor ann tyn con is - - accessorFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - f <- o .: "fieldName" - e <- o .: "expression" >>= exprFromJSON modulePath - return $ Accessor ann f e - - objectUpdateFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - e <- o .: "expression" >>= exprFromJSON modulePath - copy <- o .: "copy" >>= parseJSON - us <- o .: "updates" >>= recordFromJSON (exprFromJSON modulePath) - return $ ObjectUpdate ann e copy us - - absFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - idn <- o .: "argument" >>= identFromJSON - e <- o .: "body" >>= exprFromJSON modulePath - return $ Abs ann idn e - - appFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - e <- o .: "abstraction" >>= exprFromJSON modulePath - e' <- o .: "argument" >>= exprFromJSON modulePath - return $ App ann e e' - - caseFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - cs <- o .: "caseExpressions" >>= listParser (exprFromJSON modulePath) - cas <- o .: "caseAlternatives" >>= listParser (caseAlternativeFromJSON modulePath) - return $ Case ann cs cas - - letFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - bs <- o .: "binds" >>= listParser (bindFromJSON modulePath) - e <- o .: "expression" >>= exprFromJSON modulePath - return $ Let ann bs e - -caseAlternativeFromJSON :: FilePath -> Value -> Parser (CaseAlternative Ann) -caseAlternativeFromJSON modulePath = withObject "CaseAlternative" caseAlternativeFromObj - where - caseAlternativeFromObj o = do - bs <- o .: "binders" >>= listParser (binderFromJSON modulePath) - isGuarded <- o .: "isGuarded" - if isGuarded - then do - es <- o .: "expressions" >>= listParser parseResultWithGuard - return $ CaseAlternative bs (Left es) - else do - e <- o .: "expression" >>= exprFromJSON modulePath - return $ CaseAlternative bs (Right e) - - parseResultWithGuard :: Value -> Parser (Guard Ann, Expr Ann) - parseResultWithGuard = withObject "parseCaseWithGuards" $ - \o -> do - g <- o .: "guard" >>= exprFromJSON modulePath - e <- o .: "expression" >>= exprFromJSON modulePath - return (g, e) - -binderFromJSON :: FilePath -> Value -> Parser (Binder Ann) -binderFromJSON modulePath = withObject "Binder" binderFromObj - where - binderFromObj o = do - type_ <- o .: "binderType" - case type_ of - "NullBinder" -> nullBinderFromObj o - "VarBinder" -> varBinderFromObj o - "LiteralBinder" -> literalBinderFromObj o - "ConstructorBinder" -> constructorBinderFromObj o - "NamedBinder" -> namedBinderFromObj o - _ -> fail ("not recognized binder: \"" ++ T.unpack type_ ++ "\"") - - - nullBinderFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - return $ NullBinder ann - - varBinderFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - idn <- o .: "identifier" >>= identFromJSON - return $ VarBinder ann idn - - literalBinderFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - lit <- o .: "literal" >>= literalFromJSON (binderFromJSON modulePath) - return $ LiteralBinder ann lit - - constructorBinderFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - tyn <- o .: "typeName" >>= qualifiedFromJSON ProperName - con <- o .: "constructorName" >>= qualifiedFromJSON ProperName - bs <- o .: "binders" >>= listParser (binderFromJSON modulePath) - return $ ConstructorBinder ann tyn con bs - - namedBinderFromObj o = do - ann <- o .: "annotation" >>= annFromJSON modulePath - n <- o .: "identifier" >>= identFromJSON - b <- o .: "binder" >>= binderFromJSON modulePath - return $ NamedBinder ann n b diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Laziness.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Laziness.hs deleted file mode 100644 index 9941fd41..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Laziness.hs +++ /dev/null @@ -1,568 +0,0 @@ -module Language.PureScript.CoreFn.Laziness - ( applyLazinessTransform - ) where - -import Protolude hiding (force) -import Protolude.Unsafe (unsafeHead) - -import Control.Arrow ((&&&)) -import Data.Array qualified as A -import Data.Coerce (coerce) -import Data.Graph (SCC(..), stronglyConnComp) -import Data.List (foldl1', (!!)) -import Data.IntMap.Monoidal qualified as IM -import Data.IntSet qualified as IS -import Data.Map.Monoidal qualified as M -import Data.Semigroup (Max(..)) -import Data.Set qualified as S - -import Language.PureScript.AST.SourcePos (SourcePos(..), SourceSpan(..), nullSourceSpan) -import Language.PureScript.Constants.Libs qualified as C -import Language.PureScript.CoreFn (Ann, Bind, Expr(..), Literal(..), Meta(..), ssAnn, traverseCoreFn) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), InternalIdentData(..), ModuleName, Qualified(..), QualifiedBy(..), runIdent, runModuleName, toMaybeModuleName) -import Language.PureScript.PSString (mkString) - --- This module is responsible for ensuring that the bindings in recursive --- binding groups are initialized in a valid order, introducing run-time --- laziness and initialization checks as necessary. --- --- PureScript is a call-by-value language with strict data constructors, this --- transformation notwithstanding. The only laziness introduced here is in the --- initialization of a binding. PureScript is uninterested in the order in --- which bindings are written by the user. The compiler has always attempted to --- emit the bindings in an order that makes sense for the backend, but without --- this transformation, recursive bindings are emitted in an arbitrary order, --- which can cause unexpected behavior at run time if a binding is dereferenced --- before it has initialized. --- --- To prevent unexpected errors, this transformation does a syntax-driven --- analysis of a single recursive binding group to attempt to statically order --- the bindings, and when that fails, falls back to lazy initializers that will --- succeed or fail deterministically with a clear error at run time. --- --- Example: --- --- x = f \_ -> --- x --- --- becomes (with some details of the $runtime_lazy function elided): --- --- -- the binding of x has been rewritten as a lazy initializer --- $lazy_x = $runtime_lazy \_ -> --- f \_ -> --- $lazy_x 2 -- the reference to x has been rewritten as a force call --- x = $lazy_x 1 --- --- Central to this analysis are the concepts of delay and force, which are --- attributes given to every subexpression in the binding group. Delay and --- force are defined by the following traversal. This traversal is used twice: --- once to collect all the references made by each binding in the group, and --- then again to rewrite some references to force calls. (The implications of --- delay and force on initialization order are specified later.) - --- | --- Visits every `Var` in an expression with the provided function, including --- the amount of delay and force applied to that `Var`, and substitutes the --- result back into the tree (propagating an `Applicative` effect). --- --- Delay is a non-negative integer that represents the number of lambdas that --- enclose an expression. Force is a non-negative integer that represents the --- number of values that are being applied to an expression. Delay is always --- statically determinable, but force can be *unknown*, so it's represented --- here with a Maybe. In a function application `f a b`, `f` has force 2, but --- `a` and `b` have unknown force--it depends on what `f` does with them. --- --- The rules of assigning delay and force are simple: --- * The expressions that are assigned to bindings in this group have --- delay 0, force 0. --- * In a function application, the function expression has force 1 higher --- than the force of the application expression, and the argument --- expression has unknown force. --- * UNLESS this argument is being directly provided to a constructor (in --- other words, the function expression is either a constructor itself or --- a constructor that has already been partially applied), in which case --- the force of both subexpressions is unchanged. We can assume that --- constructors don't apply any additional force to their arguments. --- * If the force of a lambda is zero, the delay of the body of the lambda is --- incremented; otherwise, the force of the body of the lambda is --- decremented. (Applying one argument to a lambda cancels out one unit of --- delay.) --- * In the argument of a Case and the bindings of a Let, force is unknown. --- * Everywhere else, preserve the delay and force of the enclosing --- expression. --- --- Here are some illustrative examples of the above rules. We will use a --- pseudocode syntax to annotate a subexpression with delay and force: --- `expr#d!f` means `expr` has delay d and force f. `!*` is used to denote --- unknown force. --- --- x = y#0!0 --- x = y#0!2 a#0!* b#0!* --- x = (\_ -> y#1!0)#0!0 --- x = \_ _ -> y#2!1 a#2!* --- x = (\_ -> y#0!0)#0!1 z#0!* --- x = Just { a: a#0!0, b: b#0!0 } --- x = let foo = (y#1!* a b#1!*)#1!* in foo + 1 --- --- (Note that this analysis is quite ignorant of any actual control flow --- choices made at run time. It doesn't even track what happens to a reference --- after it has been locally bound by a Let or Case. Instead, it just assumes --- the worst--once locally bound to a new name, it imagines that absolutely --- anything could happen to that new name and thus to the underlying reference. --- But the value-to-weight ratio of this approach is perhaps surprisingly --- high.) --- --- Every subexpression gets a delay and a force, but we are only interested --- in references to other bindings in the binding group, so the traversal only --- exposes `Var`s to the provided function. --- -onVarsWithDelayAndForce :: forall f. Applicative f => (Int -> Maybe Int -> Ann -> Qualified Ident -> f (Expr Ann)) -> Expr Ann -> f (Expr Ann) -onVarsWithDelayAndForce f = snd . go 0 $ Just 0 - where - go :: Int -> Maybe Int -> (Bind Ann -> f (Bind Ann), Expr Ann -> f (Expr Ann)) - go delay force = (handleBind, handleExpr') - where - (handleBind, handleExpr, handleBinder, handleCaseAlternative) = traverseCoreFn handleBind handleExpr' handleBinder handleCaseAlternative - handleExpr' = \case - Var a i -> f delay force a i - Abs a i e -> Abs a i <$> snd (if force == Just 0 then go (succ delay) force else go delay $ fmap pred force) e - -- A clumsy hack to preserve TCO in a particular idiom of unsafePartial once seen in Data.Map.Internal, possibly still used elsewhere. - App a1 e1@(Var _ C.I_unsafePartial) (Abs a2 i e2) -> App a1 e1 . Abs a2 i <$> handleExpr' e2 - App a e1 e2 -> - -- `handleApp` is just to handle the constructor application exception - -- somewhat gracefully (i.e., without requiring a deep inspection of - -- the function expression at every step). If we didn't care about - -- constructors, this could have been simply: - -- App a <$> snd (go delay (fmap succ force)) e1 <*> snd (go delay Nothing) e2 - handleApp 1 [(a, e2)] e1 - Case a vs alts -> Case a <$> traverse (snd $ go delay Nothing) vs <*> traverse handleCaseAlternative alts - Let a ds e -> Let a <$> traverse (fst $ go delay Nothing) ds <*> handleExpr' e - other -> handleExpr other - - handleApp len args = \case - App a e1 e2 -> handleApp (len + 1) ((a, e2) : args) e1 - Var a@(_, _, Just meta) i | isConstructorLike meta - -> foldl (\e1 (a2, e2) -> App a2 <$> e1 <*> handleExpr' e2) (f delay force a i) args - e -> foldl (\e1 (a2, e2) -> App a2 <$> e1 <*> snd (go delay Nothing) e2) (snd (go delay (fmap (+ len) force)) e) args - isConstructorLike = \case - IsConstructor{} -> True - IsNewtype -> True - _ -> False - --- Once we assign a delay and force value to every `Var` in the binding group, --- we can consider how to order the bindings to allow them all to successfully --- initialize. There is one principle here: each binding must be initialized --- before the identifier being bound is ready for use. If the preorder thus --- induced has cycles, those cycles need to be resolved with laziness. All of --- the details concern what "ready for use" means. --- --- The definition of delay and force suggests that "ready for use" depends on --- those attributes. If a lambda is bound to the name x, then the references in --- the lambda don't need to be initialized before x is initialized. This is --- represented by the fact that those references have non-zero delay. But if --- the expression bound to x is instead the application of a function y that is --- also bound in this binding group, then not only does y need to be --- initialized before x, so do some of the non-zero delay references in y. This --- is represented by the fact that the occurrence of y in the expression bound --- to x has non-zero force. --- --- An example, reusing the pseudocode annotations defined above: --- --- x _ = y#1!0 --- y = x#0!1 a --- --- y doesn't need to be initialized before x is, because the reference to y in --- x's initializer has delay 1. But y does need to be initialized before x is --- ready for use with force 1, because force 1 is enough to overcome the delay --- of that reference. And since y has a delay-0 reference to x with force 1, y --- will need to be ready for use before it is initialized; thus, y needs to be --- made lazy. --- --- So just as function applications "cancel out" lambdas, a known applied force --- cancels out an equal amount of delay, causing some references that may not --- have been needed earlier to enter play. (And to be safe, we must assume that --- unknown force cancels out *any* amount of delay.) There is another, subtler --- aspect of this: if there are not enough lambdas to absorb every argument --- applied to a function, those arguments will end up applied to the result of --- the function. Likewise, if there is excess force left over after some of it --- has been canceled by delay, that excess is carried to the references --- activated. (Again, an unknown amount of force must be assumed to lead to an --- unknown amount of excess force.) --- --- Another example: --- --- f = g#0!2 a b --- g x = h#1!2 c x --- h _ _ _ = f#3!0 --- --- Initializing f will lead to an infinite loop in this example. f invokes g --- with two arguments. g absorbs one argument, and the second ends up being --- applied to the result of h c x, resulting in h being invoked with three --- arguments. Invoking h with three arguments results in dereferencing f, which --- is not yet ready. To capture this loop in our analysis, we say that making --- f ready for use with force 0 requires making g ready for use with force 2, --- which requires making h ready for use with force 3 (two units of force from --- the lexical position of h, plus one unit of excess force carried forward), --- which cyclically requires f to be ready for use with force 0. --- --- These preceding observations are captured and generalized by the following --- rules: --- --- USE-INIT: Before a reference to x is ready for use with any force, x must --- be initialized. --- --- We will make x lazy iff this rule induces a cycle--i.e., initializing x --- requires x to be ready for use first. --- --- USE-USE: Before a reference to x is ready for use with force f: --- * if a reference in the initializer of x has delay d and force f', --- * and either d <= f or f is unknown, --- * then that reference must itself be ready for use with --- force f – d + f' (or with unknown force if f or f' is unknown). --- --- USE-IMMEDIATE: Initializing a binding x is equivalent to requiring a --- reference to x to be ready for use with force 0, per USE-USE. --- --- Equivalently: before x is initialized, any reference in the initializer --- of x with delay 0 and force f must be ready for use with force f. --- --- Examples: --- --- Assume x is bound in a recursive binding group with the below bindings. --- --- All of the following initializers require x to be ready for use with some --- amount of force, and therefore require x to be initialized first. --- --- a = x#0!0 --- b = (\_ -> x#0!0) 1 --- c = foo x#0!* --- d = (\_ -> foo x#0!*) 1 --- --- In the following initializers, before p can be initialized, x must be --- ready for use with force f – d + f'. (And both x and q must be --- initialized, of course; but x being ready for use with that force may --- induce additional constraints.) --- --- p = ... q#0!f ... --- q = ... x#d!f' ... (where d <= f) --- --- Excess force stacks, of course: in the following initializers, before r --- can be initialized, x must be ready for use with force --- f — d + f' — d' + f'': --- --- r = ... s#0!f ... --- s = ... t#d!f' ... (where d <= f) --- t = ... x#d'!f'' ... (where d' <= f – d + f') --- --- --- To satisfy these rules, we will construct a graph between (identifier, --- delay) pairs, with edges induced by the USE-USE rule, and effectively run a --- topsort to get the initialization preorder. For this part, it's simplest to --- think of delay as an element of the naturals extended with a positive --- infinity, corresponding to an unknown amount of force. (We'll do arithmetic --- on these extended naturals as you would naively expect; we won't do anything --- suspect like subtracting infinity from infinity.) With that in mind, we can --- construct the graph as follows: for each reference from i1 to i2 with delay --- d and force f, draw an infinite family of edges from (i1, d + n) to (i2, f + --- n) for all 0 <= n <= ∞, where n represents the excess force carried over --- from a previous edge. Unfortunately, as an infinite graph, we can't expect --- the tools in Data.Graph to help us traverse it; we will have to be a little --- bit clever. --- --- The following data types and functions are for searching this infinite graph --- and carving from it a finite amount of data to work with. Specifically, we --- want to know for each identifier i, which other identifiers are --- irreflexively reachable from (i, 0) (and thus must be initialized before i --- is), and with what maximum force (in the event of a loop, not every --- reference to i in the reachable identifier needs to be rewritten to a force --- call; only the ones with delay up to the maximum force used during i's --- initialization). We also want the option of aborting a given reachability --- search, for one of two reasons. --- --- * If we encounter a reference with unknown force, abort. --- * If we encounter a cycle where force on a single identifier is --- increasing, abort. (Because of USE-USE, as soon as an identifier is --- revisited with greater force than its first visit, the difference is --- carried forward as excess, so it is possible to retrace that path to get --- an arbitrarily high amount of force.) --- --- Both reasons mean that it is theoretically possible for the identifier in --- question to need every other identifier in the binding group to be --- initialized before it is. (Every identifier in a recursive binding group is --- necessarily reachable from every other, ignoring delay and force, which is --- what arbitrarily high force lets you do.) --- --- In order to reuse parts of this reachability computation across identifiers, --- we are going to represent it with a rose tree data structure interleaved with --- a monad capturing the abort semantics. (The monad is Maybe, but we don't --- need to know that here!) - -type MaxRoseTree m a = m (IM.MonoidalIntMap (MaxRoseNode m a)) -data MaxRoseNode m a = MaxRoseNode a (MaxRoseTree m a) - --- Dissecting this data structure: --- --- m (...) --- ^ represents whether to abort or continue the search --- --- IM.MonoidalIntMap (...) --- ^ the keys of this map are other identifiers reachable from the current --- one (we'll map the identifiers in this binding group to Ints for ease of --- computation) --- --- the values of this map are: --- --- MaxRoseNode a (...) --- ^ this will store the force applied to the next identifier --- (MaxRoseTree m a) --- ^ and this, the tree of identifiers reachable from there --- --- We're only interested in continuing down the search path that applies the --- most force to a given identifier! So when we combine two MaxRoseTrees, --- we want to resolve any key collisions in their MonoidalIntMaps with this --- semigroup: - -instance Ord a => Semigroup (MaxRoseNode m a) where - l@(MaxRoseNode l1 _) <> r@(MaxRoseNode r1 _) = if r1 > l1 then r else l - --- And that's why this is called a MaxRoseTree. --- --- Traversing this tree to get a single MonoidalIntMap with the entire closure --- plus force information is fairly straightforward: - -mrtFlatten :: (Monad m, Ord a) => MaxRoseTree m a -> m (IM.MonoidalIntMap (Max a)) -mrtFlatten = (getAp . IM.foldMapWithKey (\i (MaxRoseNode a inner) -> Ap $ (IM.singleton i (Max a) <>) <$> mrtFlatten inner) =<<) - --- The use of the `Ap` monoid ensures that if any child of this tree aborts, --- the entire tree aborts. --- --- One might ask, why interleave the abort monad with the tree at all if we're --- just going to flatten it out at the end? The point is to flatten it out at --- the end, but *not* during the generation of the tree. Attempting to flatten --- the tree as we generate it can result in an infinite loop, because a subtree --- needs to be exhaustively searched for abort conditions before it can be used --- in another tree. With this approach, we can use lazy trees as building --- blocks and, as long as they get rewritten to be finite or have aborts before --- they're flattened, the analysis still terminates. - --- | --- Given a maximum index and a function that returns a map of edges to next --- indices, returns an array for each index up to maxIndex of maps from the --- indices reachable from the current index, to the maximum force applied to --- those indices. -searchReachable - :: forall m force - . (Alternative m, Monad m, Enum force, Ord force) - => Int - -> ((Int, force) -> m (IM.MonoidalIntMap (Max force))) - -> A.Array Int (m (IM.MonoidalIntMap (Max force))) -searchReachable maxIdx lookupEdges = mrtFlatten . unsafeHead <$> mem - where - -- This is a finite array of infinite lists, used to memoize all the search - -- trees. `unsafeHead` is used above to pull the first tree out of each list - -- in the array--the one corresponding to zero force, which is what's needed - -- to initialize the corresponding identifier. (`unsafeHead` is safe here, of - -- course: infinite lists.) - mem :: A.Array Int [MaxRoseTree m force] - mem = A.listArray (0, maxIdx) - [ [cutLoops <*> fmap (IM.mapWithKey memoizedNode) . lookupEdges $ (i, f) | f <- [toEnum 0..]] - | i <- [0..maxIdx] - ] - - memoizedNode :: Int -> Max force -> MaxRoseNode m force - memoizedNode i (Max force) = MaxRoseNode force $ mem A.! i !! fromEnum force - - -- And this is the function that prevents the search from actually being - -- infinite. It applies a filter to a `MaxRoseTree` at every level, looking for - -- indices anywhere in the tree that match the current vertex. If a match is - -- found with greater force than the current force, that part of the tree is - -- rewritten to abort; otherwise, that part of the tree is rewritten to be - -- empty (there's nothing new in that part of the search). - -- - -- A new version of `cutLoops` is applied for each node in the search, so - -- each edge in a search path will add another filter on a new index. Since - -- there are a finite number of indices in our universe, this guarantees that - -- the analysis terminates, because no single search path can have length - -- greater than `maxIdx`. - cutLoops :: (Int, force) -> MaxRoseTree m force -> MaxRoseTree m force - cutLoops (i, force) = go - where - go = (=<<) . IM.traverseWithKey $ \i' (MaxRoseNode force' inner) -> - MaxRoseNode force' <$> if i == i' then guard (force >= force') $> pure IM.empty else pure $ go inner - --- One last data structure to define and then it's on to the main event. --- --- The laziness transform effectively takes a list of eager bindings (x = ...) --- and splits some of them into lazy definitions ($lazy_x = ...) and lazy --- bindings (x = $lazy_x ...). It's convenient to work with these three --- declarations as the following sum type: - -data RecursiveGroupItem e = EagerBinding Ann e | LazyDefinition e | LazyBinding Ann - deriving Functor - --- | --- Transform a recursive binding group, reordering the bindings within when a --- correct initialization order can be statically determined, and rewriting --- bindings and references to be lazy otherwise. --- -applyLazinessTransform :: ModuleName -> [((Ann, Ident), Expr Ann)] -> ([((Ann, Ident), Expr Ann)], Any) -applyLazinessTransform mn rawItems = let - - -- Establish the mapping from names to ints. - rawItemsByName :: M.MonoidalMap Ident (Ann, Expr Ann) - rawItemsByName = M.fromList $ (snd . fst &&& first fst) <$> rawItems - - maxIdx = M.size rawItemsByName - 1 - - rawItemsByIndex :: A.Array Int (Ann, Expr Ann) - rawItemsByIndex = A.listArray (0, maxIdx) $ M.elems rawItemsByName - - names :: S.Set Ident - names = M.keysSet rawItemsByName - - -- Now do the first delay/force traversal of all the bindings to find - -- references to other names in this binding group. - -- - -- The parts of this type mean: - -- D is the maximum force (or Nothing if unknown) with which the identifier C - -- is referenced in any delay-B position inside the expression A. - -- - -- where A, B, C, and D are as below: - -- A B (keys) C (keys) D - findReferences :: Expr Ann -> IM.MonoidalIntMap (IM.MonoidalIntMap (Ap Maybe (Max Int))) - findReferences = (getConst .) . onVarsWithDelayAndForce $ \delay force _ -> \case - Qualified qb ident | all (== mn) (toMaybeModuleName qb), Just i <- ident `S.lookupIndex` names - -> Const . IM.singleton delay . IM.singleton i $ coerceForce force - _ -> Const IM.empty - - -- The parts of this type mean: - -- D is the maximum force (or Nothing if unknown) with which the identifier C - -- is referenced in any delay-B position inside the binding of identifier A. - -- - -- where A, B, C, and D are as below: - -- A B (keys) C (keys) D - refsByIndex :: A.Array Int (IM.MonoidalIntMap (IM.MonoidalIntMap (Ap Maybe (Max Int)))) - refsByIndex = findReferences . snd <$> rawItemsByIndex - - -- Using the approach explained above, traverse the reference graph generated - -- by `refsByIndex` and find all reachable names. - -- - -- The parts of this type mean: - -- D is the maximum force with which the identifier C is referenced, - -- directly or indirectly, during the initialization of identifier A. B is - -- Nothing if the analysis of A was inconclusive and A might need the entire - -- binding group. - -- - -- where A, B, C, and D are as below: - -- A B C (keys) D - reachablesByIndex :: A.Array Int (Maybe (IM.MonoidalIntMap (Max Int))) - reachablesByIndex = searchReachable maxIdx $ \(i, force) -> - getAp . flip IM.foldMapWithKey (dropKeysAbove force $ refsByIndex A.! i) $ \delay -> - IM.foldMapWithKey $ \i' force' -> - Ap $ IM.singleton i' . Max . (force - delay +) <$> uncoerceForce force' - - -- If `reachablesByIndex` is a sort of labeled relation, this function - -- produces part of the reverse relation, but only for the edges from the - -- given vertex. - -- - -- The parts of this type mean: - -- The identifier A is reachable from the identifier B with maximum force C - -- (B is also the index provided to the function). - -- - -- where A, B, and C are as below: - -- (B) A B (singleton key) C - reverseReachablesFor :: Int -> IM.MonoidalIntMap (IM.MonoidalIntMap (Ap Maybe (Max Int))) - reverseReachablesFor i = case reachablesByIndex A.! i of - Nothing -> IM.fromAscList $ (, IM.singleton i $ Ap Nothing) <$> [0..maxIdx] - Just im -> IM.singleton i . Ap . Just <$> im - - -- We can use `reachablesByIndex` to build a finite graph and topsort it; - -- in the process, we'll pack the nodes of the graph with data we'll want - -- next. Remember that if our reachability computation aborted, we have to - -- assume that every other identifier is reachable from that one--hence the - -- `maybe [0..maxIdx]`. - sccs = stronglyConnComp $ do - (i, mbReachable) <- A.assocs reachablesByIndex - pure ((reverseReachablesFor i, (S.elemAt i names, rawItemsByIndex A.! i)), i, maybe [0..maxIdx] (IS.toList . IM.keysSet) mbReachable) - - (replacements, items) = flip foldMap sccs $ \case - -- The easy case: this binding doesn't need to be made lazy after all! - AcyclicSCC (_, (ident, (a, e))) -> pure [(ident, EagerBinding a e)] - -- The tough case: we have a loop. - -- We need to do two things here: - -- * Collect the reversed reachables relation for each vertex in this - -- loop; we'll use this to replace references with force calls - -- * Copy the vertex list into two lists: a list of lazy definitions and - -- a list of lazy bindings - -- Both of these results are monoidal, so the outer `foldMap` will - -- concatenate them pairwise. - CyclicSCC vertices -> (foldMap fst vertices, map (fmap (LazyDefinition . snd) . snd) vertices ++ map (fmap (LazyBinding . fst) . snd) vertices) - - -- We have `replacements` expressed in terms of indices; we want to map it - -- back to names before traversing the bindings again. - replacementsByName :: M.MonoidalMap Ident (M.MonoidalMap Ident (Ap Maybe (Max Int))) - replacementsByName = M.fromAscList . map (bimap (flip S.elemAt names) (M.fromAscList . map (first (flip S.elemAt names)) . IM.toAscList)) . IM.toAscList $ replacements - - -- And finally, this is the second delay/force traversal where we take - -- `replacementsByName` and use it to rewrite references with force calls, - -- but only if the delay of those references is at most the maximum amount - -- of force used by the initialization of the referenced binding to - -- reference the outer binding. A reference made with a higher delay than - -- that can safely continue to use the original reference, since it won't be - -- needed until after the referenced binding is done initializing. - replaceReferencesWithForceCall :: (Ident, RecursiveGroupItem (Expr Ann)) -> (Ident, RecursiveGroupItem (Expr Ann)) - replaceReferencesWithForceCall pair@(ident, item) = case ident `M.lookup` replacementsByName of - Nothing -> pair - Just m -> let - rewriteExpr = (runIdentity .) . onVarsWithDelayAndForce $ \delay _ ann -> pure . \case - Qualified qb ident' | all (== mn) (toMaybeModuleName qb), any (all (>= Max delay) . getAp) $ ident' `M.lookup` m - -> makeForceCall ann ident' - q -> Var ann q - in (ident, rewriteExpr <$> item) - - -- All that's left to do is run the above replacement on every item, - -- translate items from our `RecursiveGroupItem` representation back into the - -- form CoreFn expects, and inform the caller whether we made any laziness - -- transformations after all. (That last bit of information is used to - -- determine if the runtime factory function needs to be injected.) - in (uncurry fromRGI . replaceReferencesWithForceCall <$> items, Any . not $ IM.null replacements) - - where - - nullAnn = ssAnn nullSourceSpan - runtimeLazy = Var nullAnn . Qualified ByNullSourcePos $ InternalIdent RuntimeLazyFactory - runFn3 = Var nullAnn . Qualified (ByModuleName C.M_Data_Function_Uncurried) . Ident $ C.S_runFn <> "3" - strLit = Literal nullAnn . StringLiteral . mkString - - lazifyIdent = \case - Ident txt -> InternalIdent $ Lazy txt - _ -> internalError "Unexpected argument to lazifyIdent" - - makeForceCall :: Ann -> Ident -> Expr Ann - makeForceCall (ss, _, _) ident - -- We expect the functions produced by `runtimeLazy` to accept one - -- argument: the line number on which this reference is made. The runtime - -- code uses this number to generate a message that identifies where the - -- evaluation looped. - = App nullAnn (Var nullAnn . Qualified ByNullSourcePos $ lazifyIdent ident) - . Literal nullAnn . NumericLiteral . Left . toInteger . sourcePosLine - $ spanStart ss - - fromRGI :: Ident -> RecursiveGroupItem (Expr Ann) -> ((Ann, Ident), Expr Ann) - fromRGI i = \case - EagerBinding a e -> ((a, i), e) - -- We expect the `runtimeLazy` factory to accept three arguments: the - -- identifier being initialized, the name of the module, and of course a - -- thunk that actually contains the initialization code. - LazyDefinition e -> ((nullAnn, lazifyIdent i), foldl1' (App nullAnn) [runFn3, runtimeLazy, strLit $ runIdent i, strLit $ runModuleName mn, Abs nullAnn UnusedIdent e]) - LazyBinding a -> ((a, i), makeForceCall a i) - - dropKeysAbove :: Int -> IM.MonoidalIntMap a -> IM.MonoidalIntMap a - dropKeysAbove n = fst . IM.split (n + 1) - - coerceForce :: Maybe Int -> Ap Maybe (Max Int) - coerceForce = coerce - - uncoerceForce :: Ap Maybe (Max Int) -> Maybe Int - uncoerceForce = coerce diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Meta.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Meta.hs deleted file mode 100644 index 0baddca2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Meta.hs +++ /dev/null @@ -1,51 +0,0 @@ --- | --- Metadata annotations for core functional representation --- -module Language.PureScript.CoreFn.Meta where - -import Prelude - -import Language.PureScript.Names (Ident) - --- | --- Metadata annotations --- -data Meta - -- | - -- The contained value is a data constructor - -- - = IsConstructor ConstructorType [Ident] - -- | - -- The contained value is a newtype - -- - | IsNewtype - -- | - -- The contained value is a typeclass dictionary constructor - -- - | IsTypeClassConstructor - -- | - -- The contained reference is for a foreign member - -- - | IsForeign - -- | - -- The contained value is a where clause - -- - | IsWhere - -- | - -- The contained function application was synthesized by the compiler - -- - | IsSyntheticApp - deriving (Show, Eq, Ord) - --- | --- Data constructor metadata --- -data ConstructorType - -- | - -- The constructor is for a type with a single constructor - -- - = ProductType - -- | - -- The constructor is for a type with multiple constructors - -- - | SumType deriving (Show, Eq, Ord) diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Module.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Module.hs deleted file mode 100644 index 09f5189c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Module.hs +++ /dev/null @@ -1,25 +0,0 @@ -module Language.PureScript.CoreFn.Module where - -import Prelude - -import Data.Map.Strict (Map) - -import Language.PureScript.AST.SourcePos (SourceSpan) -import Language.PureScript.Comments (Comment) -import Language.PureScript.CoreFn.Expr (Bind) -import Language.PureScript.Names (Ident, ModuleName) - --- | --- The CoreFn module representation --- -data Module a = Module - { moduleSourceSpan :: SourceSpan - , moduleComments :: [Comment] - , moduleName :: ModuleName - , modulePath :: FilePath - , moduleImports :: [(a, ModuleName)] - , moduleExports :: [Ident] - , moduleReExports :: Map ModuleName [Ident] - , moduleForeign :: [Ident] - , moduleDecls :: [Bind a] - } deriving (Functor, Show) diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Optimizer.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Optimizer.hs deleted file mode 100644 index 9e2c9fa3..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Optimizer.hs +++ /dev/null @@ -1,58 +0,0 @@ -module Language.PureScript.CoreFn.Optimizer (optimizeCoreFn) where - -import Protolude hiding (Type, moduleName) - -import Control.Monad.Supply (Supply) -import Language.PureScript.CoreFn.Ann (Ann) -import Language.PureScript.CoreFn.CSE (optimizeCommonSubexpressions) -import Language.PureScript.CoreFn.Expr (Bind, Expr(..)) -import Language.PureScript.CoreFn.Module (Module(..)) -import Language.PureScript.CoreFn.Traversals (everywhereOnValues) -import Language.PureScript.Constants.Libs qualified as C --- import Language.PureScript.CoreImp.AST (AST(StringLiteral, ObjectLiteral)) -import Language.PureScript.AST.Literals (Literal(..)) --- | --- CoreFn optimization pass. --- -optimizeCoreFn :: Module Ann -> Supply (Module Ann) -optimizeCoreFn m = fmap (\md -> m {moduleDecls = md}) . optimizeCommonSubexpressions (moduleName m) . optimizeModuleDecls $ moduleDecls m - -optimizeModuleDecls :: [Bind Ann] -> [Bind Ann] -optimizeModuleDecls = map transformBinds - where - (transformBinds, _, _) = everywhereOnValues identity transformExprs identity - transformExprs - = optimizeDataFunctionApply - . optimizeRecordGetField - --- | Optimize --- `Data_Record.getField(Data_Record.hasFieldRecord(new Data_Symbol.IsSymbol(function() { return "f"; }))())(Type_Proxy.Proxy.value)(x)` --- into --- `x.f` -optimizeRecordGetField :: Expr a -> Expr a -optimizeRecordGetField - (App ann - (App _ - (App _ - (Var _ C.I_getField) - (App _ - (App _ - (Var _ C.I_hasFieldRecord) - (App _ - (Var _ C.IsSymbolDict) - (Literal _ (ObjectLiteral - [ ("reflectSymbol", Abs _ _ - (Literal _ (StringLiteral label))) - ])))) - _)) - (Var _ C.I_Proxy)) - object) = - Accessor ann label object -optimizeRecordGetField e = e - -optimizeDataFunctionApply :: Expr a -> Expr a -optimizeDataFunctionApply e = case e of - (App a (App _ (Var _ fn) x) y) - | C.I_functionApply <- fn -> App a x y - | C.I_functionApplyFlipped <- fn -> App a y x - _ -> e diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/ToJSON.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/ToJSON.hs deleted file mode 100644 index 1b20ac4e..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/ToJSON.hs +++ /dev/null @@ -1,249 +0,0 @@ -{-# LANGUAGE NoOverloadedStrings #-} --- | --- Dump the core functional representation in JSON format for consumption --- by third-party code generators --- -module Language.PureScript.CoreFn.ToJSON - ( moduleToJSON - ) where - -import Prelude - -import Control.Arrow ((***)) -import Data.Either (isLeft) -import Data.Map.Strict qualified as M -import Data.Aeson (ToJSON(..), Value(..), object) -import Data.Aeson qualified -import Data.Aeson.Key qualified -import Data.Aeson.Types (Pair) -import Data.Version (Version, showVersion) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.AST.SourcePos (SourceSpan(..)) -import Language.PureScript.CoreFn (Ann, Bind(..), Binder(..), CaseAlternative(..), ConstructorType(..), Expr(..), Meta(..), Module(..)) -import Language.PureScript.Names (Ident, ModuleName(..), ProperName(..), Qualified(..), QualifiedBy(..), runIdent) -import Language.PureScript.PSString (PSString) - -constructorTypeToJSON :: ConstructorType -> Value -constructorTypeToJSON ProductType = toJSON "ProductType" -constructorTypeToJSON SumType = toJSON "SumType" - -infixr 8 .= -(.=) :: ToJSON a => String -> a -> Pair -key .= value = Data.Aeson.Key.fromString key Data.Aeson..= value - -metaToJSON :: Meta -> Value -metaToJSON (IsConstructor t is) - = object - [ "metaType" .= "IsConstructor" - , "constructorType" .= constructorTypeToJSON t - , "identifiers" .= identToJSON `map` is - ] -metaToJSON IsNewtype = object [ "metaType" .= "IsNewtype" ] -metaToJSON IsTypeClassConstructor = object [ "metaType" .= "IsTypeClassConstructor" ] -metaToJSON IsForeign = object [ "metaType" .= "IsForeign" ] -metaToJSON IsWhere = object [ "metaType" .= "IsWhere" ] -metaToJSON IsSyntheticApp = object [ "metaType" .= "IsSyntheticApp" ] - -sourceSpanToJSON :: SourceSpan -> Value -sourceSpanToJSON (SourceSpan _ spanStart spanEnd) = - object [ "start" .= spanStart - , "end" .= spanEnd - ] - -annToJSON :: Ann -> Value -annToJSON (ss, _, m) = object [ "sourceSpan" .= sourceSpanToJSON ss - , "meta" .= maybe Null metaToJSON m - ] - -literalToJSON :: (a -> Value) -> Literal a -> Value -literalToJSON _ (NumericLiteral (Left n)) - = object - [ "literalType" .= "IntLiteral" - , "value" .= n - ] -literalToJSON _ (NumericLiteral (Right n)) - = object - [ "literalType" .= "NumberLiteral" - , "value" .= n - ] -literalToJSON _ (StringLiteral s) - = object - [ "literalType" .= "StringLiteral" - , "value" .= s - ] -literalToJSON _ (CharLiteral c) - = object - [ "literalType" .= "CharLiteral" - , "value" .= c - ] -literalToJSON _ (BooleanLiteral b) - = object - [ "literalType" .= "BooleanLiteral" - , "value" .= b - ] -literalToJSON t (ArrayLiteral xs) - = object - [ "literalType" .= "ArrayLiteral" - , "value" .= map t xs - ] -literalToJSON t (ObjectLiteral xs) - = object - [ "literalType" .= "ObjectLiteral" - , "value" .= recordToJSON t xs - ] - -identToJSON :: Ident -> Value -identToJSON = toJSON . runIdent - -properNameToJSON :: ProperName a -> Value -properNameToJSON = toJSON . runProperName - -qualifiedToJSON :: (a -> Text) -> Qualified a -> Value -qualifiedToJSON f (Qualified qb a) = - case qb of - ByModuleName mn -> object - [ "moduleName" .= moduleNameToJSON mn - , "identifier" .= toJSON (f a) - ] - BySourcePos ss -> object - [ "sourcePos" .= toJSON ss - , "identifier" .= toJSON (f a) - ] - -moduleNameToJSON :: ModuleName -> Value -moduleNameToJSON (ModuleName name) = toJSON (T.splitOn (T.pack ".") name) - -moduleToJSON :: Version -> Module Ann -> Value -moduleToJSON v m = object - [ "sourceSpan" .= sourceSpanToJSON (moduleSourceSpan m) - , "moduleName" .= moduleNameToJSON (moduleName m) - , "modulePath" .= toJSON (modulePath m) - , "imports" .= map importToJSON (moduleImports m) - , "exports" .= map identToJSON (moduleExports m) - , "reExports" .= reExportsToJSON (moduleReExports m) - , "foreign" .= map identToJSON (moduleForeign m) - , "decls" .= map bindToJSON (moduleDecls m) - , "builtWith" .= toJSON (showVersion v) - , "comments" .= map toJSON (moduleComments m) - ] - - where - importToJSON (ann,mn) = object - [ "annotation" .= annToJSON ann - , "moduleName" .= moduleNameToJSON mn - ] - - reExportsToJSON :: M.Map ModuleName [Ident] -> Value - reExportsToJSON = toJSON . M.map (map runIdent) - -bindToJSON :: Bind Ann -> Value -bindToJSON (NonRec ann n e) - = object - [ "bindType" .= "NonRec" - , "annotation" .= annToJSON ann - , "identifier" .= identToJSON n - , "expression" .= exprToJSON e - ] -bindToJSON (Rec bs) - = object - [ "bindType" .= "Rec" - , "binds" .= map (\((ann, n), e) - -> object - [ "identifier" .= identToJSON n - , "annotation" .= annToJSON ann - , "expression" .= exprToJSON e - ]) bs - ] - -recordToJSON :: (a -> Value) -> [(PSString, a)] -> Value -recordToJSON f = toJSON . map (toJSON *** f) - -exprToJSON :: Expr Ann -> Value -exprToJSON (Var ann i) = object [ "type" .= toJSON "Var" - , "annotation" .= annToJSON ann - , "value" .= qualifiedToJSON runIdent i - ] -exprToJSON (Literal ann l) = object [ "type" .= "Literal" - , "annotation" .= annToJSON ann - , "value" .= literalToJSON exprToJSON l - ] -exprToJSON (Constructor ann d c is) = object [ "type" .= "Constructor" - , "annotation" .= annToJSON ann - , "typeName" .= properNameToJSON d - , "constructorName" .= properNameToJSON c - , "fieldNames" .= map identToJSON is - ] -exprToJSON (Accessor ann f r) = object [ "type" .= "Accessor" - , "annotation" .= annToJSON ann - , "fieldName" .= f - , "expression" .= exprToJSON r - ] -exprToJSON (ObjectUpdate ann r copy fs) - = object [ "type" .= "ObjectUpdate" - , "annotation" .= annToJSON ann - , "expression" .= exprToJSON r - , "copy" .= toJSON copy - , "updates" .= recordToJSON exprToJSON fs - ] -exprToJSON (Abs ann p b) = object [ "type" .= "Abs" - , "annotation" .= annToJSON ann - , "argument" .= identToJSON p - , "body" .= exprToJSON b - ] -exprToJSON (App ann f x) = object [ "type" .= "App" - , "annotation" .= annToJSON ann - , "abstraction" .= exprToJSON f - , "argument" .= exprToJSON x - ] -exprToJSON (Case ann ss cs) = object [ "type" .= "Case" - , "annotation" .= annToJSON ann - , "caseExpressions" - .= map exprToJSON ss - , "caseAlternatives" - .= map caseAlternativeToJSON cs - ] -exprToJSON (Let ann bs e) = object [ "type" .= "Let" - , "annotation" .= annToJSON ann - , "binds" .= map bindToJSON bs - , "expression" .= exprToJSON e - ] - -caseAlternativeToJSON :: CaseAlternative Ann -> Value -caseAlternativeToJSON (CaseAlternative bs r') = - let isGuarded = isLeft r' - in object - [ "binders" .= toJSON (map binderToJSON bs) - , "isGuarded" .= toJSON isGuarded - , (if isGuarded then "expressions" else "expression") - .= case r' of - Left rs -> toJSON $ map (\(g, e) -> object [ "guard" .= exprToJSON g, "expression" .= exprToJSON e]) rs - Right r -> exprToJSON r - ] - -binderToJSON :: Binder Ann -> Value -binderToJSON (VarBinder ann v) = object [ "binderType" .= "VarBinder" - , "annotation" .= annToJSON ann - , "identifier" .= identToJSON v - ] -binderToJSON (NullBinder ann) = object [ "binderType" .= "NullBinder" - , "annotation" .= annToJSON ann - ] -binderToJSON (LiteralBinder ann l) = object [ "binderType" .= "LiteralBinder" - , "annotation" .= annToJSON ann - , "literal" .= literalToJSON binderToJSON l - ] -binderToJSON (ConstructorBinder ann d c bs) = object [ "binderType" .= "ConstructorBinder" - , "annotation" .= annToJSON ann - , "typeName" .= qualifiedToJSON runProperName d - , "constructorName" - .= qualifiedToJSON runProperName c - , "binders" .= map binderToJSON bs - ] -binderToJSON (NamedBinder ann n b) = object [ "binderType" .= "NamedBinder" - , "annotation" .= annToJSON ann - , "identifier" .= identToJSON n - , "binder" .= binderToJSON b - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Traversals.hs b/claude-help/original-compiler/src/Language/PureScript/CoreFn/Traversals.hs deleted file mode 100644 index f0684d34..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreFn/Traversals.hs +++ /dev/null @@ -1,86 +0,0 @@ --- | --- CoreFn traversal helpers --- -module Language.PureScript.CoreFn.Traversals where - -import Prelude - -import Control.Arrow (second, (***), (+++)) -import Data.Bitraversable (bitraverse) - -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.CoreFn.Binders (Binder(..)) -import Language.PureScript.CoreFn.Expr (Bind(..), CaseAlternative(..), Expr(..)) - -everywhereOnValues :: (Bind a -> Bind a) -> - (Expr a -> Expr a) -> - (Binder a -> Binder a) -> - (Bind a -> Bind a, Expr a -> Expr a, Binder a -> Binder a) -everywhereOnValues f g h = (f', g', h') - where - f' (NonRec a name e) = f (NonRec a name (g' e)) - f' (Rec es) = f (Rec (map (second g') es)) - - g' (Literal ann e) = g (Literal ann (handleLiteral g' e)) - g' (Accessor ann prop e) = g (Accessor ann prop (g' e)) - g' (ObjectUpdate ann obj copy vs) = g (ObjectUpdate ann (g' obj) copy (map (fmap g') vs)) - g' (Abs ann name e) = g (Abs ann name (g' e)) - g' (App ann v1 v2) = g (App ann (g' v1) (g' v2)) - g' (Case ann vs alts) = g (Case ann (map g' vs) (map handleCaseAlternative alts)) - g' (Let ann ds e) = g (Let ann (map f' ds) (g' e)) - g' e = g e - - h' (LiteralBinder a b) = h (LiteralBinder a (handleLiteral h' b)) - h' (NamedBinder a name b) = h (NamedBinder a name (h' b)) - h' (ConstructorBinder a q1 q2 bs) = h (ConstructorBinder a q1 q2 (map h' bs)) - h' b = h b - - handleCaseAlternative ca = - ca { caseAlternativeBinders = map h' (caseAlternativeBinders ca) - , caseAlternativeResult = (map (g' *** g') +++ g') (caseAlternativeResult ca) - } - - handleLiteral :: (a -> a) -> Literal a -> Literal a - handleLiteral i (ArrayLiteral ls) = ArrayLiteral (map i ls) - handleLiteral i (ObjectLiteral ls) = ObjectLiteral (map (fmap i) ls) - handleLiteral _ other = other - --- | --- Apply the provided functions to the top level of AST nodes. --- --- This function is useful as a building block for recursive functions, but --- doesn't actually recurse itself. --- -traverseCoreFn - :: forall f a - . Applicative f - => (Bind a -> f (Bind a)) - -> (Expr a -> f (Expr a)) - -> (Binder a -> f (Binder a)) - -> (CaseAlternative a -> f (CaseAlternative a)) - -> (Bind a -> f (Bind a), Expr a -> f (Expr a), Binder a -> f (Binder a), CaseAlternative a -> f (CaseAlternative a)) -traverseCoreFn f g h i = (f', g', h', i') - where - f' (NonRec a name e) = NonRec a name <$> g e - f' (Rec es) = Rec <$> traverse (traverse g) es - - g' (Literal ann e) = Literal ann <$> handleLiteral g e - g' (Accessor ann prop e) = Accessor ann prop <$> g e - g' (ObjectUpdate ann obj copy vs) = (\obj' -> ObjectUpdate ann obj' copy) <$> g obj <*> traverse (traverse g) vs - g' (Abs ann name e) = Abs ann name <$> g e - g' (App ann v1 v2) = App ann <$> g v1 <*> g v2 - g' (Case ann vs alts) = Case ann <$> traverse g vs <*> traverse i alts - g' (Let ann ds e) = Let ann <$> traverse f ds <*> g' e - g' e = pure e - - h' (LiteralBinder a b) = LiteralBinder a <$> handleLiteral h b - h' (NamedBinder a name b) = NamedBinder a name <$> h b - h' (ConstructorBinder a q1 q2 bs) = ConstructorBinder a q1 q2 <$> traverse h bs - h' b = pure b - - i' ca = CaseAlternative <$> traverse h (caseAlternativeBinders ca) <*> bitraverse (traverse $ bitraverse g g) g (caseAlternativeResult ca) - - handleLiteral withItem = \case - ArrayLiteral ls -> ArrayLiteral <$> traverse withItem ls - ObjectLiteral ls -> ObjectLiteral <$> traverse (traverse withItem) ls - other -> pure other diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp.hs deleted file mode 100644 index 5029aff9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp.hs +++ /dev/null @@ -1,13 +0,0 @@ --- | The imperative core language -module Language.PureScript.CoreImp ( - module C -) where - -import Language.PureScript.CoreImp.AST as C -import Language.PureScript.CoreImp.Optimizer as C -import Language.PureScript.CoreImp.Optimizer.Blocks as C -import Language.PureScript.CoreImp.Optimizer.Common as C -import Language.PureScript.CoreImp.Optimizer.Inliner as C -import Language.PureScript.CoreImp.Optimizer.MagicDo as C -import Language.PureScript.CoreImp.Optimizer.TCO as C -import Language.PureScript.CoreImp.Optimizer.Unused as C diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/AST.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/AST.hs deleted file mode 100644 index adedeb43..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/AST.hs +++ /dev/null @@ -1,250 +0,0 @@ --- | Data types for the imperative core AST -module Language.PureScript.CoreImp.AST where - -import Prelude - -import Control.Monad ((>=>)) -import Control.Monad.Identity (Identity(..), runIdentity) -import Data.Text (Text) - -import Language.PureScript.AST (SourceSpan(..)) -import Language.PureScript.Comments (Comment) -import Language.PureScript.Names (ModuleName) -import Language.PureScript.PSString (PSString) -import Language.PureScript.Traversals (sndM) - --- | Built-in unary operators -data UnaryOperator - = Negate - | Not - | BitwiseNot - | Positive - | New - deriving (Show, Eq) - --- | Built-in binary operators -data BinaryOperator - = Add - | Subtract - | Multiply - | Divide - | Modulus - | EqualTo - | NotEqualTo - | LessThan - | LessThanOrEqualTo - | GreaterThan - | GreaterThanOrEqualTo - | And - | Or - | BitwiseAnd - | BitwiseOr - | BitwiseXor - | ShiftLeft - | ShiftRight - | ZeroFillShiftRight - deriving (Show, Eq) - --- | Data type for CoreImp comments, which can come from either the PureScript --- source or internal transformations. -data CIComments - = SourceComments [Comment] - | PureAnnotation - deriving (Show, Eq) - --- | --- Indicates whether the initializer of a variable is known not to have side --- effects, and thus can be inlined if needed or removed if unneeded. --- -data InitializerEffects = NoEffects | UnknownEffects deriving (Show, Eq) - --- | Data type for simplified JavaScript expressions -data AST - = NumericLiteral (Maybe SourceSpan) (Either Integer Double) - -- ^ A numeric literal - | StringLiteral (Maybe SourceSpan) PSString - -- ^ A string literal - | BooleanLiteral (Maybe SourceSpan) Bool - -- ^ A boolean literal - | Unary (Maybe SourceSpan) UnaryOperator AST - -- ^ A unary operator application - | Binary (Maybe SourceSpan) BinaryOperator AST AST - -- ^ A binary operator application - | ArrayLiteral (Maybe SourceSpan) [AST] - -- ^ An array literal - | Indexer (Maybe SourceSpan) AST AST - -- ^ An array indexer expression - | ObjectLiteral (Maybe SourceSpan) [(PSString, AST)] - -- ^ An object literal - | Function (Maybe SourceSpan) (Maybe Text) [Text] AST - -- ^ A function introduction (optional name, arguments, body) - | App (Maybe SourceSpan) AST [AST] - -- ^ Function application - | Var (Maybe SourceSpan) Text - -- ^ Variable - | ModuleAccessor (Maybe SourceSpan) ModuleName PSString - -- ^ Value from another module - | Block (Maybe SourceSpan) [AST] - -- ^ A block of expressions in braces - | VariableIntroduction (Maybe SourceSpan) Text (Maybe (InitializerEffects, AST)) - -- ^ A variable introduction and optional initialization - | Assignment (Maybe SourceSpan) AST AST - -- ^ A variable assignment - | While (Maybe SourceSpan) AST AST - -- ^ While loop - | For (Maybe SourceSpan) Text AST AST AST - -- ^ For loop - | ForIn (Maybe SourceSpan) Text AST AST - -- ^ ForIn loop - | IfElse (Maybe SourceSpan) AST AST (Maybe AST) - -- ^ If-then-else statement - | Return (Maybe SourceSpan) AST - -- ^ Return statement - | ReturnNoResult (Maybe SourceSpan) - -- ^ Return statement with no return value - | Throw (Maybe SourceSpan) AST - -- ^ Throw statement - | InstanceOf (Maybe SourceSpan) AST AST - -- ^ instanceof check - | Comment CIComments AST - -- ^ Commented JavaScript - -- | Import (Maybe SourceSpan) Text PSString - -- -- ^ Imported identifier and path to its module - -- | Export (Maybe SourceSpan) (NEL.NonEmpty Text) (Maybe PSString) - -- -- ^ Exported identifiers and optional path to their module (for re-exports) - deriving (Show, Eq) - -withSourceSpan :: SourceSpan -> AST -> AST -withSourceSpan withSpan = go where - ss :: Maybe SourceSpan - ss = Just withSpan - - go :: AST -> AST - go (NumericLiteral _ n) = NumericLiteral ss n - go (StringLiteral _ s) = StringLiteral ss s - go (BooleanLiteral _ b) = BooleanLiteral ss b - go (Unary _ op j) = Unary ss op j - go (Binary _ op j1 j2) = Binary ss op j1 j2 - go (ArrayLiteral _ js) = ArrayLiteral ss js - go (Indexer _ j1 j2) = Indexer ss j1 j2 - go (ObjectLiteral _ js) = ObjectLiteral ss js - go (Function _ name args j) = Function ss name args j - go (App _ j js) = App ss j js - go (Var _ s) = Var ss s - go (ModuleAccessor _ s1 s2) = ModuleAccessor ss s1 s2 - go (Block _ js) = Block ss js - go (VariableIntroduction _ name j) = VariableIntroduction ss name j - go (Assignment _ j1 j2) = Assignment ss j1 j2 - go (While _ j1 j2) = While ss j1 j2 - go (For _ name j1 j2 j3) = For ss name j1 j2 j3 - go (ForIn _ name j1 j2) = ForIn ss name j1 j2 - go (IfElse _ j1 j2 j3) = IfElse ss j1 j2 j3 - go (Return _ js) = Return ss js - go (ReturnNoResult _) = ReturnNoResult ss - go (Throw _ js) = Throw ss js - go (InstanceOf _ j1 j2) = InstanceOf ss j1 j2 - go c@Comment{} = c - -- go (Import _ ident from) = Import ss ident from - -- go (Export _ idents from) = Export ss idents from - -getSourceSpan :: AST -> Maybe SourceSpan -getSourceSpan = go where - go :: AST -> Maybe SourceSpan - go (NumericLiteral ss _) = ss - go (StringLiteral ss _) = ss - go (BooleanLiteral ss _) = ss - go (Unary ss _ _) = ss - go (Binary ss _ _ _) = ss - go (ArrayLiteral ss _) = ss - go (Indexer ss _ _) = ss - go (ObjectLiteral ss _) = ss - go (Function ss _ _ _) = ss - go (App ss _ _) = ss - go (Var ss _) = ss - go (ModuleAccessor ss _ _) = ss - go (Block ss _) = ss - go (VariableIntroduction ss _ _) = ss - go (Assignment ss _ _) = ss - go (While ss _ _) = ss - go (For ss _ _ _ _) = ss - go (ForIn ss _ _ _) = ss - go (IfElse ss _ _ _) = ss - go (Return ss _) = ss - go (ReturnNoResult ss) = ss - go (Throw ss _) = ss - go (InstanceOf ss _ _) = ss - go (Comment _ _) = Nothing - -- go (Import ss _ _) = ss - -- go (Export ss _ _) = ss - -everywhere :: (AST -> AST) -> AST -> AST -everywhere f = go where - go :: AST -> AST - go (Unary ss op j) = f (Unary ss op (go j)) - go (Binary ss op j1 j2) = f (Binary ss op (go j1) (go j2)) - go (ArrayLiteral ss js) = f (ArrayLiteral ss (map go js)) - go (Indexer ss j1 j2) = f (Indexer ss (go j1) (go j2)) - go (ObjectLiteral ss js) = f (ObjectLiteral ss (map (fmap go) js)) - go (Function ss name args j) = f (Function ss name args (go j)) - go (App ss j js) = f (App ss (go j) (map go js)) - go (Block ss js) = f (Block ss (map go js)) - go (VariableIntroduction ss name j) = f (VariableIntroduction ss name (fmap (fmap go) j)) - go (Assignment ss j1 j2) = f (Assignment ss (go j1) (go j2)) - go (While ss j1 j2) = f (While ss (go j1) (go j2)) - go (For ss name j1 j2 j3) = f (For ss name (go j1) (go j2) (go j3)) - go (ForIn ss name j1 j2) = f (ForIn ss name (go j1) (go j2)) - go (IfElse ss j1 j2 j3) = f (IfElse ss (go j1) (go j2) (fmap go j3)) - go (Return ss js) = f (Return ss (go js)) - go (Throw ss js) = f (Throw ss (go js)) - go (InstanceOf ss j1 j2) = f (InstanceOf ss (go j1) (go j2)) - go (Comment com j) = f (Comment com (go j)) - go other = f other - -everywhereTopDown :: (AST -> AST) -> AST -> AST -everywhereTopDown f = runIdentity . everywhereTopDownM (Identity . f) - -everywhereTopDownM :: (Monad m) => (AST -> m AST) -> AST -> m AST -everywhereTopDownM f = f >=> go where - f' = f >=> go - go (Unary ss op j) = Unary ss op <$> f' j - go (Binary ss op j1 j2) = Binary ss op <$> f' j1 <*> f' j2 - go (ArrayLiteral ss js) = ArrayLiteral ss <$> traverse f' js - go (Indexer ss j1 j2) = Indexer ss <$> f' j1 <*> f' j2 - go (ObjectLiteral ss js) = ObjectLiteral ss <$> traverse (sndM f') js - go (Function ss name args j) = Function ss name args <$> f' j - go (App ss j js) = App ss <$> f' j <*> traverse f' js - go (Block ss js) = Block ss <$> traverse f' js - go (VariableIntroduction ss name j) = VariableIntroduction ss name <$> traverse (traverse f') j - go (Assignment ss j1 j2) = Assignment ss <$> f' j1 <*> f' j2 - go (While ss j1 j2) = While ss <$> f' j1 <*> f' j2 - go (For ss name j1 j2 j3) = For ss name <$> f' j1 <*> f' j2 <*> f' j3 - go (ForIn ss name j1 j2) = ForIn ss name <$> f' j1 <*> f' j2 - go (IfElse ss j1 j2 j3) = IfElse ss <$> f' j1 <*> f' j2 <*> traverse f' j3 - go (Return ss j) = Return ss <$> f' j - go (Throw ss j) = Throw ss <$> f' j - go (InstanceOf ss j1 j2) = InstanceOf ss <$> f' j1 <*> f' j2 - go (Comment com j) = Comment com <$> f' j - go other = f other - -everything :: (r -> r -> r) -> (AST -> r) -> AST -> r -everything (<>.) f = go where - go j@(Unary _ _ j1) = f j <>. go j1 - go j@(Binary _ _ j1 j2) = f j <>. go j1 <>. go j2 - go j@(ArrayLiteral _ js) = foldl (<>.) (f j) (map go js) - go j@(Indexer _ j1 j2) = f j <>. go j1 <>. go j2 - go j@(ObjectLiteral _ js) = foldl (<>.) (f j) (map (go . snd) js) - go j@(Function _ _ _ j1) = f j <>. go j1 - go j@(App _ j1 js) = foldl (<>.) (f j <>. go j1) (map go js) - go j@(Block _ js) = foldl (<>.) (f j) (map go js) - go j@(VariableIntroduction _ _ (Just (_, j1))) = f j <>. go j1 - go j@(Assignment _ j1 j2) = f j <>. go j1 <>. go j2 - go j@(While _ j1 j2) = f j <>. go j1 <>. go j2 - go j@(For _ _ j1 j2 j3) = f j <>. go j1 <>. go j2 <>. go j3 - go j@(ForIn _ _ j1 j2) = f j <>. go j1 <>. go j2 - go j@(IfElse _ j1 j2 Nothing) = f j <>. go j1 <>. go j2 - go j@(IfElse _ j1 j2 (Just j3)) = f j <>. go j1 <>. go j2 <>. go j3 - go j@(Return _ j1) = f j <>. go j1 - go j@(Throw _ j1) = f j <>. go j1 - go j@(InstanceOf _ j1 j2) = f j <>. go j1 <>. go j2 - go j@(Comment _ j1) = f j <>. go j1 - go other = f other diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Module.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Module.hs deleted file mode 100644 index bdf4b818..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Module.hs +++ /dev/null @@ -1,19 +0,0 @@ -module Language.PureScript.CoreImp.Module where - -import Protolude -import Data.List.NonEmpty qualified as NEL (NonEmpty) - -import Language.PureScript.Comments (Comment) -import Language.PureScript.CoreImp.AST (AST) -import Language.PureScript.PSString (PSString) - -data Module = Module - { modHeader :: [Comment] - , modImports :: [Import] - , modBody :: [AST] - , modExports :: [Export] - } - -data Import = Import Text PSString - -data Export = Export (NEL.NonEmpty Text) (Maybe PSString) diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer.hs deleted file mode 100644 index e59738df..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer.hs +++ /dev/null @@ -1,85 +0,0 @@ --- | This module optimizes code in the simplified-JavaScript intermediate representation. --- --- The following optimizations are supported: --- --- * Collapsing nested blocks --- --- * Tail call elimination --- --- * Inlining of (>>=) and ret for the Eff monad --- --- * Removal of unnecessary thunks --- --- * Eta conversion --- --- * Inlining variables --- --- * Inline Prelude.($), Prelude.(#), Prelude.(++), Prelude.(!!) --- --- * Inlining primitive JavaScript operators -module Language.PureScript.CoreImp.Optimizer (optimize) where - -import Prelude - -import Data.Text (Text) - -import Control.Monad.Supply.Class (MonadSupply) -import Language.PureScript.CoreImp.AST (AST(..), InitializerEffects(..)) -import Language.PureScript.CoreImp.Optimizer.Blocks (collapseNestedBlocks, collapseNestedIfs) -import Language.PureScript.CoreImp.Optimizer.Common (applyAll, replaceIdents) -import Language.PureScript.CoreImp.Optimizer.Inliner (etaConvert, evaluateIifes, inlineCommonOperators, inlineCommonValues, inlineFnComposition, inlineFnIdentity, inlineUnsafeCoerce, inlineUnsafePartial, inlineVariables, unThunk) -import Language.PureScript.CoreImp.Optimizer.MagicDo (inlineST, magicDoEff, magicDoEffect, magicDoST) -import Language.PureScript.CoreImp.Optimizer.TCO (tco) -import Language.PureScript.CoreImp.Optimizer.Unused (removeCodeAfterReturnStatements, removeUndefinedApp, removeUnusedEffectFreeVars) - --- | Apply a series of optimizer passes to simplified JavaScript code -optimize :: forall m. MonadSupply m => [Text] -> [[AST]] -> m [[AST]] -optimize exps jsDecls = removeUnusedEffectFreeVars exps <$> traverse (traverse go) jsDecls - where - go :: AST -> m AST - go js = do - js' <- untilFixedPoint (inlineFnComposition expander . inlineFnIdentity expander . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp . applyAll - [ inlineCommonValues expander - , inlineCommonOperators expander - ]) js - untilFixedPoint (return . tidyUp) . tco . inlineST - =<< untilFixedPoint (return . magicDoST expander) - =<< untilFixedPoint (return . magicDoEff expander) - =<< untilFixedPoint (return . magicDoEffect expander) js' - - tidyUp :: AST -> AST - tidyUp = applyAll - [ collapseNestedBlocks - , collapseNestedIfs - , removeCodeAfterReturnStatements - , removeUndefinedApp - , unThunk - , etaConvert - , evaluateIifes - , inlineVariables - ] - - expander = buildExpander (concat jsDecls) - -untilFixedPoint :: (Monad m, Eq a) => (a -> m a) -> a -> m a -untilFixedPoint f = go - where - go a = do - a' <- f a - if a' == a then return a' else go a' - --- | --- Take all top-level ASTs and return a function for expanding top-level --- variables during the various inlining steps in `optimize`. --- --- Everything that gets inlined as an optimization is of a form that would --- have been lifted to a top-level binding during CSE, so for purposes of --- inlining we can save some time by only expanding variables bound at that --- level and not worrying about any inner scopes. --- -buildExpander :: [AST] -> AST -> AST -buildExpander = replaceIdents . foldr go [] - where - go = \case - VariableIntroduction _ name (Just (NoEffects, e)) -> ((name, e) :) - _ -> id diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Blocks.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Blocks.hs deleted file mode 100644 index add5d7c9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Blocks.hs +++ /dev/null @@ -1,28 +0,0 @@ --- | Optimizer steps for simplifying JavaScript blocks -module Language.PureScript.CoreImp.Optimizer.Blocks - ( collapseNestedBlocks - , collapseNestedIfs - ) where - -import Prelude - -import Language.PureScript.CoreImp.AST (AST(..), BinaryOperator(..), everywhere) - --- | Collapse blocks which appear nested directly below another block -collapseNestedBlocks :: AST -> AST -collapseNestedBlocks = everywhere collapse where - collapse :: AST -> AST - collapse (Block ss sts) = Block ss (concatMap go sts) - collapse js = js - - go :: AST -> [AST] - go (Block _ sts) = sts - go s = [s] - -collapseNestedIfs :: AST -> AST -collapseNestedIfs = everywhere collapse where - collapse :: AST -> AST - collapse (IfElse _ (BooleanLiteral _ True) (Block _ [js]) _) = js - collapse (IfElse s1 cond1 (Block _ [IfElse s2 cond2 body Nothing]) Nothing) = - IfElse s1 (Binary s2 And cond1 cond2) body Nothing - collapse js = js diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Common.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Common.hs deleted file mode 100644 index ac63f6a2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Common.hs +++ /dev/null @@ -1,72 +0,0 @@ --- | Common functions used by the various optimizer phases -module Language.PureScript.CoreImp.Optimizer.Common where - -import Prelude - -import Data.Text (Text) -import Data.List (foldl') -import Data.Maybe (fromMaybe) - -import Language.PureScript.Crash (internalError) -import Language.PureScript.CoreImp.AST (AST(..), everything, everywhere) -import Language.PureScript.Names (ModuleName) -import Language.PureScript.PSString (PSString) - -applyAll :: [a -> a] -> a -> a -applyAll = foldl' (.) id - -replaceIdent :: Text -> AST -> AST -> AST -replaceIdent var1 js = everywhere replace - where - replace (Var _ var2) | var1 == var2 = js - replace other = other - -replaceIdents :: [(Text, AST)] -> AST -> AST -replaceIdents vars = everywhere replace - where - replace v@(Var _ var) = fromMaybe v $ lookup var vars - replace other = other - -isReassigned :: Text -> AST -> Bool -isReassigned var1 = everything (||) check - where - check :: AST -> Bool - check (Function _ _ args _) | var1 `elem` args = True - check (VariableIntroduction _ arg _) | var1 == arg = True - check (Assignment _ (Var _ arg) _) | var1 == arg = True - check (For _ arg _ _ _) | var1 == arg = True - check (ForIn _ arg _ _) | var1 == arg = True - check _ = False - -isRebound :: AST -> AST -> Bool -isRebound js d = any (\v -> isReassigned v d || isUpdated v d) (everything (++) variablesOf js) - where - variablesOf (Var _ var) = [var] - variablesOf _ = [] - -targetVariable :: AST -> Text -targetVariable (Var _ var) = var -targetVariable (Indexer _ _ tgt) = targetVariable tgt -targetVariable _ = internalError "Invalid argument to targetVariable" - -isUpdated :: Text -> AST -> Bool -isUpdated var1 = everything (||) check - where - check :: AST -> Bool - check (Assignment _ target _) | var1 == targetVariable target = True - check _ = False - -removeFromBlock :: ([AST] -> [AST]) -> AST -> AST -removeFromBlock go (Block ss sts) = Block ss (go sts) -removeFromBlock _ js = js - -pattern Ref :: (ModuleName, PSString) -> AST -pattern Ref pair <- (refPatternHelper -> Just pair) --- ideally: pattern Ref (moduleName, refName) <- ModuleAccessor _ moduleName refName --- but: https://gitlab.haskell.org/ghc/ghc/-/issues/12203 --- https://github.com/ghc-proposals/ghc-proposals/pull/138 - -refPatternHelper :: AST -> Maybe (ModuleName, PSString) -refPatternHelper = \case - ModuleAccessor _ moduleName refName -> Just (moduleName, refName) - _ -> Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Inliner.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Inliner.hs deleted file mode 100644 index e7314df9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Inliner.hs +++ /dev/null @@ -1,294 +0,0 @@ --- | This module performs basic inlining of known functions -module Language.PureScript.CoreImp.Optimizer.Inliner - ( inlineVariables - , inlineCommonValues - , inlineCommonOperators - , inlineFnComposition - , inlineFnIdentity - , inlineUnsafeCoerce - , inlineUnsafePartial - , etaConvert - , unThunk - , evaluateIifes - ) where - -import Prelude - -import Control.Monad.Supply.Class (MonadSupply, freshName) - -import Data.Either (rights) -import Data.Maybe (fromMaybe) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Names (ModuleName) -import Language.PureScript.PSString (PSString, mkString) -import Language.PureScript.CoreImp.AST (AST(..), BinaryOperator(..), InitializerEffects(..), UnaryOperator(..), everywhere, everywhereTopDown, everywhereTopDownM, getSourceSpan) -import Language.PureScript.CoreImp.Optimizer.Common (pattern Ref, applyAll, isReassigned, isRebound, isUpdated, removeFromBlock, replaceIdent, replaceIdents) -import Language.PureScript.AST (SourceSpan(..)) -import Language.PureScript.Constants.Libs qualified as C -import Language.PureScript.Constants.Prim qualified as C - --- TODO: Potential bug: --- Shouldn't just inline this case: { var x = 0; x.toFixed(10); } --- Needs to be: { 0..toFixed(10); } --- Probably needs to be fixed in pretty-printer instead. -shouldInline :: AST -> Bool -shouldInline (Var _ _) = True -shouldInline (ModuleAccessor _ _ _) = True -shouldInline (NumericLiteral _ _) = True -shouldInline (StringLiteral _ _) = True -shouldInline (BooleanLiteral _ _) = True -shouldInline (Indexer _ index val) = shouldInline index && shouldInline val -shouldInline _ = False - -etaConvert :: AST -> AST -etaConvert = everywhere convert - where - convert :: AST -> AST - convert (Block ss [Return _ (App _ (Function _ Nothing idents block@(Block _ body)) args)]) - | all shouldInline args && - not (any ((`isRebound` block) . Var Nothing) idents) && - not (any (`isRebound` block) args) - = Block ss (map (replaceIdents (zip idents args)) body) - convert (Function _ Nothing [] (Block _ [Return _ (App _ fn [])])) = fn - convert js = js - -unThunk :: AST -> AST -unThunk = everywhere convert - where - convert :: AST -> AST - convert (Block ss []) = Block ss [] - convert (Block ss jss) = - case last jss of - Return _ (App _ (Function _ Nothing [] (Block _ body)) []) -> Block ss $ init jss ++ body - _ -> Block ss jss - convert js = js - -evaluateIifes :: AST -> AST -evaluateIifes = everywhere convert - where - convert :: AST -> AST - convert (App _ (Function _ Nothing [] (Block _ [Return _ ret])) []) = ret - convert (App _ (Function _ Nothing idents (Block _ [Return ss ret])) []) - | not (any (`isReassigned` ret) idents) = replaceIdents (map (, Var ss C.S_undefined) idents) ret - convert js = js - -inlineVariables :: AST -> AST -inlineVariables = everywhere $ removeFromBlock go - where - go :: [AST] -> [AST] - go [] = [] - go (VariableIntroduction _ var (Just (_, js)) : sts) - | shouldInline js && not (any (isReassigned var) sts) && not (any (isRebound js) sts) && not (any (isUpdated var) sts) = - go (map (replaceIdent var js) sts) - go (s:sts) = s : go sts - -inlineCommonValues :: (AST -> AST) -> AST -> AST -inlineCommonValues expander = everywhere convert - where - convert :: AST -> AST - convert (expander -> App ss (Ref fn) [Ref dict]) - | dict `elem` [C.P_semiringNumber, C.P_semiringInt], C.P_zero <- fn = NumericLiteral ss (Left 0) - | dict `elem` [C.P_semiringNumber, C.P_semiringInt], C.P_one <- fn = NumericLiteral ss (Left 1) - | C.P_boundedBoolean <- dict, C.P_bottom <- fn = BooleanLiteral ss False - | C.P_boundedBoolean <- dict, C.P_top <- fn = BooleanLiteral ss True - convert (App ss (expander -> App _ (Ref C.P_negate) [Ref C.P_ringInt]) [x]) - = Binary ss BitwiseOr (Unary ss Negate x) (NumericLiteral ss (Left 0)) - convert (App ss (App _ (expander -> App _ (Ref fn) [Ref dict]) [x]) [y]) - | C.P_semiringInt <- dict, C.P_add <- fn = intOp ss Add x y - | C.P_semiringInt <- dict, C.P_mul <- fn = intOp ss Multiply x y - | C.P_ringInt <- dict, C.P_sub <- fn = intOp ss Subtract x y - convert other = other - intOp ss op x y = Binary ss BitwiseOr (Binary ss op x y) (NumericLiteral ss (Left 0)) - -inlineCommonOperators :: (AST -> AST) -> AST -> AST -inlineCommonOperators expander = everywhereTopDown $ applyAll $ - [ binary C.P_semiringNumber C.P_add Add - , binary C.P_semiringNumber C.P_mul Multiply - - , binary C.P_ringNumber C.P_sub Subtract - , unary C.P_ringNumber C.P_negate Negate - - , binary C.P_euclideanRingNumber C.P_div Divide - - , binary C.P_eqNumber C.P_eq EqualTo - , binary C.P_eqNumber C.P_notEq NotEqualTo - , binary C.P_eqInt C.P_eq EqualTo - , binary C.P_eqInt C.P_notEq NotEqualTo - , binary C.P_eqString C.P_eq EqualTo - , binary C.P_eqString C.P_notEq NotEqualTo - , binary C.P_eqChar C.P_eq EqualTo - , binary C.P_eqChar C.P_notEq NotEqualTo - , binary C.P_eqBoolean C.P_eq EqualTo - , binary C.P_eqBoolean C.P_notEq NotEqualTo - - , binary C.P_ordBoolean C.P_lessThan LessThan - , binary C.P_ordBoolean C.P_lessThanOrEq LessThanOrEqualTo - , binary C.P_ordBoolean C.P_greaterThan GreaterThan - , binary C.P_ordBoolean C.P_greaterThanOrEq GreaterThanOrEqualTo - , binary C.P_ordChar C.P_lessThan LessThan - , binary C.P_ordChar C.P_lessThanOrEq LessThanOrEqualTo - , binary C.P_ordChar C.P_greaterThan GreaterThan - , binary C.P_ordChar C.P_greaterThanOrEq GreaterThanOrEqualTo - , binary C.P_ordInt C.P_lessThan LessThan - , binary C.P_ordInt C.P_lessThanOrEq LessThanOrEqualTo - , binary C.P_ordInt C.P_greaterThan GreaterThan - , binary C.P_ordInt C.P_greaterThanOrEq GreaterThanOrEqualTo - , binary C.P_ordNumber C.P_lessThan LessThan - , binary C.P_ordNumber C.P_lessThanOrEq LessThanOrEqualTo - , binary C.P_ordNumber C.P_greaterThan GreaterThan - , binary C.P_ordNumber C.P_greaterThanOrEq GreaterThanOrEqualTo - , binary C.P_ordString C.P_lessThan LessThan - , binary C.P_ordString C.P_lessThanOrEq LessThanOrEqualTo - , binary C.P_ordString C.P_greaterThan GreaterThan - , binary C.P_ordString C.P_greaterThanOrEq GreaterThanOrEqualTo - - , binary C.P_semigroupString C.P_append Add - - , binary C.P_heytingAlgebraBoolean C.P_conj And - , binary C.P_heytingAlgebraBoolean C.P_disj Or - , unary C.P_heytingAlgebraBoolean C.P_not Not - - , binary' C.P_or BitwiseOr - , binary' C.P_and BitwiseAnd - , binary' C.P_xor BitwiseXor - , binary' C.P_shl ShiftLeft - , binary' C.P_shr ShiftRight - , binary' C.P_zshr ZeroFillShiftRight - , unary' C.P_complement BitwiseNot - - , inlineNonClassFunction (isModFnWithDict C.P_unsafeIndex) $ flip (Indexer Nothing) - ] ++ - [ fn | i <- [0..10], fn <- [ mkFn i, runFn i ] ] ++ - [ fn | i <- [0..10], fn <- [ mkEffFn C.P_mkEffFn i, runEffFn C.P_runEffFn i ] ] ++ - [ fn | i <- [0..10], fn <- [ mkEffFn C.P_mkEffectFn i, runEffFn C.P_runEffectFn i ] ] ++ - [ fn | i <- [0..10], fn <- [ mkEffFn C.P_mkSTFn i, runEffFn C.P_runSTFn i ] ] - where - binary :: (ModuleName, PSString) -> (ModuleName, PSString) -> BinaryOperator -> AST -> AST - binary dict fn op = convert where - convert :: AST -> AST - convert (App ss (App _ (expander -> App _ (Ref fn') [Ref dict']) [x]) [y]) | dict == dict', fn == fn' = Binary ss op x y - convert other = other - binary' :: (ModuleName, PSString) -> BinaryOperator -> AST -> AST - binary' fn op = convert where - convert :: AST -> AST - convert (App ss (App _ (Ref fn') [x]) [y]) | fn == fn' = Binary ss op x y - convert other = other - unary :: (ModuleName, PSString) -> (ModuleName, PSString) -> UnaryOperator -> AST -> AST - unary dict fn op = convert where - convert :: AST -> AST - convert (App ss (expander -> App _ (Ref fn') [Ref dict']) [x]) | dict == dict', fn == fn' = Unary ss op x - convert other = other - unary' :: (ModuleName, PSString) -> UnaryOperator -> AST -> AST - unary' fn op = convert where - convert :: AST -> AST - convert (App ss (Ref fn') [x]) | fn == fn' = Unary ss op x - convert other = other - - mkFn :: Int -> AST -> AST - mkFn = mkFn' C.P_mkFn $ \ss1 ss2 ss3 args js -> - Function ss1 Nothing args (Block ss2 [Return ss3 js]) - - mkEffFn :: (ModuleName, PSString) -> Int -> AST -> AST - mkEffFn mkFn_ = mkFn' mkFn_ $ \ss1 ss2 ss3 args js -> - Function ss1 Nothing args (Block ss2 [Return ss3 (App ss3 js [])]) - - mkFn' :: (ModuleName, PSString) -> (Maybe SourceSpan -> Maybe SourceSpan -> Maybe SourceSpan -> [Text] -> AST -> AST) -> Int -> AST -> AST - mkFn' mkFn_ res 0 = convert where - convert :: AST -> AST - convert (App _ (Ref mkFnN) [Function s1 Nothing [_] (Block s2 [Return s3 js])]) | isNFn mkFn_ 0 mkFnN = - res s1 s2 s3 [] js - convert other = other - mkFn' mkFn_ res n = convert where - convert :: AST -> AST - convert orig@(App ss (Ref mkFnN) [fn]) | isNFn mkFn_ n mkFnN = - case collectArgs n [] fn of - Just (args, [Return ss' ret]) -> res ss ss ss' args ret - _ -> orig - convert other = other - collectArgs :: Int -> [Text] -> AST -> Maybe ([Text], [AST]) - collectArgs 1 acc (Function _ Nothing [oneArg] (Block _ js)) | length acc == n - 1 = Just (reverse (oneArg : acc), js) - collectArgs m acc (Function _ Nothing [oneArg] (Block _ [Return _ ret])) = collectArgs (m - 1) (oneArg : acc) ret - collectArgs _ _ _ = Nothing - - isNFn :: (ModuleName, PSString) -> Int -> (ModuleName, PSString) -> Bool - isNFn prefix n fn = fmap (<> mkString (T.pack $ show n)) prefix == fn - - runFn :: Int -> AST -> AST - runFn = runFn' C.P_runFn App - - runEffFn :: (ModuleName, PSString) -> Int -> AST -> AST - runEffFn runFn_ = runFn' runFn_ $ \ss fn acc -> - Function ss Nothing [] (Block ss [Return ss (App ss fn acc)]) - - runFn' :: (ModuleName, PSString) -> (Maybe SourceSpan -> AST -> [AST] -> AST) -> Int -> AST -> AST - runFn' runFn_ res n = convert where - convert :: AST -> AST - convert js = fromMaybe js $ go n [] js - - go :: Int -> [AST] -> AST -> Maybe AST - go 0 acc (App ss (Ref runFnN) [fn]) | isNFn runFn_ n runFnN && length acc == n = - Just $ res ss fn acc - go m acc (App _ lhs [arg]) = go (m - 1) (arg : acc) lhs - go _ _ _ = Nothing - - inlineNonClassFunction :: (AST -> Bool) -> (AST -> AST -> AST) -> AST -> AST - inlineNonClassFunction p f = convert where - convert :: AST -> AST - convert (App _ (App _ op' [x]) [y]) | p op' = f x y - convert other = other - - isModFnWithDict :: (ModuleName, PSString) -> AST -> Bool - isModFnWithDict fn (App _ (Ref fn') [Var _ _]) = fn == fn' - isModFnWithDict _ _ = False - --- (f <<< g $ x) = f (g x) --- (f <<< g) = \x -> f (g x) -inlineFnComposition :: forall m. MonadSupply m => (AST -> AST) -> AST -> m AST -inlineFnComposition expander = everywhereTopDownM convert - where - convert :: AST -> m AST - convert (App s1 (App s2 (App _ (expander -> App _ (Ref fn) [Ref C.P_semigroupoidFn]) [x]) [y]) [z]) - | C.P_compose <- fn = return $ App s1 x [App s2 y [z]] - | C.P_composeFlipped <- fn = return $ App s2 y [App s1 x [z]] - convert app@(App ss (App _ (expander -> App _ (Ref fn) [Ref C.P_semigroupoidFn]) _) _) - | fn `elem` [C.P_compose, C.P_composeFlipped] = mkApps ss <$> goApps app <*> freshName - convert other = return other - - mkApps :: Maybe SourceSpan -> [Either AST (Text, AST)] -> Text -> AST - mkApps ss fns a = App ss (Function ss Nothing [] (Block ss $ vars <> [Return Nothing comp])) [] - where - vars = uncurry (VariableIntroduction ss) . fmap (Just . (UnknownEffects, )) <$> rights fns - comp = Function ss Nothing [a] (Block ss [Return Nothing apps]) - apps = foldr (\fn acc -> App ss (mkApp fn) [acc]) (Var ss a) fns - - mkApp :: Either AST (Text, AST) -> AST - mkApp = either id $ \(name, arg) -> Var (getSourceSpan arg) name - - goApps :: AST -> m [Either AST (Text, AST)] - goApps (App _ (App _ (expander -> App _ (Ref fn) [Ref C.P_semigroupoidFn]) [x]) [y]) - | C.P_compose <- fn = mappend <$> goApps x <*> goApps y - | C.P_composeFlipped <- fn = mappend <$> goApps y <*> goApps x - goApps app@App {} = pure . Right . (,app) <$> freshName - goApps other = pure [Left other] - -inlineFnIdentity :: (AST -> AST) -> AST -> AST -inlineFnIdentity expander = everywhereTopDown convert - where - convert :: AST -> AST - convert (App _ (expander -> App _ (Ref C.P_identity) [Ref C.P_categoryFn]) [x]) = x - convert other = other - -inlineUnsafeCoerce :: AST -> AST -inlineUnsafeCoerce = everywhereTopDown convert where - convert (App _ (Ref C.P_unsafeCoerce) [ comp ]) = comp - convert other = other - -inlineUnsafePartial :: AST -> AST -inlineUnsafePartial = everywhereTopDown convert where - convert (App ss (Ref C.P_unsafePartial) [ comp ]) - -- Apply to undefined here, the application should be optimized away - -- if it is safe to do so - = App ss comp [ Var ss C.S_undefined ] - convert other = other diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/MagicDo.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/MagicDo.hs deleted file mode 100644 index b5916757..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/MagicDo.hs +++ /dev/null @@ -1,136 +0,0 @@ --- | This module implements the "Magic Do" optimization, which inlines calls to return --- and bind for the Eff monad, as well as some of its actions. -module Language.PureScript.CoreImp.Optimizer.MagicDo (magicDoEffect, magicDoEff, magicDoST, inlineST) where - -import Prelude -import Protolude (ordNub) - -import Data.Maybe (fromJust, isJust) - -import Language.PureScript.CoreImp.AST (AST(..), InitializerEffects(..), UnaryOperator(..), everything, everywhere, everywhereTopDown) -import Language.PureScript.CoreImp.Optimizer.Common (pattern Ref) -import Language.PureScript.Names (ModuleName) -import Language.PureScript.PSString (mkString) -import Language.PureScript.Constants.Libs qualified as C - --- | Inline type class dictionaries for >>= and return for the Eff monad --- --- E.g. --- --- Prelude[">>="](dict)(m1)(function(x) { --- return ...; --- }) --- --- becomes --- --- function __do { --- var x = m1(); --- ... --- } -magicDoEff :: (AST -> AST) -> AST -> AST -magicDoEff = magicDo C.M_Control_Monad_Eff C.effDictionaries - -magicDoEffect :: (AST -> AST) -> AST -> AST -magicDoEffect = magicDo C.M_Effect C.effectDictionaries - -magicDoST :: (AST -> AST) -> AST -> AST -magicDoST = magicDo C.M_Control_Monad_ST_Internal C.stDictionaries - -magicDo :: ModuleName -> C.EffectDictionaries -> (AST -> AST) -> AST -> AST -magicDo effectModule C.EffectDictionaries{..} expander = everywhereTopDown convert - where - -- The name of the function block which is added to denote a do block - fnName = "__do" - -- Desugar monomorphic calls to >>= and return for the Eff monad - convert :: AST -> AST - -- Desugar pure - convert (App _ (App _ pure' [val]) []) | isPure pure' = val - -- Desugar discard - convert (App _ (App _ bind [m]) [Function s1 Nothing [] (Block s2 js)]) | isDiscard bind = - Function s1 (Just fnName) [] $ Block s2 (App s2 m [] : map applyReturns js ) - -- Desugar bind to wildcard - convert (App _ (App _ bind [m]) [Function s1 Nothing [] (Block s2 js)]) - | isBind bind = - Function s1 (Just fnName) [] $ Block s2 (App s2 m [] : map applyReturns js ) - -- Desugar bind - convert (App _ (App _ bind [m]) [Function s1 Nothing [arg] (Block s2 js)]) | isBind bind = - Function s1 (Just fnName) [] $ Block s2 (VariableIntroduction s2 arg (Just (UnknownEffects, App s2 m [])) : map applyReturns js) - -- Desugar untilE - convert (App s1 (App _ f [arg]) []) | isEffFunc edUntil f = - App s1 (Function s1 Nothing [] (Block s1 [ While s1 (Unary s1 Not (App s1 arg [])) (Block s1 []), Return s1 $ ObjectLiteral s1 []])) [] - -- Desugar whileE - convert (App _ (App _ (App s1 f [arg1]) [arg2]) []) | isEffFunc edWhile f = - App s1 (Function s1 Nothing [] (Block s1 [ While s1 (App s1 arg1 []) (Block s1 [ App s1 arg2 [] ]), Return s1 $ ObjectLiteral s1 []])) [] - -- Inline __do returns - convert (Return _ (App _ (Function _ (Just ident) [] body) [])) | ident == fnName = body - -- Inline double applications - convert (App _ (App s1 (Function s2 Nothing [] (Block ss body)) []) []) = - App s1 (Function s2 Nothing [] (Block ss (applyReturns `fmap` body))) [] - convert other = other - -- Check if an expression represents a monomorphic call to >>= for the Eff monad - isBind (expander -> App _ (Ref C.P_bind) [Ref dict]) = (effectModule, edBindDict) == dict - isBind _ = False - -- Check if an expression represents a call to @discard@ - isDiscard (expander -> App _ (expander -> App _ (Ref C.P_discard) [Ref C.P_discardUnit]) [Ref dict]) = (effectModule, edBindDict) == dict - isDiscard _ = False - -- Check if an expression represents a monomorphic call to pure or return for the Eff applicative - isPure (expander -> App _ (Ref C.P_pure) [Ref dict]) = (effectModule, edApplicativeDict) == dict - isPure _ = False - -- Check if an expression represents a function in the Effect module - isEffFunc name (Ref fn) = (effectModule, name) == fn - isEffFunc _ _ = False - - applyReturns :: AST -> AST - applyReturns (Return ss ret) = Return ss (App ss ret []) - applyReturns (Block ss jss) = Block ss (map applyReturns jss) - applyReturns (While ss cond js) = While ss cond (applyReturns js) - applyReturns (For ss v lo hi js) = For ss v lo hi (applyReturns js) - applyReturns (ForIn ss v xs js) = ForIn ss v xs (applyReturns js) - applyReturns (IfElse ss cond t f) = IfElse ss cond (applyReturns t) (applyReturns `fmap` f) - applyReturns other = other - --- | Inline functions in the ST module -inlineST :: AST -> AST -inlineST = everywhere convertBlock - where - -- Look for run blocks and inline the STRefs there. - -- If all STRefs are used in the scope of the same run, only using { read, write, modify } then - -- we can be more aggressive about inlining, and actually turn STRefs into local variables. - convertBlock (App s1 (Ref C.P_run) [arg]) = - let refs = ordNub . findSTRefsIn $ arg - usages = findAllSTUsagesIn arg - allUsagesAreLocalVars = all (\u -> let v = toVar u in isJust v && fromJust v `elem` refs) usages - localVarsDoNotEscape = all (\r -> length (r `appearingIn` arg) == length (filter (\u -> let v = toVar u in v == Just r) usages)) refs - in App s1 (everywhere (convert (allUsagesAreLocalVars && localVarsDoNotEscape)) arg) [] - convertBlock other = other - -- Convert a block in a safe way, preserving object wrappers of references, - -- or in a more aggressive way, turning wrappers into local variables depending on the - -- agg(ressive) parameter. - convert agg (App s1 (Ref C.P_new) [arg]) = - Function s1 Nothing [] (Block s1 [Return s1 $ if agg then arg else ObjectLiteral s1 [(mkString C.stRefValue, arg)]]) - convert agg (App _ (App s1 (Ref C.P_read) [ref]) []) = - if agg then ref else Indexer s1 (StringLiteral s1 C.stRefValue) ref - convert agg (App _ (App _ (App s1 (Ref C.P_write) [arg]) [ref]) []) = - if agg then Assignment s1 ref arg else Assignment s1 (Indexer s1 (StringLiteral s1 C.stRefValue) ref) arg - convert agg (App _ (App _ (App s1 (Ref C.P_modify) [func]) [ref]) []) = - if agg then Assignment s1 ref (App s1 func [ref]) else Assignment s1 (Indexer s1 (StringLiteral s1 C.stRefValue) ref) (App s1 func [Indexer s1 (StringLiteral s1 C.stRefValue) ref]) - convert _ other = other - -- Find all ST Refs initialized in this block - findSTRefsIn = everything (++) isSTRef - where - isSTRef (VariableIntroduction _ ident (Just (_, App _ (App _ (Ref C.P_new) [_]) []))) = [ident] - isSTRef _ = [] - -- Find all STRefs used as arguments to read, write, modify - findAllSTUsagesIn = everything (++) isSTUsage - where - isSTUsage (App _ (App _ (Ref C.P_read) [ref]) []) = [ref] - isSTUsage (App _ (App _ (App _ (Ref f) [_]) [ref]) []) | f `elem` [C.P_write, C.P_modify] = [ref] - isSTUsage _ = [] - -- Find all uses of a variable - appearingIn ref = everything (++) isVar - where - isVar e@(Var _ v) | v == ref = [e] - isVar _ = [] - -- Convert a AST value to a String if it is a Var - toVar (Var _ v) = Just v - toVar _ = Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/TCO.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/TCO.hs deleted file mode 100644 index db133f5a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/TCO.hs +++ /dev/null @@ -1,191 +0,0 @@ --- | This module implements tail call elimination. -module Language.PureScript.CoreImp.Optimizer.TCO (tco) where - -import Prelude - -import Control.Applicative (empty) -import Control.Monad (guard) -import Control.Monad.State (State, evalState, gets, modify) -import Data.Functor (($>)) -import Data.Set qualified as S -import Data.Text (Text, pack) -import Language.PureScript.CoreImp.AST (AST(..), InitializerEffects(..), UnaryOperator(..), everything, everywhereTopDownM) -import Language.PureScript.AST.SourcePos (SourceSpan) -import Safe (headDef, tailSafe) - --- | Eliminate tail calls -tco :: AST -> AST -tco = flip evalState 0 . everywhereTopDownM convert where - tcoVar :: Text -> Text - tcoVar arg = "$tco_var_" <> arg - - copyVar :: Text -> Text - copyVar arg = "$copy_" <> arg - - tcoDoneM :: State Int Text - tcoDoneM = gets $ \count -> "$tco_done" <> - if count == 0 then "" else pack . show $ count - - tcoLoop :: Text - tcoLoop = "$tco_loop" - - tcoResult :: Text - tcoResult = "$tco_result" - - convert :: AST -> State Int AST - convert (VariableIntroduction ss name (Just (p, fn@Function {}))) - | Just trFns <- findTailRecursiveFns name arity body' - = VariableIntroduction ss name . Just . (p,) . replace <$> toLoop trFns name arity outerArgs innerArgs body' - where - innerArgs = headDef [] argss - outerArgs = concat . reverse $ tailSafe argss - arity = length argss - -- this is the number of calls, not the number of arguments, if there's - -- ever a practical difference. - (argss, body', replace) = topCollectAllFunctionArgs [] id fn - convert js = pure js - - rewriteFunctionsWith :: ([Text] -> [Text]) -> [[Text]] -> (AST -> AST) -> AST -> ([[Text]], AST, AST -> AST) - rewriteFunctionsWith argMapper = collectAllFunctionArgs - where - collectAllFunctionArgs allArgs f (Function s1 ident args (Block s2 (body@(Return _ _):_))) = - collectAllFunctionArgs (args : allArgs) (\b -> f (Function s1 ident (argMapper args) (Block s2 [b]))) body - collectAllFunctionArgs allArgs f (Function ss ident args body@(Block _ _)) = - (args : allArgs, body, f . Function ss ident (argMapper args)) - collectAllFunctionArgs allArgs f (Return s1 (Function s2 ident args (Block s3 [body]))) = - collectAllFunctionArgs (args : allArgs) (\b -> f (Return s1 (Function s2 ident (argMapper args) (Block s3 [b])))) body - collectAllFunctionArgs allArgs f (Return s1 (Function s2 ident args body@(Block _ _))) = - (args : allArgs, body, f . Return s1 . Function s2 ident (argMapper args)) - collectAllFunctionArgs allArgs f body = (allArgs, body, f) - - topCollectAllFunctionArgs :: [[Text]] -> (AST -> AST) -> AST -> ([[Text]], AST, AST -> AST) - topCollectAllFunctionArgs = rewriteFunctionsWith (map copyVar) - - innerCollectAllFunctionArgs :: [[Text]] -> (AST -> AST) -> AST -> ([[Text]], AST, AST -> AST) - innerCollectAllFunctionArgs = rewriteFunctionsWith id - - countReferences :: Text -> AST -> Int - countReferences ident = everything (+) match where - match :: AST -> Int - match (Var _ ident') | ident == ident' = 1 - match _ = 0 - - -- If `ident` is a tail-recursive function, returns a set of identifiers - -- that are locally bound to functions participating in the tail recursion. - -- Otherwise, returns Nothing. - findTailRecursiveFns :: Text -> Int -> AST -> Maybe (S.Set Text) - findTailRecursiveFns ident arity js = guard (countReferences ident js > 0) *> go (S.empty, S.singleton (ident, arity)) - where - - go :: (S.Set Text, S.Set (Text, Int)) -> Maybe (S.Set Text) - go (known, required) = - case S.minView required of - Just (r, required') -> do - required'' <- findTailPositionDeps r js - go (S.insert (fst r) known, required' <> S.filter (not . (`S.member` known) . fst) required'') - Nothing -> - pure known - - -- Returns set of identifiers (with their arities) that need to be used - -- exclusively in tail calls using their full arity in order for this - -- identifier to be considered in tail position (or Nothing if this - -- identifier is used somewhere not as a tail call with full arity). - findTailPositionDeps :: (Text, Int) -> AST -> Maybe (S.Set (Text, Int)) - findTailPositionDeps (ident, arity) = allInTailPosition where - countSelfReferences = countReferences ident - - allInTailPosition (Return _ expr) - | isSelfCall ident arity expr = guard (countSelfReferences expr == 1) $> S.empty - | otherwise = guard (countSelfReferences expr == 0) $> S.empty - allInTailPosition (While _ js1 body) - = guard (countSelfReferences js1 == 0) *> allInTailPosition body - allInTailPosition (For _ _ js1 js2 body) - = guard (countSelfReferences js1 == 0 && countSelfReferences js2 == 0) *> allInTailPosition body - allInTailPosition (ForIn _ _ js1 body) - = guard (countSelfReferences js1 == 0) *> allInTailPosition body - allInTailPosition (IfElse _ js1 body el) - = guard (countSelfReferences js1 == 0) *> liftA2 mappend (allInTailPosition body) (foldMapA allInTailPosition el) - allInTailPosition (Block _ body) - = foldMapA allInTailPosition body - allInTailPosition (Throw _ js1) - = guard (countSelfReferences js1 == 0) $> S.empty - allInTailPosition (ReturnNoResult _) - = pure S.empty - allInTailPosition (VariableIntroduction _ _ Nothing) - = pure S.empty - allInTailPosition (VariableIntroduction _ ident' (Just (_, js1))) - | countSelfReferences js1 == 0 = pure S.empty - | Function _ Nothing _ _ <- js1 - , (argss, body, _) <- innerCollectAllFunctionArgs [] id js1 - = S.insert (ident', length argss) <$> allInTailPosition body - | otherwise = empty - allInTailPosition (Assignment _ _ js1) - = guard (countSelfReferences js1 == 0) $> S.empty - allInTailPosition (Comment _ js1) - = allInTailPosition js1 - allInTailPosition _ - = empty - - toLoop :: S.Set Text -> Text -> Int -> [Text] -> [Text] -> AST -> State Int AST - toLoop trFns ident arity outerArgs innerArgs js = do - tcoDone <- tcoDoneM - modify (+ 1) - - let - markDone :: Maybe SourceSpan -> AST - markDone ss = Assignment ss (Var ss tcoDone) (BooleanLiteral ss True) - - loopify :: AST -> AST - loopify (Return ss ret) - | isSelfCall ident arity ret = - let - allArgumentValues = concat $ collectArgs [] ret - in - Block ss $ - zipWith (\val arg -> - Assignment ss (Var ss (tcoVar arg)) val) allArgumentValues outerArgs - ++ zipWith (\val arg -> - Assignment ss (Var ss (copyVar arg)) val) (drop (length outerArgs) allArgumentValues) innerArgs - ++ [ ReturnNoResult ss ] - | isIndirectSelfCall ret = Return ss ret - | otherwise = Block ss [ markDone ss, Return ss ret ] - loopify (ReturnNoResult ss) = Block ss [ markDone ss, ReturnNoResult ss ] - loopify (While ss cond body) = While ss cond (loopify body) - loopify (For ss i js1 js2 body) = For ss i js1 js2 (loopify body) - loopify (ForIn ss i js1 body) = ForIn ss i js1 (loopify body) - loopify (IfElse ss cond body el) = IfElse ss cond (loopify body) (fmap loopify el) - loopify (Block ss body) = Block ss (map loopify body) - loopify (VariableIntroduction ss f (Just (p, fn@(Function _ Nothing _ _)))) - | (_, body, replace) <- innerCollectAllFunctionArgs [] id fn - , f `S.member` trFns = VariableIntroduction ss f (Just (p, replace (loopify body))) - loopify other = other - - pure $ Block rootSS $ - map (\arg -> VariableIntroduction rootSS (tcoVar arg) (Just (UnknownEffects, Var rootSS (copyVar arg)))) outerArgs ++ - [ VariableIntroduction rootSS tcoDone (Just (UnknownEffects, BooleanLiteral rootSS False)) - , VariableIntroduction rootSS tcoResult Nothing - , Function rootSS (Just tcoLoop) (outerArgs ++ innerArgs) (Block rootSS [loopify js]) - , While rootSS (Unary rootSS Not (Var rootSS tcoDone)) - (Block rootSS - [Assignment rootSS (Var rootSS tcoResult) (App rootSS (Var rootSS tcoLoop) (map (Var rootSS . tcoVar) outerArgs ++ map (Var rootSS . copyVar) innerArgs))]) - , Return rootSS (Var rootSS tcoResult) - ] - where - rootSS = Nothing - - collectArgs :: [[AST]] -> AST -> [[AST]] - collectArgs acc (App _ fn args') = collectArgs (args' : acc) fn - collectArgs acc _ = acc - - isIndirectSelfCall :: AST -> Bool - isIndirectSelfCall (App _ (Var _ ident') _) = ident' `S.member` trFns - isIndirectSelfCall (App _ fn _) = isIndirectSelfCall fn - isIndirectSelfCall _ = False - - isSelfCall :: Text -> Int -> AST -> Bool - isSelfCall ident 1 (App _ (Var _ ident') _) = ident == ident' - isSelfCall ident arity (App _ fn _) = isSelfCall ident (arity - 1) fn - isSelfCall _ _ _ = False - -foldMapA :: (Applicative f, Monoid w, Foldable t) => (a -> f w) -> t a -> f w -foldMapA f = foldr (liftA2 mappend . f) (pure mempty) diff --git a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Unused.hs b/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Unused.hs deleted file mode 100644 index 7b7acd12..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/CoreImp/Optimizer/Unused.hs +++ /dev/null @@ -1,55 +0,0 @@ --- | Removes unused variables -module Language.PureScript.CoreImp.Optimizer.Unused - ( removeCodeAfterReturnStatements - , removeUndefinedApp - , removeUnusedEffectFreeVars - ) where - -import Prelude - -import Control.Monad (filterM) -import Data.Monoid (Any(..)) -import Data.Set qualified as S -import Data.Text (Text) - -import Language.PureScript.CoreImp.AST (AST(..), InitializerEffects(..), everything, everywhere) -import Language.PureScript.CoreImp.Optimizer.Common (removeFromBlock) -import Language.PureScript.Constants.Prim qualified as C - -removeCodeAfterReturnStatements :: AST -> AST -removeCodeAfterReturnStatements = everywhere (removeFromBlock go) - where - go :: [AST] -> [AST] - go jss = - case break isReturn jss of - (_, []) -> jss - (body, ret : _ ) -> body ++ [ret] - - isReturn (Return _ _) = True - isReturn (ReturnNoResult _) = True - isReturn _ = False - -removeUndefinedApp :: AST -> AST -removeUndefinedApp = everywhere convert - where - convert (App ss fn [Var _ C.S_undefined]) = App ss fn [] - convert js = js - -removeUnusedEffectFreeVars :: [Text] -> [[AST]] -> [[AST]] -removeUnusedEffectFreeVars exps = loop - where - expsSet = S.fromList exps - - loop :: [[AST]] -> [[AST]] - loop asts = if changed then loop (filter (not . null) asts') else asts - where - used = expsSet <> foldMap (foldMap (everything (<>) (\case Var _ x -> S.singleton x; _ -> S.empty))) asts - (Any changed, asts') = traverse (filterM (anyFalses . isInUsedSet used)) asts - - isInUsedSet :: S.Set Text -> AST -> Bool - isInUsedSet used = \case - VariableIntroduction _ var (Just (NoEffects, _)) -> var `S.member` used - _ -> True - - anyFalses :: Bool -> (Any, Bool) - anyFalses x = (Any (not x), x) diff --git a/claude-help/original-compiler/src/Language/PureScript/Crash.hs b/claude-help/original-compiler/src/Language/PureScript/Crash.hs deleted file mode 100644 index 9b041262..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Crash.hs +++ /dev/null @@ -1,12 +0,0 @@ -module Language.PureScript.Crash (HasCallStack, internalError) where - -import Prelude - -import GHC.Stack (HasCallStack) - --- | Exit with an error message and a crash report link. -internalError :: HasCallStack => String -> a -internalError = - error - . ("An internal error occurred during compilation: " ++) - . (++ "\nPlease report this at https://github.com/purescript/purescript/issues") diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs.hs b/claude-help/original-compiler/src/Language/PureScript/Docs.hs deleted file mode 100644 index 417c98f3..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs.hs +++ /dev/null @@ -1,16 +0,0 @@ - --- | Data types and functions for rendering generated documentation from --- PureScript code, in a variety of formats. - -module Language.PureScript.Docs - ( module Docs - ) where - -import Language.PureScript.Docs.Collect as Docs -import Language.PureScript.Docs.Convert as Docs -import Language.PureScript.Docs.Prim as Docs -import Language.PureScript.Docs.Render as Docs -import Language.PureScript.Docs.RenderedCode as Docs -import Language.PureScript.Docs.Tags as Docs -import Language.PureScript.Docs.Types as Docs -import Language.PureScript.Docs.Css as Docs diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/AsHtml.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/AsHtml.hs deleted file mode 100644 index df7b55f3..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/AsHtml.hs +++ /dev/null @@ -1,354 +0,0 @@ - --- | Functions for rendering generated documentation from PureScript code as --- HTML. - -module Language.PureScript.Docs.AsHtml ( - HtmlOutput(..), - HtmlOutputModule(..), - HtmlRenderContext(..), - nullRenderContext, - packageAsHtml, - moduleAsHtml, - makeFragment, - renderMarkdown -) where - -import Prelude -import Control.Category ((>>>)) -import Control.Monad (unless) -import Data.Bifunctor (bimap) -import Data.Char (isUpper) -import Data.Either (isRight) -import Data.List.NonEmpty qualified as NE -import Data.Maybe (fromMaybe) -import Data.Foldable (for_) -import Data.String (fromString) - -import Data.Text (Text) -import Data.Text qualified as T - -import Text.Blaze.Html5 as H hiding (map) -import Text.Blaze.Html5.Attributes qualified as A -import Cheapskate qualified - -import Language.PureScript qualified as P - -import Language.PureScript.Docs.Types -import Language.PureScript.Docs.RenderedCode (Link(..), outputWith) -import Language.PureScript.Docs.Render qualified as Render -import Language.PureScript.CST qualified as CST - -data HtmlOutput a = HtmlOutput - { htmlIndex :: [(Maybe Char, a)] - , htmlModules :: [(P.ModuleName, HtmlOutputModule a)] - } - deriving (Show, Functor) - -data HtmlOutputModule a = HtmlOutputModule - { htmlOutputModuleLocals :: a - , htmlOutputModuleReExports :: [(InPackage P.ModuleName, a)] - } - deriving (Show, Functor) - -data HtmlRenderContext = HtmlRenderContext - { buildDocLink :: Namespace -> Text -> ContainingModule -> Maybe DocLink - , renderDocLink :: DocLink -> Text - , renderSourceLink :: P.SourceSpan -> Maybe Text - } - --- | --- An HtmlRenderContext for when you don't want to render any links. -nullRenderContext :: HtmlRenderContext -nullRenderContext = HtmlRenderContext - { buildDocLink = const (const (const Nothing)) - , renderDocLink = const "" - , renderSourceLink = const Nothing - } - -packageAsHtml - :: (InPackage P.ModuleName -> Maybe HtmlRenderContext) - -> Package x - -> HtmlOutput Html -packageAsHtml getHtmlCtx Package{..} = - HtmlOutput indexFile modules - where - indexFile = [] - modules = moduleAsHtml getHtmlCtx <$> pkgModules - -moduleAsHtml - :: (InPackage P.ModuleName -> Maybe HtmlRenderContext) - -> Module - -> (P.ModuleName, HtmlOutputModule Html) -moduleAsHtml getHtmlCtx Module{..} = (modName, HtmlOutputModule modHtml reexports) - where - modHtml = do - let r = fromMaybe nullRenderContext $ getHtmlCtx (Local modName) - in do - for_ modComments renderMarkdown - for_ modDeclarations (declAsHtml r) - reexports = - flip map modReExports $ \(pkg, decls) -> - let r = fromMaybe nullRenderContext $ getHtmlCtx pkg - in (pkg, foldMap (declAsHtml r) decls) - --- renderIndex :: LinksContext -> [(Maybe Char, Html)] --- renderIndex LinksContext{..} = go ctxBookmarks --- where --- go = takeLocals --- >>> groupIndex getIndex renderEntry --- >>> map (second (ul . mconcat)) --- --- getIndex (_, title_) = do --- c <- textHeadMay title_ --- guard (toUpper c `elem` ['A'..'Z']) --- pure c --- --- textHeadMay t = --- case T.length t of --- 0 -> Nothing --- _ -> Just (T.index t 0) --- --- renderEntry (mn, title_) = --- li $ do --- let url = T.pack (filePathFor mn `relativeTo` "index") <> "#" <> title_ --- code $ --- a ! A.href (v url) $ text title_ --- sp --- text ("(" <> P.runModuleName mn <> ")") --- --- groupIndex :: Ord i => (a -> Maybe i) -> (a -> b) -> [a] -> [(Maybe i, [b])] --- groupIndex f g = --- map (second DList.toList) . M.toList . foldr go' M.empty . sortBy (comparing f) --- where --- go' x = insertOrAppend (f x) (g x) --- insertOrAppend idx val m = --- let cur = M.findWithDefault DList.empty idx m --- new = DList.snoc cur val --- in M.insert idx new m - -declAsHtml :: HtmlRenderContext -> Declaration -> Html -declAsHtml r d@Declaration{..} = do - let declFragment = makeFragment (declInfoNamespace declInfo) declTitle - H.div ! A.class_ "decl" ! A.id (v (T.drop 1 declFragment)) $ do - h3 ! A.class_ "decl__title clearfix" $ do - a ! A.class_ "decl__anchor" ! A.href (v declFragment) $ "#" - H.span $ text declTitle - text "\x200b" -- Zero-width space to allow double-click selection of title - for_ declSourceSpan (linkToSource r) - - H.div ! A.class_ "decl__body" $ do - case declInfo of - AliasDeclaration fixity alias_ -> - renderAlias fixity alias_ - _ -> do - pre ! A.class_ "decl__signature" $ do - for_ declKind $ \kindInfo -> do - code ! A.class_ "decl__kind" $ do - codeAsHtml r (Render.renderKindSig declTitle kindInfo) - code $ codeAsHtml r (Render.renderDeclaration d) - - for_ declComments renderMarkdown - - let (instances, dctors, members) = partitionChildren declChildren - - unless (null dctors) $ do - h4 "Constructors" - renderChildren r dctors - - unless (null members) $ do - h4 "Members" - renderChildren r members - - unless (null instances) $ do - h4 "Instances" - renderChildren r instances - where - linkToSource :: HtmlRenderContext -> P.SourceSpan -> Html - linkToSource ctx srcspan = - maybe (return ()) go (renderSourceLink ctx srcspan) - where - go href = - H.span ! A.class_ "decl__source" $ - a ! A.href (v href) $ text "Source" - -renderChildren :: HtmlRenderContext -> [ChildDeclaration] -> Html -renderChildren _ [] = return () -renderChildren r xs = ul $ mapM_ item xs - where - item decl = - li ! A.id (v (T.drop 1 (fragment decl))) $ do - renderCode decl - for_ (cdeclComments decl) $ \coms -> - H.div ! A.class_ "decl__child_comments" $ renderMarkdown coms - - fragment decl = makeFragment (childDeclInfoNamespace (cdeclInfo decl)) (cdeclTitle decl) - renderCode = code . codeAsHtml r . Render.renderChildDeclaration - -codeAsHtml :: HtmlRenderContext -> RenderedCode -> Html -codeAsHtml r = outputWith elemAsHtml - where - elemAsHtml e = case e of - Syntax x -> - withClass "syntax" (text x) - Keyword x -> - withClass "keyword" (text x) - Space -> - text " " - Symbol ns name link_ -> - case link_ of - Link mn -> - let - class_ = - if startsWithUpper name then "ctor" else "ident" - target - | isOp name = - if ns == TypeLevel - then "type (" <> name <> ")" - else "(" <> name <> ")" - | otherwise = name - in - linkToDecl ns target mn (withClass class_ (text name)) - NoLink -> - text name - Role role -> - case role of - "nominal" -> renderRole describeNominal "decl__role_nominal" - "phantom" -> renderRole describePhantom "decl__role_phantom" - - -- representational is intentionally not rendered - "representational" -> toHtml ("" :: Text) - - x -> P.internalError $ "codeAsHtml: unknown value for role annotation: '" <> T.unpack x <> "'" - where - renderRole hoverTextContent className = - H.a ! A.href (v docRepoRolePage) ! A.target (v "_blank") ! A.class_ "decl__role" $ do - H.abbr ! A.class_ "decl__role_hover" ! A.title (v hoverTextContent) $ do - H.sub ! A.class_ className $ do - toHtml ("" :: Text) - - docRepoRolePage = - "https://github.com/purescript/documentation/blob/master/language/Roles.md" - - describeNominal = - "The 'nominal' role means this argument may not change when coercing the type." - describePhantom = - "The 'phantom' role means this argument can change freely when coercing the type." - - linkToDecl = linkToDeclaration r - - startsWithUpper :: Text -> Bool - startsWithUpper str = not (T.null str) && isUpper (T.index str 0) - - isOp = isRight . runParser CST.parseOperator - - runParser :: CST.Parser x -> Text -> Either String x - runParser p' = - bimap (CST.prettyPrintError . NE.head) snd - . CST.runTokenParser p' - . CST.lex - -renderLink :: HtmlRenderContext -> DocLink -> Html -> Html -renderLink r link_@DocLink{..} = - a ! A.href (v (renderDocLink r link_ <> fragmentFor link_)) - ! A.title (v fullyQualifiedName) - where - fullyQualifiedName = - P.runModuleName modName <> "." <> linkTitle - - modName = case linkLocation of - LocalModule m -> m - DepsModule _ _ m -> m - BuiltinModule m -> m - -makeFragment :: Namespace -> Text -> Text -makeFragment ns = (prefix <>) . escape - where - prefix = case ns of - TypeLevel -> "#t:" - ValueLevel -> "#v:" - - -- TODO - escape = id - -fragmentFor :: DocLink -> Text -fragmentFor l = makeFragment (linkNamespace l) (linkTitle l) - -linkToDeclaration :: - HtmlRenderContext -> - Namespace -> - Text -> - ContainingModule -> - Html -> - Html -linkToDeclaration r ns target containMn = - maybe id (renderLink r) (buildDocLink r ns target containMn) - -renderAlias :: P.Fixity -> FixityAlias -> Html -renderAlias (P.Fixity associativity precedence) alias_ = - p $ do - -- TODO: Render a link - toHtml $ "Operator alias for " <> P.showQualified showAliasName alias_ <> " " - em $ - text ("(" <> associativityStr <> " / precedence " <> T.pack (show precedence) <> ")") - where - showAliasName (Left valueAlias) = P.runProperName valueAlias - showAliasName (Right typeAlias) = case typeAlias of - (Left identifier) -> P.runIdent identifier - (Right properName) -> P.runProperName properName - associativityStr = case associativity of - P.Infixl -> "left-associative" - P.Infixr -> "right-associative" - P.Infix -> "non-associative" - --- | Render Markdown to HTML. Safe for untrusted input. Relative links are --- | removed. -renderMarkdown :: Text -> H.Html -renderMarkdown = - H.toMarkup . removeRelativeLinks . Cheapskate.markdown opts - where - opts = Cheapskate.def { Cheapskate.allowRawHtml = False } - -removeRelativeLinks :: Cheapskate.Doc -> Cheapskate.Doc -removeRelativeLinks = Cheapskate.walk go - where - go :: Cheapskate.Inlines -> Cheapskate.Inlines - go = (>>= stripRelatives) - - stripRelatives :: Cheapskate.Inline -> Cheapskate.Inlines - stripRelatives (Cheapskate.Link contents_ href _) - | isRelativeURI href = contents_ - stripRelatives other = pure other - - -- Tests for a ':' character in the first segment of a URI. - -- - -- See Section 4.2 of RFC 3986: - -- https://tools.ietf.org/html/rfc3986#section-4.2 - -- - -- >>> isRelativeURI "http://example.com/" == False - -- >>> isRelativeURI "mailto:me@example.com" == False - -- >>> isRelativeURI "foo/bar" == True - -- >>> isRelativeURI "/bar" == True - -- >>> isRelativeURI "./bar" == True - isRelativeURI :: Text -> Bool - isRelativeURI = - T.takeWhile (/= '/') >>> T.all (/= ':') - -v :: Text -> AttributeValue -v = toValue - -withClass :: String -> Html -> Html -withClass className = H.span ! A.class_ (fromString className) - -partitionChildren :: - [ChildDeclaration] -> - ([ChildDeclaration], [ChildDeclaration], [ChildDeclaration]) -partitionChildren = - reverseAll . foldl go ([], [], []) - where - go (instances, dctors, members) rcd = - case cdeclInfo rcd of - ChildInstance _ _ -> (rcd : instances, dctors, members) - ChildDataConstructor _ -> (instances, rcd : dctors, members) - ChildTypeClassMember _ -> (instances, dctors, rcd : members) - - reverseAll (xs, ys, zs) = (reverse xs, reverse ys, reverse zs) diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/AsMarkdown.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/AsMarkdown.hs deleted file mode 100644 index 6f8b80a9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/AsMarkdown.hs +++ /dev/null @@ -1,123 +0,0 @@ -module Language.PureScript.Docs.AsMarkdown - ( Docs - , runDocs - , moduleAsMarkdown - , codeToString - , declAsMarkdown - ) where - -import Prelude - -import Control.Monad (unless, zipWithM_) -import Control.Monad.Writer (Writer, tell, execWriter) - -import Data.Foldable (for_) -import Data.List (partition) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Docs.RenderedCode (RenderedCode, RenderedCodeElement(..), outputWith) -import Language.PureScript.Docs.Types (ChildDeclaration(..), ChildDeclarationInfo(..), Declaration(..), Module(..), ignorePackage) -import Language.PureScript.Docs.Render qualified as Render -import Language.PureScript.Names qualified as P - -moduleAsMarkdown :: Module -> Docs -moduleAsMarkdown Module{..} = do - headerLevel 2 $ "Module " <> P.runModuleName modName - spacer - for_ modComments tell' - mapM_ declAsMarkdown modDeclarations - spacer - for_ modReExports $ \(mn', decls) -> do - let mn = ignorePackage mn' - headerLevel 3 $ "Re-exported from " <> P.runModuleName mn <> ":" - spacer - mapM_ declAsMarkdown decls - -declAsMarkdown :: Declaration -> Docs -declAsMarkdown decl@Declaration{..} = do - headerLevel 4 (ticks declTitle) - spacer - - let (instances, children) = partition (isChildInstance . cdeclInfo) declChildren - fencedBlock $ do - tell' (codeToString $ Render.renderDeclaration decl) - zipWithM_ (\f c -> tell' (childToString f c)) (First : repeat NotFirst) children - spacer - - for_ declComments tell' - - unless (null instances) $ do - headerLevel 5 "Instances" - fencedBlock $ mapM_ (tell' . childToString NotFirst) instances - spacer - - where - isChildInstance (ChildInstance _ _) = True - isChildInstance _ = False - -codeToString :: RenderedCode -> Text -codeToString = outputWith elemAsMarkdown - where - elemAsMarkdown (Syntax x) = x - elemAsMarkdown (Keyword x) = x - elemAsMarkdown Space = " " - elemAsMarkdown (Symbol _ x _) = x - - -- roles aren't rendered in markdown - elemAsMarkdown (Role _) = "" - --- fixityAsMarkdown :: P.Fixity -> Docs --- fixityAsMarkdown (P.Fixity associativity precedence) = --- tell' $ concat [ "_" --- , associativityStr --- , " / precedence " --- , show precedence --- , "_" --- ] --- where --- associativityStr = case associativity of --- P.Infixl -> "left-associative" --- P.Infixr -> "right-associative" --- P.Infix -> "non-associative" - -childToString :: First -> ChildDeclaration -> Text -childToString f decl@ChildDeclaration{..} = - case cdeclInfo of - ChildDataConstructor _ -> - let c = if f == First then "=" else "|" - in " " <> c <> " " <> str - ChildTypeClassMember _ -> - " " <> str - ChildInstance _ _ -> - str - where - str = codeToString $ Render.renderChildDeclaration decl - -data First - = First - | NotFirst - deriving (Show, Eq, Ord) - -type Docs = Writer [Text] () - -runDocs :: Docs -> Text -runDocs = T.unlines . execWriter - -tell' :: Text -> Docs -tell' = tell . (:[]) - -spacer :: Docs -spacer = tell' "" - -headerLevel :: Int -> Text -> Docs -headerLevel level hdr = tell' (T.replicate level "#" <> " " <> hdr) - -fencedBlock :: Docs -> Docs -fencedBlock inner = do - tell' "``` purescript" - inner - tell' "```" - -ticks :: Text -> Text -ticks = ("`" <>) . (<> "`") diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Collect.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Collect.hs deleted file mode 100644 index 125809d1..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Collect.hs +++ /dev/null @@ -1,227 +0,0 @@ - -module Language.PureScript.Docs.Collect - ( collectDocs - ) where - -import Protolude hiding (check) - -import Control.Arrow ((&&&)) -import Data.Aeson.BetterErrors qualified as ABE -import Data.ByteString qualified as BS -import Data.Map qualified as Map -import Data.Set qualified as Set -import Data.Text qualified as T -import Data.Text.IO qualified as TIO -import System.FilePath (()) -import System.IO.UTF8 (readUTF8FileT, readUTF8FilesT) - -import Language.PureScript.Docs.Convert.ReExports (updateReExports) -import Language.PureScript.Docs.Prim (primModules) -import Language.PureScript.Docs.Types (InPackage(..), Module(..), asModule, displayPackageError, ignorePackage) - -import Language.PureScript.AST qualified as P -import Language.PureScript.CST qualified as P -import Language.PureScript.Crash qualified as P -import Language.PureScript.Errors qualified as P -import Language.PureScript.Externs qualified as P -import Language.PureScript.Make qualified as P -import Language.PureScript.Make.IdeCache (sqliteInit) -import Language.PureScript.Names qualified as P -import Language.PureScript.Options qualified as P - -import Web.Bower.PackageMeta (PackageName) - --- | --- Given a compiler output directory, a list of input PureScript source files, --- and a list of dependency PureScript source files, produce documentation for --- the input files in the intermediate documentation format. Note that --- dependency files are not included in the result. --- --- If the output directory is not up to date with respect to the provided input --- and dependency files, the files will be built as if with just the "docs" --- codegen target, i.e. "purs compile --codegen docs". --- -collectDocs :: - forall m. - (MonadError P.MultipleErrors m, MonadIO m) => - FilePath -> - [FilePath] -> - [(PackageName, FilePath)] -> - m ([(FilePath, Module)], Map P.ModuleName PackageName) -collectDocs outputDir inputFiles depsFiles = do - (modulePaths, modulesDeps) <- getModulePackageInfo inputFiles depsFiles - externs <- compileForDocs outputDir (map fst modulePaths) - - let (withPackage, shouldKeep) = - packageDiscriminators modulesDeps - let go = - operateAndRetag identity modName $ \mns -> do - docsModules <- traverse (liftIO . parseDocsJsonFile outputDir) mns - addReExports withPackage docsModules externs - - docsModules <- go modulePaths - pure (filter (shouldKeep . modName . snd) docsModules, modulesDeps) - - where - packageDiscriminators modulesDeps = - let - shouldKeep mn = isLocal mn && not (P.isBuiltinModuleName mn) - - withPackage :: P.ModuleName -> InPackage P.ModuleName - withPackage mn = - case Map.lookup mn modulesDeps of - Just pkgName -> FromDep pkgName mn - Nothing -> Local mn - - isLocal :: P.ModuleName -> Bool - isLocal = not . flip Map.member modulesDeps - in - (withPackage, shouldKeep) - --- | --- Compile with just the 'docs' codegen target, writing results into the given --- output directory. --- -compileForDocs :: - forall m. - (MonadError P.MultipleErrors m, MonadIO m) => - FilePath -> - [FilePath] -> - m [P.ExternsFile] -compileForDocs outputDir inputFiles = do - result <- liftIO $ do - sqliteInit outputDir - moduleFiles <- readUTF8FilesT inputFiles - fmap fst $ P.runMake testOptions $ do - ms <- P.parseModulesFromFiles identity moduleFiles - let filePathMap = Map.fromList $ map (\(fp, pm) -> (P.getModuleName $ P.resPartial pm, Right fp)) ms - foreigns <- P.inferForeignModules filePathMap - let makeActions = - (P.buildMakeActions outputDir filePathMap foreigns False) - { P.progress = liftIO . TIO.hPutStr stdout . (<> "\n") . P.renderProgressMessage "documentation for " - } - P.make makeActions (map snd ms) - either throwError return result - - where - testOptions :: P.Options - testOptions = P.defaultOptions { P.optionsCodegenTargets = Set.singleton P.Docs } - -parseDocsJsonFile :: FilePath -> P.ModuleName -> IO Module -parseDocsJsonFile outputDir mn = - let - filePath = outputDir T.unpack (P.runModuleName mn) "docs.json" - in do - str <- BS.readFile filePath - case ABE.parseStrict asModule str of - Right m -> pure m - Left err -> P.internalError $ - "Failed to decode: " ++ filePath ++ - intercalate "\n" (map T.unpack (ABE.displayError displayPackageError err)) - -addReExports :: - (MonadError P.MultipleErrors m) => - (P.ModuleName -> InPackage P.ModuleName) -> - [Module] -> - [P.ExternsFile] -> - m [Module] -addReExports withPackage docsModules externs = do - -- We add the Prim docs modules here, so that docs generation is still - -- possible if the modules we are generating docs for re-export things from - -- Prim submodules. Note that the Prim modules do not exist as - -- @Language.PureScript.Module@ values because they do not contain anything - -- that exists at runtime. However, we have pre-constructed - -- @Language.PureScript.Docs.Types.Module@ values for them, which we use - -- here. - let moduleMap = - Map.fromList - (map (modName &&& identity) - (docsModules ++ primModules)) - - let withReExports = updateReExports externs withPackage moduleMap - pure (Map.elems withReExports) - --- | --- Perform an operation on a list of things which are tagged, and reassociate --- the things with their tags afterwards. --- -operateAndRetag :: - forall m a b key tag. - Monad m => - Ord key => - Show key => - (a -> key) -> - (b -> key) -> - ([a] -> m [b]) -> - [(tag, a)] -> - m [(tag, b)] -operateAndRetag keyA keyB operation input = - map retag <$> operation (map snd input) - where - tags :: Map key tag - tags = Map.fromList $ map (\(tag, a) -> (keyA a, tag)) input - - findTag :: key -> tag - findTag key = - case Map.lookup key tags of - Just tag -> tag - Nothing -> P.internalError ("Missing tag for: " ++ show key) - - retag :: b -> (tag, b) - retag b = (findTag (keyB b), b) - --- | --- Given: --- --- * A list of local source files --- * A list of source files from external dependencies, together with their --- package names --- --- This function does the following: --- --- * Partially parse all of the input and dependency source files to get --- the module name of each module --- * Associate each dependency module with its package name, thereby --- distinguishing these from local modules --- * Return the file paths paired with the names of the modules they --- contain, and a Map of module names to package names for modules which --- come from dependencies. If a module does not exist in the map, it can --- safely be --- assumed to be local. -getModulePackageInfo :: - (MonadError P.MultipleErrors m, MonadIO m) => - [FilePath] - -> [(PackageName, FilePath)] - -> m ([(FilePath, P.ModuleName)], Map P.ModuleName PackageName) -getModulePackageInfo inputFiles depsFiles = do - inputFiles' <- traverse (readFileAs . Local) inputFiles - depsFiles' <- traverse (readFileAs . uncurry FromDep) depsFiles - - moduleNames <- getModuleNames (inputFiles' ++ depsFiles') - - let mnMap = - Map.fromList $ - mapMaybe (\(pkgPath, mn) -> (mn,) <$> getPkgName pkgPath) moduleNames - - pure (map (first ignorePackage) moduleNames, mnMap) - - where - getModuleNames :: - (MonadError P.MultipleErrors m) => - [(InPackage FilePath, Text)] - -> m [(InPackage FilePath, P.ModuleName)] - getModuleNames = - fmap (map (second (P.getModuleName . P.resPartial))) - . either throwError return - . P.parseModulesFromFiles ignorePackage - - getPkgName = \case - Local _ -> Nothing - FromDep pkgName _ -> Just pkgName - - readFileAs :: - (MonadIO m) => - InPackage FilePath -> - m (InPackage FilePath, Text) - readFileAs fi = - liftIO . fmap (fi,) $ readUTF8FileT (ignorePackage fi) diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Convert.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Convert.hs deleted file mode 100644 index a7dc1758..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Convert.hs +++ /dev/null @@ -1,273 +0,0 @@ --- | Functions for converting PureScript ASTs into values of the data types --- from Language.PureScript.Docs. - -module Language.PureScript.Docs.Convert - ( convertModule - ) where - -import Protolude hiding (check) - -import Control.Category ((>>>)) -import Control.Monad.Writer.Strict (runWriterT) -import Control.Monad.Supply (evalSupplyT) -import Data.List.NonEmpty qualified as NE -import Data.Map qualified as Map -import Data.String (String) -import Data.Text qualified as T - -import Language.PureScript.Docs.Convert.Single (convertSingleModule) -import Language.PureScript.Docs.Types (Declaration(..), DeclarationInfo(..), KindInfo(..), Module(..), Type') -import Language.PureScript.CST qualified as CST -import Language.PureScript.AST qualified as P -import Language.PureScript.Crash qualified as P -import Language.PureScript.Errors qualified as P -import Language.PureScript.Externs qualified as P -import Language.PureScript.Environment qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Roles qualified as P -import Language.PureScript.Sugar qualified as P -import Language.PureScript.Types qualified as P -import Language.PureScript.Constants.Prim qualified as Prim -import Language.PureScript.Sugar (RebracketCaller(CalledByDocs)) - --- | --- Convert a single module to a Docs.Module, making use of a pre-existing --- type-checking environment in order to fill in any missing types. Note that --- re-exports will not be included. --- -convertModule :: - MonadError P.MultipleErrors m => - [P.ExternsFile] -> - P.Env -> - P.Environment -> - P.Module -> - m Module -convertModule externs env checkEnv = - fmap (insertValueTypesAndAdjustKinds checkEnv . convertSingleModule) . partiallyDesugar externs env - --- | --- Convert FFI declarations into `DataDeclaration` so that the declaration's --- roles (if any) can annotate the generated type parameter names. --- --- Inserts all data declarations inferred roles if none were specified --- explicitly. --- --- Updates all the types of the ValueDeclarations inside the module based on --- their types inside the given Environment. --- --- Removes explicit kind signatures if they are "uninteresting." --- --- Inserts inferred kind signatures into the corresponding declarations --- if no kind signature was declared explicitly and the kind --- signature is "interesting." --- -insertValueTypesAndAdjustKinds :: - P.Environment -> Module -> Module -insertValueTypesAndAdjustKinds env m = - m { modDeclarations = map (go . insertInferredRoles . convertFFIDecl) (modDeclarations m) } - where - -- Convert FFI declarations into data declaration - -- by generating the type parameters' names based on its kind signature. - -- Note: `Prim` modules' docs don't go through this conversion process - -- so `ExternDataDeclaration` values will still exist beyond this point. - convertFFIDecl d@Declaration { declInfo = ExternDataDeclaration kind roles } = - d { declInfo = DataDeclaration P.Data (genTypeParams kind) roles - , declKind = Just (KindInfo P.DataSig kind) - } - - convertFFIDecl other = other - - insertInferredRoles d@Declaration { declInfo = DataDeclaration dataDeclType args [] } = - d { declInfo = DataDeclaration dataDeclType args inferredRoles } - - where - inferredRoles :: [P.Role] - inferredRoles = do - let key = P.Qualified (P.ByModuleName (modName m)) (P.ProperName (declTitle d)) - case Map.lookup key (P.types env) of - Just (_, tyKind) -> case tyKind of - P.DataType _ tySourceTyRole _ -> - map (\(_,_,r) -> r) tySourceTyRole - P.ExternData rs -> - rs - _ -> - [] - Nothing -> - err $ "type not found: " <> show key - - insertInferredRoles other = - other - - -- Given an FFI declaration like this - -- ``` - -- foreign import data Foo - -- :: forall a b c d - -- . MyKind a b - -- -> OtherKind c d - -- -> Symbol - -- -> (Type -> Type) - -- -> (Type) -- unneeded parens a developer typo - -- -> Type - -- ``` - -- Return a list of values, one for each implicit type parameter - -- of `(tX, Nothing)` where `X` refers to the index of he parameter - -- in that list, matching the values expected by `Render.toTypeVar` - genTypeParams :: Type' -> [(Text, Maybe Type')] - genTypeParams kind = do - let n = countParams 0 kind - map (\(i :: Int) -> ("t" <> T.pack (show i), Nothing)) $ take n [0..] - where - countParams :: Int -> Type' -> Int - countParams acc = \case - P.ForAll _ _ _ _ rest _ -> - countParams acc rest - - P.TypeApp _ f a | isFunctionApplication f -> - countParams (acc + 1) a - - P.ParensInType _ ty -> - countParams acc ty - - _ -> - acc - - isFunctionApplication = \case - P.TypeApp _ (P.TypeConstructor () Prim.Function) _ -> True - P.ParensInType _ ty -> isFunctionApplication ty - _ -> False - - -- insert value types - go d@Declaration { declInfo = ValueDeclaration P.TypeWildcard{} } = - let - ident = P.Ident . CST.getIdent . CST.nameValue . parseIdent $ declTitle d - ty = lookupName ident - in - d { declInfo = ValueDeclaration (ty $> ()) } - - go d@Declaration{..} | Just keyword <- extractKeyword declInfo = - case declKind of - Just ks -> - -- hide explicit kind signatures that are "uninteresting" - if isUninteresting keyword $ kiKind ks - then d { declKind = Nothing } - else d - Nothing -> - -- insert inferred kinds so long as they are "interesting" - insertInferredKind d declTitle keyword - - go other = - other - - parseIdent = - either (err . ("failed to parse Ident: " ++)) identity . runParser CST.parseIdent - - lookupName name = - let key = P.Qualified (P.ByModuleName (modName m)) name - in case Map.lookup key (P.names env) of - Just (ty, _, _) -> - ty - Nothing -> - err ("name not found: " ++ show key) - - -- Extracts the keyword for a declaration (if there is one) - extractKeyword :: DeclarationInfo -> Maybe P.KindSignatureFor - extractKeyword = \case - DataDeclaration dataDeclType _ _ -> Just $ case dataDeclType of - P.Data -> P.DataSig - P.Newtype -> P.NewtypeSig - TypeSynonymDeclaration _ _ -> Just P.TypeSynonymSig - TypeClassDeclaration _ _ _ -> Just P.ClassSig - _ -> Nothing - - -- Returns True if the kind signature is "uninteresting", which - -- is a kind that follows this form: - -- - `Type` - -- - `Constraint` (class declaration only) - -- - `Type -> K` where `K` is an "uninteresting" kind - isUninteresting - :: P.KindSignatureFor -> Type' -> Bool - isUninteresting keyword = \case - -- `Type -> ...` - P.TypeApp _ f a | isTypeAppFunctionType f -> isUninteresting keyword a - P.ParensInType _ ty -> isUninteresting keyword ty - x -> isKindPrimType x || (isClassKeyword && isKindPrimConstraint x) - where - isClassKeyword = case keyword of - P.ClassSig -> True - _ -> False - - isTypeAppFunctionType = \case - P.TypeApp _ f a -> isKindFunction f && isKindPrimType a - P.ParensInType _ ty -> isTypeAppFunctionType ty - _ -> False - - isKindFunction = isTypeConstructor Prim.Function - isKindPrimType = isTypeConstructor Prim.Type - isKindPrimConstraint = isTypeConstructor Prim.Constraint - - isTypeConstructor k = \case - P.TypeConstructor _ k' -> k' == k - P.ParensInType _ ty -> isTypeConstructor k ty - _ -> False - - insertInferredKind :: Declaration -> Text -> P.KindSignatureFor -> Declaration - insertInferredKind d name keyword = - let - key = P.Qualified (P.ByModuleName (modName m)) (P.ProperName name) - in case Map.lookup key (P.types env) of - Just (inferredKind, _) -> - if isUninteresting keyword inferredKind' - then d - else d { declKind = Just $ KindInfo - { kiKeyword = keyword - , kiKind = dropTypeSortAnnotation inferredKind' - } - } - where - inferredKind' = inferredKind $> () - - -- Note: the below change to the final kind used is intentionally - -- NOT being done for explicit kind signatures: - -- - -- changes `forall (k :: Type). k -> ...` - -- to `forall k . k -> ...` - dropTypeSortAnnotation = \case - P.ForAll sa vis txt (Just (P.TypeConstructor _ Prim.Type)) rest skol -> - P.ForAll sa vis txt Nothing (dropTypeSortAnnotation rest) skol - rest -> rest - - Nothing -> - err ("type not found: " ++ show key) - - err msg = - P.internalError ("Docs.Convert.insertValueTypes: " ++ msg) - -runParser :: CST.Parser a -> Text -> Either String a -runParser p = - bimap (CST.prettyPrintError . NE.head) snd - . CST.runTokenParser p - . CST.lex - --- | --- Partially desugar modules so that they are suitable for extracting --- documentation information from. --- -partiallyDesugar :: - (MonadError P.MultipleErrors m) => - [P.ExternsFile] -> - P.Env -> - P.Module -> - m P.Module -partiallyDesugar externs env = evalSupplyT 0 . desugar' - where - desugar' = - P.desugarDoModule - >=> P.desugarAdoModule - >=> P.desugarLetPatternModule - >>> P.desugarCasesModule - >=> P.desugarTypeDeclarationsModule - >=> fmap fst . runWriterT . flip evalStateT (env, mempty) . P.desugarImports - >=> P.rebracketFiltered CalledByDocs isInstanceDecl externs - - isInstanceDecl P.TypeInstanceDeclaration {} = True - isInstanceDecl _ = False diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Convert/ReExports.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Convert/ReExports.hs deleted file mode 100644 index 600b343a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Convert/ReExports.hs +++ /dev/null @@ -1,518 +0,0 @@ -module Language.PureScript.Docs.Convert.ReExports - ( updateReExports - ) where - -import Prelude - -import Control.Arrow ((&&&), first, second) -import Control.Monad (foldM, (<=<)) -import Control.Monad.Reader.Class (MonadReader, ask) -import Control.Monad.State.Class (MonadState, gets, modify) -import Control.Monad.Trans.Reader (runReaderT) -import Control.Monad.Trans.State.Strict (execState) - -import Data.Either (partitionEithers) -import Data.Foldable (fold, traverse_) -import Data.Map (Map) -import Data.Maybe (mapMaybe) -import Data.Map qualified as Map -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Docs.Types - -import Language.PureScript.AST qualified as P -import Language.PureScript.Crash qualified as P -import Language.PureScript.Errors qualified as P -import Language.PureScript.Externs qualified as P -import Language.PureScript.ModuleDependencies qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Types qualified as P - - --- | --- Given: --- --- * A list of externs files --- * A function for tagging a module with the package it comes from --- * A map of modules, indexed by their names, which are assumed to not --- have their re-exports listed yet --- --- This function adds all the missing re-exports. --- -updateReExports :: - [P.ExternsFile] -> - (P.ModuleName -> InPackage P.ModuleName) -> - Map P.ModuleName Module -> - Map P.ModuleName Module -updateReExports externs withPackage = execState action - where - action = - traverse_ go traversalOrder - - go mn = do - mdl <- lookup' mn - reExports <- getReExports externsEnv mn - let mdl' = mdl { modReExports = map (first withPackage) reExports } - modify (Map.insert mn mdl') - - lookup' mn = do - v <- gets (Map.lookup mn) - case v of - Just v' -> - pure v' - Nothing -> - internalError ("Module missing: " ++ T.unpack (P.runModuleName mn)) - - externsEnv :: Map P.ModuleName P.ExternsFile - externsEnv = Map.fromList $ map (P.efModuleName &&& id) externs - - traversalOrder :: [P.ModuleName] - traversalOrder = - case P.sortModules P.Transitive externsSignature externs of - Right (es, _) -> map P.efModuleName es - Left errs -> internalError $ - "failed to sortModules: " ++ - P.prettyPrintMultipleErrors P.defaultPPEOptions errs - - externsSignature :: P.ExternsFile -> P.ModuleSignature - externsSignature ef = - P.ModuleSignature - { P.sigSourceSpan = P.efSourceSpan ef - , P.sigModuleName = P.efModuleName ef - , P.sigImports = map (\ei -> (P.eiModule ei, P.nullSourceSpan)) (P.efImports ef) - } - --- | --- Collect all of the re-exported declarations for a single module. --- --- We require that modules have already been sorted (P.sortModules) in order to --- ensure that by the time we convert a particular module, all its dependencies --- have already been converted. --- -getReExports :: - (MonadState (Map P.ModuleName Module) m) => - Map P.ModuleName P.ExternsFile -> - P.ModuleName -> - m [(P.ModuleName, [Declaration])] -getReExports externsEnv mn = - case Map.lookup mn externsEnv of - Nothing -> - internalError ("Module missing: " ++ T.unpack (P.runModuleName mn)) - Just P.ExternsFile { P.efExports = refs } -> do - let reExpRefs = mapMaybe toReExportRef refs - runReaderT (collectDeclarations reExpRefs) mn - -toReExportRef :: P.DeclarationRef -> Maybe (P.ExportSource, P.DeclarationRef) -toReExportRef (P.ReExportRef _ source ref) = Just (source, ref) -toReExportRef _ = Nothing - --- | --- Assemble a list of declarations re-exported from a particular module, based --- on the Imports and Exports value for that module, and by extracting the --- declarations from the current state. --- --- This function works by searching through the lists of exported declarations --- in the Exports, and looking them up in the associated Imports value to find --- the module they were imported from. --- --- Additionally: --- --- * Attempts to move re-exported type class members under their parent --- type classes, if possible, or otherwise, "promote" them from --- ChildDeclarations to proper Declarations. --- * Filters data declarations to ensure that only re-exported data --- constructors are listed. --- * Filters type class declarations to ensure that only re-exported type --- class members are listed. --- -collectDeclarations :: forall m. - (MonadState (Map P.ModuleName Module) m, MonadReader P.ModuleName m) => - [(P.ExportSource, P.DeclarationRef)] -> - m [(P.ModuleName, [Declaration])] -collectDeclarations reExports = do - valsAndMembers <- collect lookupValueDeclaration expVals - valOps <- collect lookupValueOpDeclaration expValOps - typeClasses <- collect lookupTypeClassDeclaration expTCs - types <- collect lookupTypeDeclaration expTypes - typeOps <- collect lookupTypeOpDeclaration expTypeOps - - (vals, classes) <- handleTypeClassMembers valsAndMembers typeClasses - - let filteredTypes = filterDataConstructors expCtors types - let filteredClasses = filterTypeClassMembers (Map.keys expVals) classes - - pure (Map.toList (Map.unionsWith (<>) [filteredTypes, filteredClasses, vals, valOps, typeOps])) - - where - - collect - :: (P.ModuleName -> a -> m (P.ModuleName, [b])) - -> Map a P.ExportSource - -> m (Map P.ModuleName [b]) - collect lookup' exps = do - let reExps = Map.toList $ Map.mapMaybe P.exportSourceImportedFrom exps - decls <- traverse (uncurry (flip lookup')) reExps - return $ Map.fromListWith (<>) decls - - expVals :: Map P.Ident P.ExportSource - expVals = mkExportMap P.getValueRef - - expValOps :: Map (P.OpName 'P.ValueOpName) P.ExportSource - expValOps = mkExportMap P.getValueOpRef - - expTCs :: Map (P.ProperName 'P.ClassName) P.ExportSource - expTCs = mkExportMap P.getTypeClassRef - - expTypes :: Map (P.ProperName 'P.TypeName) P.ExportSource - expTypes = mkExportMap (fmap fst . P.getTypeRef) - - expTypeOps :: Map (P.OpName 'P.TypeOpName) P.ExportSource - expTypeOps = mkExportMap P.getTypeOpRef - - mkExportMap :: Ord name => (P.DeclarationRef -> Maybe name) -> Map name P.ExportSource - mkExportMap f = - Map.fromList $ - mapMaybe (\(exportSrc, ref) -> (,exportSrc) <$> f ref) reExports - - expCtors :: [P.ProperName 'P.ConstructorName] - expCtors = concatMap (fold . (snd <=< P.getTypeRef . snd)) reExports - -lookupValueDeclaration :: - forall m. - (MonadState (Map P.ModuleName Module) m, - MonadReader P.ModuleName m) => - P.ModuleName -> - P.Ident -> - m (P.ModuleName, [Either (Text, Constraint', ChildDeclaration) Declaration]) -lookupValueDeclaration importedFrom ident = do - decls <- lookupModuleDeclarations "lookupValueDeclaration" importedFrom - let - rs = - filter (\d -> declTitle d == P.showIdent ident - && (isValue d || isValueAlias d)) decls - errOther :: Show a => a -> m b - errOther other = - internalErrorInModule - ("lookupValueDeclaration: unexpected result:\n" ++ - "other: " ++ show other ++ "\n" ++ - "ident: " ++ show ident ++ "\n" ++ - "decls: " ++ show decls) - - case rs of - [r] -> - pure (importedFrom, [Right r]) - [] -> - -- It's a type class member. - -- Note that we need to filter based on the child declaration info using - -- `isTypeClassMember` anyway, because child declarations of type classes - -- are not necessarily members; they could also be instances. - let - allTypeClassChildDecls = - decls - |> mapMaybe (\d -> (d,) <$> typeClassConstraintFor d) - |> concatMap (\(d, constr) -> - map (declTitle d, constr,) - (declChildren d)) - - matchesIdent cdecl = - cdeclTitle cdecl == P.showIdent ident - - matchesAndIsTypeClassMember = - uncurry (&&) . (matchesIdent &&& isTypeClassMember) - - in - case filter (matchesAndIsTypeClassMember . thd) allTypeClassChildDecls of - [r'] -> - pure (importedFrom, [Left r']) - other -> - errOther other - other -> errOther other - - where - thd :: (a, b, c) -> c - thd (_, _, x) = x - -lookupValueOpDeclaration - :: (MonadState (Map P.ModuleName Module) m, MonadReader P.ModuleName m) - => P.ModuleName - -> P.OpName 'P.ValueOpName - -> m (P.ModuleName, [Declaration]) -lookupValueOpDeclaration importedFrom op = do - decls <- lookupModuleDeclarations "lookupValueOpDeclaration" importedFrom - case filter (\d -> declTitle d == P.showOp op && isValueAlias d) decls of - [d] -> - pure (importedFrom, [d]) - other -> - internalErrorInModule - ("lookupValueOpDeclaration: unexpected result for: " ++ show other) - --- | --- Extract a particular type declaration. For data declarations, constructors --- are only included in the output if they are listed in the arguments. --- -lookupTypeDeclaration :: - (MonadState (Map P.ModuleName Module) m, - MonadReader P.ModuleName m) => - P.ModuleName -> - P.ProperName 'P.TypeName -> - m (P.ModuleName, [Declaration]) -lookupTypeDeclaration importedFrom ty = do - decls <- lookupModuleDeclarations "lookupTypeDeclaration" importedFrom - let - ds = filter (\d -> declTitle d == P.runProperName ty && isType d) decls - case ds of - [d] -> - pure (importedFrom, [d]) - [] | P.isBuiltinModuleName importedFrom -> - -- Type classes in builtin modules (i.e. submodules of Prim) also have - -- corresponding pseudo-types in the primEnv, but since these are an - -- implementation detail they do not exist in the Modules, and hence in - -- this case, `ds` will be empty. - pure (importedFrom, []) - other -> - internalErrorInModule - ("lookupTypeDeclaration: unexpected result for " ++ show ty ++ ": " ++ show other) - -lookupTypeOpDeclaration - :: (MonadState (Map P.ModuleName Module) m,MonadReader P.ModuleName m) - => P.ModuleName - -> P.OpName 'P.TypeOpName - -> m (P.ModuleName, [Declaration]) -lookupTypeOpDeclaration importedFrom tyOp = do - decls <- lookupModuleDeclarations "lookupTypeOpDeclaration" importedFrom - let - ds = filter (\d -> declTitle d == ("type " <> P.showOp tyOp) && isTypeAlias d) decls - case ds of - [d] -> - pure (importedFrom, [d]) - other -> - internalErrorInModule - ("lookupTypeOpDeclaration: unexpected result: " ++ show other) - -lookupTypeClassDeclaration - :: (MonadState (Map P.ModuleName Module) m, MonadReader P.ModuleName m) - => P.ModuleName - -> P.ProperName 'P.ClassName - -> m (P.ModuleName, [Declaration]) -lookupTypeClassDeclaration importedFrom tyClass = do - decls <- lookupModuleDeclarations "lookupTypeClassDeclaration" importedFrom - let - ds = filter (\d -> declTitle d == P.runProperName tyClass - && isTypeClass d) - decls - case ds of - [d] -> - pure (importedFrom, [d]) - other -> - internalErrorInModule - ("lookupTypeClassDeclaration: unexpected result for " - ++ show tyClass ++ ": " - ++ (unlines . map show) other) - --- | --- Get the full list of declarations for a particular module out of the --- state, or raise an internal error if it is not there. --- -lookupModuleDeclarations :: - (MonadState (Map P.ModuleName Module) m, - MonadReader P.ModuleName m) => - String -> - P.ModuleName -> - m [Declaration] -lookupModuleDeclarations definedIn moduleName = do - mmdl <- gets (Map.lookup moduleName) - case mmdl of - Nothing -> - internalErrorInModule - (definedIn ++ ": module missing: " - ++ T.unpack (P.runModuleName moduleName)) - Just mdl -> - pure (allDeclarations mdl) - -handleTypeClassMembers :: - (MonadReader P.ModuleName m) => - Map P.ModuleName [Either (Text, Constraint', ChildDeclaration) Declaration] -> - Map P.ModuleName [Declaration] -> - m (Map P.ModuleName [Declaration], Map P.ModuleName [Declaration]) -handleTypeClassMembers valsAndMembers typeClasses = - let - moduleEnvs = - Map.unionWith (<>) - (fmap valsAndMembersToEnv valsAndMembers) - (fmap typeClassesToEnv typeClasses) - in - moduleEnvs - |> traverse handleEnv - |> fmap splitMap - -valsAndMembersToEnv :: - [Either (Text, Constraint', ChildDeclaration) Declaration] -> TypeClassEnv -valsAndMembersToEnv xs = - let (envUnhandledMembers, envValues) = partitionEithers xs - envTypeClasses = [] - in TypeClassEnv{..} - -typeClassesToEnv :: [Declaration] -> TypeClassEnv -typeClassesToEnv classes = - TypeClassEnv - { envUnhandledMembers = [] - , envValues = [] - , envTypeClasses = classes - } - --- | --- An intermediate data type, used for either moving type class members under --- their parent type classes, or promoting them to normal Declaration values --- if their parent type class has not been re-exported. --- -data TypeClassEnv = TypeClassEnv - { -- | - -- Type class members which have not yet been dealt with. The Text is the - -- name of the type class they belong to, and the constraint is used to - -- make sure that they have the correct type if they get promoted. - -- - envUnhandledMembers :: [(Text, Constraint', ChildDeclaration)] - -- | - -- A list of normal value declarations. Type class members will be added to - -- this list if their parent type class is not available. - -- - , envValues :: [Declaration] - -- | - -- A list of type class declarations. Type class members will be added to - -- their parents in this list, if they exist. - -- - , envTypeClasses :: [Declaration] - } - deriving (Show) - -instance Semigroup TypeClassEnv where - (TypeClassEnv a1 b1 c1) <> (TypeClassEnv a2 b2 c2) = - TypeClassEnv (a1 <> a2) (b1 <> b2) (c1 <> c2) - -instance Monoid TypeClassEnv where - mempty = - TypeClassEnv mempty mempty mempty - --- | --- Take a TypeClassEnv and handle all of the type class members in it, either --- adding them to their parent classes, or promoting them to normal Declaration --- values. --- --- Returns a tuple of (values, type classes). --- -handleEnv - :: (MonadReader P.ModuleName m) - => TypeClassEnv - -> m ([Declaration], [Declaration]) -handleEnv TypeClassEnv{..} = - envUnhandledMembers - |> foldM go (envValues, mkMap envTypeClasses) - |> fmap (second Map.elems) - - where - mkMap = - Map.fromList . map (declTitle &&& id) - - go (values, tcs) (title, constraint, childDecl) = - case Map.lookup title tcs of - Just _ -> - -- Leave the state unchanged; if the type class is there, the child - -- will be too. - pure (values, tcs) - Nothing -> do - c <- promoteChild constraint childDecl - pure (c : values, tcs) - - promoteChild constraint ChildDeclaration{..} = - case cdeclInfo of - ChildTypeClassMember typ -> - pure Declaration - { declTitle = cdeclTitle - , declComments = cdeclComments - , declSourceSpan = cdeclSourceSpan - , declChildren = [] - , declInfo = ValueDeclaration (addConstraint constraint typ) - , declKind = Nothing - } - _ -> - internalErrorInModule - ("handleEnv: Bad child declaration passed to promoteChild: " - ++ T.unpack cdeclTitle) - - addConstraint constraint = - P.quantify . P.moveQuantifiersToFront () . P.ConstrainedType () constraint - -splitMap :: Map k (v1, v2) -> (Map k v1, Map k v2) -splitMap = fmap fst &&& fmap snd - --- | --- Given a list of exported constructor names, remove any data constructor --- names in the provided Map of declarations which are not in the list. --- -filterDataConstructors - :: [P.ProperName 'P.ConstructorName] - -> Map P.ModuleName [Declaration] - -> Map P.ModuleName [Declaration] -filterDataConstructors = - filterExportedChildren isDataConstructor P.runProperName - --- | --- Given a list of exported type class member names, remove any data --- type class member names in the provided Map of declarations which are not in --- the list. --- -filterTypeClassMembers - :: [P.Ident] - -> Map P.ModuleName [Declaration] - -> Map P.ModuleName [Declaration] -filterTypeClassMembers = - filterExportedChildren isTypeClassMember P.showIdent - -filterExportedChildren - :: (Functor f) - => (ChildDeclaration -> Bool) - -> (name -> Text) - -> [name] - -> f [Declaration] - -> f [Declaration] -filterExportedChildren isTargetedKind runName expNames = fmap filterDecls - where - filterDecls = - map $ filterChildren $ \c -> - not (isTargetedKind c) || cdeclTitle c `elem` expNames' - expNames' = map runName expNames - -allDeclarations :: Module -> [Declaration] -allDeclarations Module{..} = - modDeclarations ++ concatMap snd modReExports - -(|>) :: a -> (a -> b) -> b -x |> f = f x - -internalError :: String -> a -internalError = P.internalError . ("Docs.Convert.ReExports: " ++) - -internalErrorInModule - :: (MonadReader P.ModuleName m) - => String - -> m a -internalErrorInModule msg = do - mn <- ask - internalError - ("while collecting re-exports for module: " ++ T.unpack (P.runModuleName mn) ++ - ", " ++ msg) - --- | --- If the provided Declaration is a TypeClassDeclaration, construct an --- appropriate Constraint for use with the types of its members. --- -typeClassConstraintFor :: Declaration -> Maybe Constraint' -typeClassConstraintFor Declaration{..} = - case declInfo of - TypeClassDeclaration tyArgs _ _ -> - Just (P.Constraint () (P.Qualified P.ByNullSourcePos (P.ProperName declTitle)) [] (mkConstraint tyArgs) Nothing) - _ -> - Nothing - where - mkConstraint = map (P.TypeVar () . fst) diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Convert/Single.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Convert/Single.hs deleted file mode 100644 index b3b15e7b..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Convert/Single.hs +++ /dev/null @@ -1,235 +0,0 @@ -module Language.PureScript.Docs.Convert.Single - ( convertSingleModule - , convertComments - ) where - -import Protolude hiding (moduleName) - -import Control.Category ((>>>)) - -import Data.Text qualified as T - -import Language.PureScript.Docs.Types (ChildDeclaration(..), ChildDeclarationInfo(..), Declaration(..), DeclarationInfo(..), KindInfo(..), Module(..), Type', convertFundepsToStrings, isType, isTypeClass) - -import Language.PureScript.AST qualified as P -import Language.PureScript.Comments qualified as P -import Language.PureScript.Crash qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Roles qualified as P -import Language.PureScript.Types qualified as P - --- | --- Convert a single Module, but ignore re-exports; any re-exported types or --- values will not appear in the result. --- -convertSingleModule :: P.Module -> Module -convertSingleModule m@(P.Module _ coms moduleName _ _) = - Module moduleName comments (declarations m) [] - where - comments = convertComments coms - declarations = - P.exportedDeclarations - >>> mapMaybe (\d -> getDeclarationTitle d >>= convertDeclaration d) - >>> augmentDeclarations - --- | Different declarations we can augment -data AugmentType - = AugmentClass - -- ^ Augment documentation for a type class - | AugmentType - -- ^ Augment documentation for a type constructor - --- | The data type for an intermediate stage which we go through during --- converting. --- --- In the first pass, we take all top level declarations in the module, and --- collect other information which will later be used to augment the top level --- declarations. These two situation correspond to the Right and Left --- constructors, respectively. --- --- In the second pass, we go over all of the Left values and augment the --- relevant declarations, leaving only the augmented Right values. --- --- Note that in the Left case, we provide a [Text] as well as augment --- information. The [Text] value should be a list of titles of declarations --- that the augmentation should apply to. For example, for a type instance --- declaration, that would be any types or type classes mentioned in the --- instance. For a fixity declaration, it would be just the relevant operator's --- name. -type IntermediateDeclaration - = Either ([(Text, AugmentType)], DeclarationAugment) Declaration - --- | Some data which will be used to augment a Declaration in the --- output. --- --- The AugmentChild constructor allows us to move all children under their --- respective parents. It is only necessary for type instance declarations, --- since they appear at the top level in the AST, and since they might need to --- appear as children in two places (for example, if a data type defined in a --- module is an instance of a type class also defined in that module). --- --- The AugmentKindSig constructor allows us to add a kind signature --- to its corresponding declaration. Comments for both declarations --- are also merged together. -data DeclarationAugment - = AugmentChild ChildDeclaration - | AugmentKindSig KindSignatureInfo - | AugmentRole (Maybe Text) [P.Role] - -data KindSignatureInfo = KindSignatureInfo - { ksiComments :: Maybe Text - , ksiKeyword :: P.KindSignatureFor - , ksiKind :: Type' - } - --- | Augment top-level declarations; the second pass. See the comments under --- the type synonym IntermediateDeclaration for more information. -augmentDeclarations :: [IntermediateDeclaration] -> [Declaration] -augmentDeclarations (partitionEithers -> (augments, toplevels)) = - foldl' go toplevels augments - where - go ds (parentTitles, a) = - map (\d -> - if any (matches d) parentTitles - then augmentWith a d - else d) ds - - matches d (name, AugmentType) = isType d && declTitle d == name - matches d (name, AugmentClass) = isTypeClass d && declTitle d == name - - augmentWith (AugmentChild child) d = - d { declChildren = declChildren d ++ [child] } - augmentWith (AugmentKindSig KindSignatureInfo{..}) d = - d { declComments = mergeComments ksiComments $ declComments d - , declKind = Just $ KindInfo { kiKeyword = ksiKeyword, kiKind = ksiKind } - } - augmentWith (AugmentRole comms roles) d = - d { declComments = mergeComments (declComments d) comms - , declInfo = insertRoles - } - where - insertRoles = case declInfo d of - DataDeclaration dataDeclType args [] -> - DataDeclaration dataDeclType args roles - DataDeclaration _ _ _ -> - P.internalError "augmentWith: could not add a second role declaration to a data declaration" - - ExternDataDeclaration kind [] -> - ExternDataDeclaration kind roles - ExternDataDeclaration _ _ -> - P.internalError "augmentWith: could not add a second role declaration to an FFI declaration" - - _ -> P.internalError "augmentWith: could not add role to declaration" - - mergeComments :: Maybe Text -> Maybe Text -> Maybe Text - mergeComments Nothing bot = bot - mergeComments top Nothing = top - mergeComments (Just topComs) (Just bottomComs) = - Just $ topComs <> "\n" <> bottomComs - -getDeclarationTitle :: P.Declaration -> Maybe Text -getDeclarationTitle (P.ValueDeclaration vd) = Just (P.showIdent (P.valdeclIdent vd)) -getDeclarationTitle (P.ExternDeclaration _ name _) = Just (P.showIdent name) -getDeclarationTitle (P.DataDeclaration _ _ name _ _) = Just (P.runProperName name) -getDeclarationTitle (P.ExternDataDeclaration _ name _) = Just (P.runProperName name) -getDeclarationTitle (P.TypeSynonymDeclaration _ name _ _) = Just (P.runProperName name) -getDeclarationTitle (P.TypeClassDeclaration _ name _ _ _ _) = Just (P.runProperName name) -getDeclarationTitle (P.TypeInstanceDeclaration _ _ _ _ name _ _ _ _) = Just $ either (const "") P.showIdent name -getDeclarationTitle (P.TypeFixityDeclaration _ _ _ op) = Just ("type " <> P.showOp op) -getDeclarationTitle (P.ValueFixityDeclaration _ _ _ op) = Just (P.showOp op) -getDeclarationTitle (P.KindDeclaration _ _ n _) = Just (P.runProperName n) -getDeclarationTitle (P.RoleDeclaration P.RoleDeclarationData{..}) = Just (P.runProperName rdeclIdent) -getDeclarationTitle _ = Nothing - --- | Create a basic Declaration value. -mkDeclaration :: P.SourceAnn -> Text -> DeclarationInfo -> Declaration -mkDeclaration (ss, com) title info = - Declaration { declTitle = title - , declComments = convertComments com - , declSourceSpan = Just ss -- TODO: make this non-optional when we next break the format - , declChildren = [] - , declInfo = info - , declKind = Nothing -- kind sigs are added in augment pass - } - -basicDeclaration :: P.SourceAnn -> Text -> DeclarationInfo -> Maybe IntermediateDeclaration -basicDeclaration sa title = Just . Right . mkDeclaration sa title - -convertDeclaration :: P.Declaration -> Text -> Maybe IntermediateDeclaration -convertDeclaration (P.ValueDecl sa _ _ _ [P.MkUnguarded (P.TypedValue _ _ ty)]) title = - basicDeclaration sa title (ValueDeclaration (ty $> ())) -convertDeclaration (P.ValueDecl sa _ _ _ _) title = - -- If no explicit type declaration was provided, insert a wildcard, so that - -- the actual type will be added during type checking. - basicDeclaration sa title (ValueDeclaration (P.TypeWildcard () P.UnnamedWildcard)) -convertDeclaration (P.ExternDeclaration sa _ ty) title = - basicDeclaration sa title (ValueDeclaration (ty $> ())) -convertDeclaration (P.DataDeclaration sa dtype _ args ctors) title = - Just (Right (mkDeclaration sa title info) { declChildren = children }) - where - info = DataDeclaration dtype (fmap (fmap (fmap ($> ()))) args) [] - children = map convertCtor ctors - convertCtor :: P.DataConstructorDeclaration -> ChildDeclaration - convertCtor P.DataConstructorDeclaration{..} = - let (sourceSpan, comments) = dataCtorAnn - in ChildDeclaration (P.runProperName dataCtorName) (convertComments comments) (Just sourceSpan) (ChildDataConstructor (fmap (($> ()) . snd) dataCtorFields)) -convertDeclaration (P.ExternDataDeclaration sa _ kind') title = - basicDeclaration sa title (ExternDataDeclaration (kind' $> ()) []) -convertDeclaration (P.TypeSynonymDeclaration sa _ args ty) title = - basicDeclaration sa title (TypeSynonymDeclaration (fmap (fmap (fmap ($> ()))) args) (ty $> ())) -convertDeclaration (P.TypeClassDeclaration sa _ args implies fundeps ds) title = - Just (Right (mkDeclaration sa title info) { declChildren = children }) - where - args' = fmap (fmap (fmap ($> ()))) args - info = TypeClassDeclaration args' (fmap ($> ()) implies) (convertFundepsToStrings args' fundeps) - children = map convertClassMember ds - convertClassMember (P.TypeDeclaration (P.TypeDeclarationData (ss, com) ident' ty)) = - ChildDeclaration (P.showIdent ident') (convertComments com) (Just ss) (ChildTypeClassMember (ty $> ())) - convertClassMember _ = - P.internalError "convertDeclaration: Invalid argument to convertClassMember." -convertDeclaration (P.TypeInstanceDeclaration (ss, com) _ _ _ _ constraints className tys _) title = - Just (Left ((classNameString, AugmentClass) : map (, AugmentType) typeNameStrings, AugmentChild childDecl)) - where - classNameString = unQual className - typeNameStrings = ordNub (concatMap (P.everythingOnTypes (++) extractProperNames) tys) - unQual x = let (P.Qualified _ y) = x in P.runProperName y - - extractProperNames (P.TypeConstructor _ n) = [unQual n] - extractProperNames _ = [] - - childDecl = ChildDeclaration title (convertComments com) (Just ss) (ChildInstance (fmap ($> ()) constraints) (classApp $> ())) - classApp = foldl' P.srcTypeApp (P.srcTypeConstructor (fmap P.coerceProperName className)) tys -convertDeclaration (P.ValueFixityDeclaration sa fixity (P.Qualified mn alias) _) title = - Just . Right $ mkDeclaration sa title (AliasDeclaration fixity (P.Qualified mn (Right alias))) -convertDeclaration (P.TypeFixityDeclaration sa fixity (P.Qualified mn alias) _) title = - Just . Right $ mkDeclaration sa title (AliasDeclaration fixity (P.Qualified mn (Left alias))) -convertDeclaration (P.KindDeclaration sa keyword _ kind) title = - Just $ Left ([(title, AugmentType), (title, AugmentClass)], AugmentKindSig ksi) - where - comms = convertComments $ snd sa - ksi = KindSignatureInfo { ksiComments = comms, ksiKeyword = keyword, ksiKind = kind $> () } -convertDeclaration (P.RoleDeclaration P.RoleDeclarationData{..}) title = - Just $ Left ([(title, AugmentType)], AugmentRole comms rdeclRoles) - where - comms = convertComments $ snd rdeclSourceAnn - -convertDeclaration _ _ = Nothing - -convertComments :: [P.Comment] -> Maybe Text -convertComments cs = do - let raw = concatMap toLines cs - let docs = mapMaybe stripPipe raw - guard (not (null docs)) - pure (T.unlines docs) - - where - toLines (P.LineComment s) = [s] - toLines (P.BlockComment s) = T.lines s - - stripPipe = - T.dropWhile (== ' ') - >>> T.stripPrefix "|" - >>> fmap (dropPrefix " ") - - dropPrefix prefix str = - fromMaybe str (T.stripPrefix prefix str) diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Css.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Css.hs deleted file mode 100644 index 9567db96..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Css.hs +++ /dev/null @@ -1,31 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} -module Language.PureScript.Docs.Css where - -import Data.ByteString (ByteString) -import Data.Text (Text) -import Data.Text.Encoding (decodeUtf8) -import Data.FileEmbed (embedFile) - --- | --- An embedded copy of normalize.css as a UTF-8 encoded ByteString; this should --- be included before pursuit.css in any HTML page using pursuit.css. --- -normalizeCss :: ByteString -normalizeCss = $(embedFile "app/static/normalize.css") - --- | --- Like 'normalizeCss', but as a 'Text'. -normalizeCssT :: Text -normalizeCssT = decodeUtf8 normalizeCss - --- | --- CSS for use with generated HTML docs, as a UTF-8 encoded ByteString. --- -pursuitCss :: ByteString -pursuitCss = $(embedFile "app/static/pursuit.css") - --- | --- Like 'pursuitCss', but as a 'Text'. --- -pursuitCssT :: Text -pursuitCssT = decodeUtf8 pursuitCss diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Prim.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Prim.hs deleted file mode 100644 index 801a64bc..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Prim.hs +++ /dev/null @@ -1,666 +0,0 @@ --- | This module provides documentation for the builtin Prim modules. -module Language.PureScript.Docs.Prim - ( primDocsModule - , primRowDocsModule - , primTypeErrorDocsModule - , primModules - ) where - -import Prelude hiding (fail) -import Data.Functor (($>)) -import Data.Text (Text) -import Data.Text qualified as T -import Data.Map qualified as Map -import Language.PureScript.Docs.Types (Declaration(..), DeclarationInfo(..), Module(..), Type', convertFundepsToStrings) - -import Language.PureScript.Constants.Prim qualified as P -import Language.PureScript.Crash qualified as P -import Language.PureScript.Environment qualified as P -import Language.PureScript.Names qualified as P - -primModules :: [Module] -primModules = - [ primDocsModule - , primBooleanDocsModule - , primCoerceDocsModule - , primOrderingDocsModule - , primRowDocsModule - , primRowListDocsModule - , primSymbolDocsModule - , primIntDocsModule - , primTypeErrorDocsModule - ] - -primDocsModule :: Module -primDocsModule = Module - { modName = P.moduleNameFromString "Prim" - , modComments = Just $ T.unlines - [ "The `Prim` module is embedded in the PureScript compiler in order to provide compiler support for certain types — for example, value literals, or syntax sugar. It is implicitly imported unqualified in every module except those that list it as a qualified import." - , "" - , "`Prim` does not include additional built-in types and kinds that are defined deeper in the compiler such as Type wildcards (e.g. `f :: _ -> Int`) and Quantified Types. Rather, these are documented in [the PureScript language reference](https://github.com/purescript/documentation/blob/master/language/Types.md)." - ] - , modDeclarations = - [ function - , array - , record - , number - , int - , string - , char - , boolean - , partial - , kindType - , kindConstraint - , kindSymbol - , kindRow - ] - , modReExports = [] - } - -primBooleanDocsModule :: Module -primBooleanDocsModule = Module - { modName = P.moduleNameFromString "Prim.Boolean" - , modComments = Just "The Prim.Boolean module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains a type level `Boolean` data structure." - , modDeclarations = - [ booleanTrue - , booleanFalse - ] - , modReExports = [] - } - -primCoerceDocsModule :: Module -primCoerceDocsModule = Module - { modName = P.moduleNameFromString "Prim.Coerce" - , modComments = Just "The Prim.Coerce module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains an automatically solved type class for coercing types that have provably-identical runtime representations with [purescript-safe-coerce](https://pursuit.purescript.org/packages/purescript-safe-coerce)." - , modDeclarations = - [ coercible - ] - , modReExports = [] - } - -primOrderingDocsModule :: Module -primOrderingDocsModule = Module - { modName = P.moduleNameFromString "Prim.Ordering" - , modComments = Just "The Prim.Ordering module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains a type level `Ordering` data structure." - , modDeclarations = - [ kindOrdering - , orderingLT - , orderingEQ - , orderingGT - ] - , modReExports = [] - } - -primRowDocsModule :: Module -primRowDocsModule = Module - { modName = P.moduleNameFromString "Prim.Row" - , modComments = Just "The Prim.Row module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains automatically solved type classes for working with row types." - , modDeclarations = - [ union - , nub - , lacks - , rowCons - ] - , modReExports = [] - } - -primRowListDocsModule :: Module -primRowListDocsModule = Module - { modName = P.moduleNameFromString "Prim.RowList" - , modComments = Just "The Prim.RowList module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains a type level list (`RowList`) that represents an ordered view of a row of types." - , modDeclarations = - [ kindRowList - , rowListCons - , rowListNil - , rowToList - ] - , modReExports = [] - } - -primSymbolDocsModule :: Module -primSymbolDocsModule = Module - { modName = P.moduleNameFromString "Prim.Symbol" - , modComments = Just "The Prim.Symbol module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains automatically solved type classes for working with `Symbols`." - , modDeclarations = - [ symbolAppend - , symbolCompare - , symbolCons - ] - , modReExports = [] - } - -primIntDocsModule :: Module -primIntDocsModule = Module - { modName = P.moduleNameFromString "Prim.Int" - , modComments = Just "The Prim.Int module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains automatically solved type classes for working with type-level intural numbers." - , modDeclarations = - [ intAdd - , intCompare - , intMul - , intToString - ] - , modReExports = [] - } - -primTypeErrorDocsModule :: Module -primTypeErrorDocsModule = Module - { modName = P.moduleNameFromString "Prim.TypeError" - , modComments = Just "The Prim.TypeError module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains type classes that provide custom type error and warning functionality." - , modDeclarations = - [ warn - , fail - , kindDoc - , textDoc - , quoteDoc - , quoteLabelDoc - , besideDoc - , aboveDoc - ] - , modReExports = [] - } - -unsafeLookup - :: forall v (a :: P.ProperNameType) - . Map.Map (P.Qualified (P.ProperName a)) v - -> String - -> P.Qualified (P.ProperName a) - -> v -unsafeLookup m errorMsg name = go name - where - go = fromJust' . flip Map.lookup m - - fromJust' (Just x) = x - fromJust' _ = P.internalError $ errorMsg ++ show (P.runProperName $ P.disqualify name) - -lookupPrimTypeKind - :: P.Qualified (P.ProperName 'P.TypeName) - -> Type' -lookupPrimTypeKind = ($> ()) . fst . unsafeLookup - ( P.primTypes <> - P.primBooleanTypes <> - P.primOrderingTypes <> - P.primRowTypes <> - P.primRowListTypes <> - P.primTypeErrorTypes - ) "Docs.Prim: No such Prim type: " - -primType :: P.Qualified (P.ProperName 'P.TypeName) -> Text -> Declaration -primType tn comments = Declaration - { declTitle = P.runProperName $ P.disqualify tn - , declComments = Just comments - , declSourceSpan = Nothing - , declChildren = [] - , declInfo = ExternDataDeclaration (lookupPrimTypeKind tn) [] - , declKind = Nothing - } - --- | Lookup the TypeClassData of a Prim class. This function is specifically --- not exported because it is partial. -lookupPrimClass :: P.Qualified (P.ProperName 'P.ClassName) -> P.TypeClassData -lookupPrimClass = unsafeLookup - ( P.primClasses <> - P.primCoerceClasses <> - P.primRowClasses <> - P.primRowListClasses <> - P.primSymbolClasses <> - P.primIntClasses <> - P.primTypeErrorClasses - ) "Docs.Prim: No such Prim class: " - -primClass :: P.Qualified (P.ProperName 'P.ClassName) -> Text -> Declaration -primClass cn comments = Declaration - { declTitle = P.runProperName $ P.disqualify cn - , declComments = Just comments - , declSourceSpan = Nothing - , declChildren = [] - , declInfo = - let - tcd = lookupPrimClass cn - args = fmap (fmap ($> ())) <$> P.typeClassArguments tcd - superclasses = ($> ()) <$> P.typeClassSuperclasses tcd - fundeps = convertFundepsToStrings args (P.typeClassDependencies tcd) - in - TypeClassDeclaration args superclasses fundeps - , declKind = Nothing - } - -kindType :: Declaration -kindType = primType P.Type $ T.unlines - [ "`Type` is the kind of all proper types: those that classify value-level terms." - , "For example the type `Boolean` has kind `Type`; denoted by `Boolean :: Type`." - ] - -kindConstraint :: Declaration -kindConstraint = primType P.Constraint $ T.unlines - [ "`Constraint` is the kind of type class constraints." - , "For example, a type class declaration like this:" - , "" - , " class Semigroup a where" - , " append :: a -> a -> a" - , "" - , "has the kind signature:" - , "" - , " class Semigroup :: Type -> Constraint" - ] - -kindSymbol :: Declaration -kindSymbol = primType P.Symbol $ T.unlines - [ "`Symbol` is the kind of type-level strings." - , "" - , "Construct types of this kind using the same literal syntax as documented" - , "for strings." - , "" - , " type Hello :: Symbol" - , " type Hello = \"Hello, world\"" - , "" - ] - -kindRow :: Declaration -kindRow = primType P.Row $ T.unlines - [ "`Row` is the kind constructor of label-indexed types which map type-level strings to other types." - , "The most common use of `Row` is `Row Type`, a row mapping labels to basic (of kind `Type`) types:" - , "" - , " type ExampleRow :: Row Type" - , " type ExampleRow = ( name :: String, values :: Array Int )" - , "" - , "This is the kind of `Row` expected by the `Record` type constructor." - , "More advanced row kinds like `Row (Type -> Type)` are used much less frequently." - ] - -function :: Declaration -function = primType P.Function $ T.unlines - [ "A function, which takes values of the type specified by the first type" - , "parameter, and returns values of the type specified by the second." - , "In the JavaScript backend, this is a standard JavaScript Function." - , "" - , "The type constructor `(->)` is syntactic sugar for this type constructor." - , "It is recommended to use `(->)` rather than `Function`, where possible." - , "" - , "That is, prefer this:" - , "" - , " f :: Number -> Number" - , "" - , "to either of these:" - , "" - , " f :: Function Number Number" - , " f :: (->) Number Number" - ] - -array :: Declaration -array = primType P.Array $ T.unlines - [ "An Array: a data structure supporting efficient random access. In" - , "the JavaScript backend, values of this type are represented as JavaScript" - , "Arrays at runtime." - , "" - , "Construct values using literals:" - , "" - , " x = [1,2,3,4,5] :: Array Int" - ] - -record :: Declaration -record = primType P.Record $ T.unlines - [ "The type of records whose fields are known at compile time. In the" - , "JavaScript backend, values of this type are represented as JavaScript" - , "Objects at runtime." - , "" - , "The type signature here means that the `Record` type constructor takes" - , "a row of concrete types. For example:" - , "" - , " type Person = Record (name :: String, age :: Number)" - , "" - , "The syntactic sugar with curly braces `{ }` is generally preferred, though:" - , "" - , " type Person = { name :: String, age :: Number }" - , "" - , "The row associates a type to each label which appears in the record." - , "" - , "_Technical note_: PureScript allows duplicate labels in rows, and the" - , "meaning of `Record r` is based on the _first_ occurrence of each label in" - , "the row `r`." - ] - -number :: Declaration -number = primType P.Number $ T.unlines - [ "A double precision floating point number (IEEE 754)." - , "" - , "Construct values of this type with literals." - , "Negative literals must be wrapped in parentheses if the negation sign could be mistaken" - , "for an infix operator:" - , "" - , " x = 35.23 :: Number" - , " y = -1.224e6 :: Number" - , " z = exp (-1.0) :: Number" - ] - -int :: Declaration -int = primType P.Int $ T.unlines - [ "A 32-bit signed integer. See the `purescript-integers` package for details" - , "of how this is accomplished when compiling to JavaScript." - , "" - , "Construct values of this type with literals. Hexadecimal syntax is supported." - , "Negative literals must be wrapped in parentheses if the negation sign could be mistaken" - , "for an infix operator:" - , "" - , " x = -23 :: Int" - , " y = 0x17 :: Int" - , " z = complement (-24) :: Int" - , "" - , "Integers used as types are considered to have kind `Int`." - , "Unlike value-level `Int`s, which must be representable as a 32-bit signed integer," - , "type-level `Int`s are unbounded. Hexadecimal support is also supported at the type level." - , "" - , " type One :: Int" - , " type One = 1" - , " " - , " type Beyond32BitSignedInt :: Int" - , " type Beyond32BitSignedInt = 2147483648" - , " " - , " type HexInt :: Int" - , " type HexInt = 0x17" - , "" - , "Negative integer literals at the type level must be" - , "wrapped in parentheses if the negation sign could be mistaken for an infix operator." - , "" - , " type NegativeOne = -1" - , " foo :: Proxy (-1) -> ..." - ] - -string :: Declaration -string = primType P.String $ T.unlines - [ "A String. As in JavaScript, String values represent sequences of UTF-16" - , "code units, which are not required to form a valid encoding of Unicode" - , "text (for example, lone surrogates are permitted)." - , "" - , "Construct values of this type with literals, using double quotes `\"`:" - , "" - , " x = \"hello, world\" :: String" - , "" - , "Multi-line string literals are also supported with triple quotes (`\"\"\"`):" - , "" - , " x = \"\"\"multi" - , " line\"\"\"" - , "" - , "At the type level, string literals represent types with kind `Symbol`." - , "These types will have kind `String` in a future release:" - , "" - , " type Hello :: Symbol" - , " type Hello = \"Hello, world\"" - ] - -char :: Declaration -char = primType P.Char $ T.unlines - [ "A single character (UTF-16 code unit). The JavaScript representation is a" - , "normal `String`, which is guaranteed to contain one code unit. This means" - , "that astral plane characters (i.e. those with code point values greater" - , "than `0xFFFF`) cannot be represented as `Char` values." - , "" - , "Construct values of this type with literals, using single quotes `'`:" - , "" - , " x = 'a' :: Char" - ] - -boolean :: Declaration -boolean = primType P.Boolean $ T.unlines - [ "A JavaScript Boolean value." - , "" - , "Construct values of this type with the literals `true` and `false`." - , "" - , "The `True` and `False` types defined in `Prim.Boolean` have this type as their kind." - ] - -partial :: Declaration -partial = primClass P.Partial $ T.unlines - [ "The Partial type class is used to indicate that a function is *partial,*" - , "that is, it is not defined for all inputs. In practice, attempting to use" - , "a partial function with a bad input will usually cause an error to be" - , "thrown, although it is not safe to assume that this will happen in all" - , "cases. For more information, see" - , "[purescript-partial](https://pursuit.purescript.org/packages/purescript-partial/)." - ] - -booleanTrue :: Declaration -booleanTrue = primType P.True $ T.unlines - [ "The 'True' boolean type." - ] - -booleanFalse :: Declaration -booleanFalse = primType P.False $ T.unlines - [ "The 'False' boolean type." - ] - -coercible :: Declaration -coercible = primClass P.Coercible $ T.unlines - [ "Coercible is a two-parameter type class that has instances for types `a`" - , "and `b` if the compiler can infer that they have the same representation." - , "Coercible constraints are solved according to the following rules:" - , "" - , "* _reflexivity_, any type has the same representation as itself:" - , "`Coercible a a` holds." - , "" - , "* _symmetry_, if a type `a` can be coerced to some other type `b`, then `b`" - , "can also be coerced back to `a`: `Coercible a b` implies `Coercible b a`." - , "" - , "* _transitivity_, if a type `a` can be coerced to some other type `b` which" - , "can be coerced to some other type `c`, then `a` can also be coerced to `c`:" - , "`Coercible a b` and `Coercible b c` imply `Coercible a c`." - , "" - , "* Newtypes can be freely wrapped and unwrapped when their constructor is" - , "in scope:" - , "" - , " newtype Age = Age Int" - , "" - , "`Coercible Int Age` and `Coercible Age Int` hold since `Age` has the same" - , "runtime representation than `Int`." - , "" - , "Newtype constructors have to be in scope to preserve abstraction. It's" - , "common to declare a newtype to encode some invariants (non emptiness of" - , "arrays with `Data.Array.NonEmpty.NonEmptyArray` for example), hide its" - , "constructor and export smart constructors instead. Without this restriction," - , "the guarantees provided by such newtypes would be void." - , "" - , "* If none of the above are applicable, two types of kind `Type` may be" - , "coercible, but only if their heads are the same. For example," - , "`Coercible (Maybe a) (Either a b)` does not hold because `Maybe` and" - , "`Either` are different. Those types don't share a common runtime" - , "representation so coercing between them would be unsafe. In addition their" - , "arguments may need to be identical or coercible, depending on the _roles_" - , "of the head's type parameters. Roles are documented in [the PureScript" - , "language reference](https://github.com/purescript/documentation/blob/master/language/Roles.md)." - , "" - , "Coercible being polykinded, we can also coerce more than types of kind `Type`:" - , "" - , "* Rows are coercible when they have the same labels, when the corresponding" - , "pairs of types are coercible and when their tails are coercible:" - , "`Coercible ( label :: a | r ) ( label :: b | s )` holds when" - , "`Coercible a b` and `Coercible r s` do. Closed rows cannot be coerced to" - , "open rows." - , "" - , "* Higher kinded types are coercible if they are coercible when fully" - , "saturated: `Coercible (f :: _ -> Type) (g :: _ -> Type)` holds when" - , "`Coercible (f a) (g a)` does." - , "" - , "This rule may seem puzzling since there is no term of type `_ -> Type` to" - , "apply `coerce` to, but it is necessary when coercing types with higher" - , "kinded parameters." - ] - -kindOrdering :: Declaration -kindOrdering = primType P.TypeOrdering $ T.unlines - [ "The `Ordering` kind represents the three possibilities of comparing two" - , "types of the same kind: `LT` (less than), `EQ` (equal to), and" - , "`GT` (greater than)." - ] - -orderingLT :: Declaration -orderingLT = primType P.LT $ T.unlines - [ "The 'less than' ordering type." - ] - -orderingEQ :: Declaration -orderingEQ = primType P.EQ $ T.unlines - [ "The 'equal to' ordering type." - ] - -orderingGT :: Declaration -orderingGT = primType P.GT $ T.unlines - [ "The 'greater than' ordering type." - ] - -union :: Declaration -union = primClass P.RowUnion $ T.unlines - [ "The Union type class is used to compute the union of two rows of types" - , "(left-biased, including duplicates)." - , "" - , "The third type argument represents the union of the first two." - ] - -nub :: Declaration -nub = primClass P.RowNub $ T.unlines - [ "The Nub type class is used to remove duplicate labels from rows." - ] - -lacks :: Declaration -lacks = primClass P.RowLacks $ T.unlines - [ "The Lacks type class asserts that a label does not occur in a given row." - ] - -rowCons :: Declaration -rowCons = primClass P.RowCons $ T.unlines - [ "The Cons type class is a 4-way relation which asserts that one row of" - , "types can be obtained from another by inserting a new label/type pair on" - , "the left." - ] - -kindRowList :: Declaration -kindRowList = primType P.RowList $ T.unlines - [ "A type level list representation of a row of types." - ] - -rowListCons :: Declaration -rowListCons = primType P.RowListCons $ T.unlines - [ "Constructs a new `RowList` from a label, a type, and an existing tail" - , "`RowList`. E.g: `Cons \"x\" Int (Cons \"y\" Int Nil)`." - ] - -rowListNil :: Declaration -rowListNil = primType P.RowListNil $ T.unlines - [ "The empty `RowList`." - ] - -rowToList :: Declaration -rowToList = primClass P.RowToList $ T.unlines - [ "Compiler solved type class for generating a `RowList` from a closed row" - , "of types. Entries are sorted by label and duplicates are preserved in" - , "the order they appeared in the row." - ] - -symbolAppend :: Declaration -symbolAppend = primClass P.SymbolAppend $ T.unlines - [ "Compiler solved type class for appending `Symbol`s together." - ] - -symbolCompare :: Declaration -symbolCompare = primClass P.SymbolCompare $ T.unlines - [ "Compiler solved type class for comparing two `Symbol`s." - , "Produces an `Ordering`." - ] - -symbolCons :: Declaration -symbolCons = primClass P.SymbolCons $ T.unlines - [ "Compiler solved type class for either splitting up a symbol into its" - , "head and tail or for combining a head and tail into a new symbol." - , "Requires the head to be a single character and the combined string" - , "cannot be empty." - ] - -intAdd :: Declaration -intAdd = primClass P.IntAdd $ T.unlines - [ "Compiler solved type class for adding type-level `Int`s." - ] - -intCompare :: Declaration -intCompare = primClass P.IntCompare $ T.unlines - [ "Compiler solved type class for comparing two type-level `Int`s." - , "Produces an `Ordering`." - ] - -intMul :: Declaration -intMul = primClass P.IntMul $ T.unlines - [ "Compiler solved type class for multiplying type-level `Int`s." - ] - -intToString :: Declaration -intToString = primClass P.IntToString $ T.unlines - [ "Compiler solved type class for converting a type-level `Int` into a type-level `String` (i.e. `Symbol`)." - ] - -fail :: Declaration -fail = primClass P.Fail $ T.unlines - [ "The Fail type class is part of the custom type errors feature. To provide" - , "a custom type error when someone tries to use a particular instance," - , "write that instance out with a Fail constraint." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] - -warn :: Declaration -warn = primClass P.Warn $ T.unlines - [ "The Warn type class allows a custom compiler warning to be displayed." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] - -kindDoc :: Declaration -kindDoc = primType P.Doc $ T.unlines - [ "`Doc` is the kind of type-level documents." - , "" - , "This kind is used with the `Fail` and `Warn` type classes." - , "Build up a `Doc` with `Text`, `Quote`, `QuoteLabel`, `Beside`, and `Above`." - ] - -textDoc :: Declaration -textDoc = primType P.Text $ T.unlines - [ "The Text type constructor makes a Doc from a Symbol" - , "to be used in a custom type error." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] - -quoteDoc :: Declaration -quoteDoc = primType P.Quote $ T.unlines - [ "The Quote type constructor renders any concrete type as a Doc" - , "to be used in a custom type error." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] - -quoteLabelDoc :: Declaration -quoteLabelDoc = primType P.QuoteLabel $ T.unlines - [ "The `QuoteLabel` type constructor will produce a `Doc` when given a `Symbol`. When the resulting `Doc` is rendered" - , "for a `Warn` or `Fail` constraint, a syntactically valid label will be produced, escaping with quotes as needed." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] - -besideDoc :: Declaration -besideDoc = primType P.Beside $ T.unlines - [ "The Beside type constructor combines two Docs horizontally" - , "to be used in a custom type error." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] - -aboveDoc :: Declaration -aboveDoc = primType P.Above $ T.unlines - [ "The Above type constructor combines two Docs vertically" - , "in a custom type error." - , "" - , "For more information, see" - , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)." - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Render.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Render.hs deleted file mode 100644 index 3a0038d9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Render.hs +++ /dev/null @@ -1,142 +0,0 @@ --- | --- Functions for creating `RenderedCode` values from data types in --- Language.PureScript.Docs.Types. --- --- These functions are the ones that are used in markdown/html documentation --- generation, but the intention is that you are able to supply your own --- instead if necessary. For example, the Hoogle input file generator --- substitutes some of these - -module Language.PureScript.Docs.Render where - -import Prelude - -import Data.Maybe (maybeToList) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Docs.RenderedCode -import Language.PureScript.Docs.Types (ChildDeclaration(..), ChildDeclarationInfo(..), Constraint', Declaration(..), DeclarationInfo(..), KindInfo(..), Type', isTypeClassMember, kindSignatureForKeyword) -import Language.PureScript.Docs.Utils.MonoidExtras (mintersperse) - -import Language.PureScript.AST qualified as P -import Language.PureScript.Environment qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Types qualified as P - -renderKindSig :: Text -> KindInfo -> RenderedCode -renderKindSig declTitle KindInfo{..} = - mintersperse sp - [ keyword $ kindSignatureForKeyword kiKeyword - , renderType (P.TypeConstructor () (notQualified declTitle)) - , syntax "::" - , renderType kiKind - ] - -renderDeclaration :: Declaration -> RenderedCode -renderDeclaration Declaration{..} = - mintersperse sp $ case declInfo of - ValueDeclaration ty -> - [ ident' declTitle - , syntax "::" - , renderType ty - ] - DataDeclaration dtype args roles -> - [ keyword (P.showDataDeclType dtype) - , renderTypeWithRole roles (typeApp declTitle args) - ] - - -- All FFI declarations, except for `Prim` modules' doc declarations, - -- will have been converted to `DataDeclaration`s by this point. - ExternDataDeclaration kind' _ -> - [ keywordData - , renderType (P.TypeConstructor () (notQualified declTitle)) - , syntax "::" - , renderType kind' - ] - TypeSynonymDeclaration args ty -> - [ keywordType - , renderType (typeApp declTitle args) - , syntax "=" - , renderType ty - ] - TypeClassDeclaration args implies fundeps -> - [ keywordClass ] - ++ maybeToList superclasses - ++ [renderType (typeApp declTitle args)] - ++ fundepsList - ++ [keywordWhere | any isTypeClassMember declChildren] - - where - superclasses - | null implies = Nothing - | otherwise = Just $ - syntax "(" - <> mintersperse (syntax "," <> sp) (map renderConstraint implies) - <> syntax ")" <> sp <> syntax "<=" - - fundepsList = - [syntax "|" | not (null fundeps)] - ++ [mintersperse - (syntax "," <> sp) - [typeVars from <> sp <> syntax "->" <> sp <> typeVars to | (from, to) <- fundeps ] - ] - where - typeVars = mintersperse sp . map typeVar - - AliasDeclaration (P.Fixity associativity precedence) for -> - [ keywordFixity associativity - , syntax $ T.pack $ show precedence - , alias for - , keywordAs - , aliasName for declTitle - ] - -renderChildDeclaration :: ChildDeclaration -> RenderedCode -renderChildDeclaration ChildDeclaration{..} = - mintersperse sp $ case cdeclInfo of - ChildInstance constraints ty -> - maybeToList (renderConstraints constraints) ++ [ renderType ty ] - ChildDataConstructor args -> - dataCtor' cdeclTitle : map renderTypeAtom args - - ChildTypeClassMember ty -> - [ ident' cdeclTitle - , syntax "::" - , renderType ty - ] - -renderConstraint :: Constraint' -> RenderedCode -renderConstraint (P.Constraint ann pn kinds tys _) = - renderType $ foldl (P.TypeApp ann) (foldl (P.KindApp ann) (P.TypeConstructor ann (fmap P.coerceProperName pn)) kinds) tys - -renderConstraints :: [Constraint'] -> Maybe RenderedCode -renderConstraints constraints - | null constraints = Nothing - | otherwise = Just $ - syntax "(" - <> renderedConstraints - <> syntax ")" <> sp <> syntax "=>" - where - renderedConstraints = - mintersperse (syntax "," <> sp) - (map renderConstraint constraints) - -notQualified :: Text -> P.Qualified (P.ProperName a) -notQualified = P.Qualified P.ByNullSourcePos . P.ProperName - -ident' :: Text -> RenderedCode -ident' = ident . P.Qualified P.ByNullSourcePos . P.Ident - -dataCtor' :: Text -> RenderedCode -dataCtor' = dataCtor . notQualified - -typeApp :: Text -> [(Text, Maybe Type')] -> Type' -typeApp title typeArgs = - foldl (P.TypeApp ()) - (P.TypeConstructor () (notQualified title)) - (map toTypeVar typeArgs) - -toTypeVar :: (Text, Maybe Type') -> Type' -toTypeVar (s, Nothing) = P.TypeVar () s -toTypeVar (s, Just k) = P.KindedType () (P.TypeVar () s) k diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode.hs deleted file mode 100644 index 2d8d0253..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode.hs +++ /dev/null @@ -1,8 +0,0 @@ - --- | Data types and functions for representing a simplified form of PureScript --- code, intended for use in e.g. HTML documentation. - -module Language.PureScript.Docs.RenderedCode (module RenderedCode) where - -import Language.PureScript.Docs.RenderedCode.Types as RenderedCode -import Language.PureScript.Docs.RenderedCode.RenderType as RenderedCode diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode/RenderType.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode/RenderType.hs deleted file mode 100644 index c6a985b0..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode/RenderType.hs +++ /dev/null @@ -1,255 +0,0 @@ --- HLint is confused by the identifier `pattern` if PatternSynonyms is enabled. -{-# LANGUAGE NoPatternSynonyms #-} - --- | Functions for producing RenderedCode values from PureScript Type values. - -module Language.PureScript.Docs.RenderedCode.RenderType - ( renderType - , renderTypeWithRole - , renderType' - , renderTypeAtom - , renderTypeAtom' - , renderRow - ) where - -import Prelude - -import Data.Maybe (fromMaybe) -import Data.Text (Text, pack) -import Data.List (uncons) - -import Control.Arrow ((<+>)) -import Control.PatternArrows as PA - -import Language.PureScript.Crash (internalError) -import Language.PureScript.Label (Label) -import Language.PureScript.Names (coerceProperName) -import Language.PureScript.Pretty.Types (PrettyPrintConstraint, PrettyPrintType(..), convertPrettyPrintType, prettyPrintLabel) -import Language.PureScript.Roles (Role, displayRole) -import Language.PureScript.Types (Type, TypeVarVisibility, typeVarVisibilityPrefix) -import Language.PureScript.PSString (prettyPrintString) - -import Language.PureScript.Docs.RenderedCode.Types (RenderedCode, keywordForall, roleAnn, sp, syntax, typeCtor, typeOp, typeVar) -import Language.PureScript.Docs.Utils.MonoidExtras (mintersperse) - -typeLiterals :: Pattern () PrettyPrintType RenderedCode -typeLiterals = mkPattern match - where - match (PPTypeWildcard name) = - Just $ syntax $ maybe "_" ("?" <>) name - match (PPTypeVar var role) = - Just $ typeVar var <> roleAnn role - match (PPRecord labels tail_) = - Just $ mintersperse sp - [ syntax "{" - , renderRow labels tail_ - , syntax "}" - ] - match (PPTypeConstructor n) = - Just (typeCtor n) - match (PPRow labels tail_) = - Just (syntax "(" <> renderRow labels tail_ <> syntax ")") - match (PPBinaryNoParensType op l r) = - Just $ renderTypeAtom' l <> sp <> renderTypeAtom' op <> sp <> renderTypeAtom' r - match (PPTypeOp n) = - Just (typeOp n) - match (PPTypeLevelString str) = - Just (syntax (prettyPrintString str)) - match (PPTypeLevelInt nat) = - Just (syntax $ pack $ show nat) - match _ = - Nothing - -renderConstraint :: PrettyPrintConstraint -> RenderedCode -renderConstraint (pn, ks, tys) = - let instApp = foldl PPTypeApp (foldl (\a b -> PPTypeApp a (PPKindArg b)) (PPTypeConstructor (fmap coerceProperName pn)) ks) tys - in renderType' instApp - -renderConstraints :: PrettyPrintConstraint -> RenderedCode -> RenderedCode -renderConstraints con ty = - mintersperse sp - [ renderConstraint con - , syntax "=>" - , ty - ] - --- | --- Render code representing a Row --- -renderRow :: [(Label, PrettyPrintType)] -> Maybe PrettyPrintType -> RenderedCode -renderRow h t = renderHead h <> renderTail t - -renderHead :: [(Label, PrettyPrintType)] -> RenderedCode -renderHead = mintersperse (syntax "," <> sp) . map renderLabel - -renderLabel :: (Label, PrettyPrintType) -> RenderedCode -renderLabel (label, ty) = - mintersperse sp - [ typeVar $ prettyPrintLabel label - , syntax "::" - , renderType' ty - ] - -renderTail :: Maybe PrettyPrintType -> RenderedCode -renderTail Nothing = mempty -renderTail (Just other) = sp <> syntax "|" <> sp <> renderType' other - -typeApp :: Pattern () PrettyPrintType (PrettyPrintType, PrettyPrintType) -typeApp = mkPattern match - where - match (PPTypeApp f x) = Just (f, x) - match _ = Nothing - -kindArg :: Pattern () PrettyPrintType ((), PrettyPrintType) -kindArg = mkPattern match - where - match (PPKindArg ty) = Just ((), ty) - match _ = Nothing - -appliedFunction :: Pattern () PrettyPrintType (PrettyPrintType, PrettyPrintType) -appliedFunction = mkPattern match - where - match (PPFunction arg ret) = Just (arg, ret) - match _ = Nothing - -kinded :: Pattern () PrettyPrintType (PrettyPrintType, PrettyPrintType) -kinded = mkPattern match - where - match (PPKindedType t k) = Just (t, k) - match _ = Nothing - -constrained :: Pattern () PrettyPrintType (PrettyPrintConstraint, PrettyPrintType) -constrained = mkPattern match - where - match (PPConstrainedType con ty) = Just (con, ty) - match _ = Nothing - -explicitParens :: Pattern () PrettyPrintType ((), PrettyPrintType) -explicitParens = mkPattern match - where - match (PPParensInType ty) = Just ((), ty) - match _ = Nothing - -matchTypeAtom :: Pattern () PrettyPrintType RenderedCode -matchTypeAtom = typeLiterals <+> fmap parens_ matchType - where - parens_ x = syntax "(" <> x <> syntax ")" - -matchType :: Pattern () PrettyPrintType RenderedCode -matchType = buildPrettyPrinter operators matchTypeAtom - where - operators :: OperatorTable () PrettyPrintType RenderedCode - operators = - OperatorTable [ [ Wrap kindArg $ \_ ty -> syntax "@" <> ty ] - , [ AssocL typeApp $ \f x -> f <> sp <> x ] - , [ AssocR appliedFunction $ \arg ret -> mintersperse sp [arg, syntax "->", ret] ] - , [ Wrap constrained $ \deps ty -> renderConstraints deps ty ] - , [ Wrap forall_ $ \tyVars ty -> mconcat [ keywordForall, sp, renderTypeVars tyVars, syntax ".", sp, ty ] ] - , [ Wrap kinded $ \ty k -> mintersperse sp [renderType' ty, syntax "::", k] ] - , [ Wrap explicitParens $ \_ ty -> ty ] - ] - -forall_ :: Pattern () PrettyPrintType ([(TypeVarVisibility, Text, Maybe PrettyPrintType)], PrettyPrintType) -forall_ = mkPattern match - where - match (PPForAll mbKindedIdents ty) = Just (mbKindedIdents, ty) - match _ = Nothing - -renderTypeInternal :: (PrettyPrintType -> PrettyPrintType) -> Type a -> RenderedCode -renderTypeInternal insertRolesIfAny = - renderType' . insertRolesIfAny . convertPrettyPrintType maxBound - --- | --- Render code representing a Type --- -renderType :: Type a -> RenderedCode -renderType = renderTypeInternal id - --- | --- Render code representing a Type --- but augment the `TypeVar`s with their `Role` if they have one --- -renderTypeWithRole :: [Role] -> Type a -> RenderedCode -renderTypeWithRole = \case - [] -> renderType - roleList -> renderTypeInternal (addRole roleList [] . Left) - where - -- `data Foo first second = Foo` will produce - -- ``` - -- PPTypeApp - -- (PPTypeApp (PPTypeConstructor fooName) (PPTypeVar "first" Nothing)) - -- (PPTypeVar "second" Nothing) - -- ``` - -- So, we recurse down the left side of `TypeApp` first before - -- recursing down the right side. To make this stack-safe, - -- we use a tail-recursive function with its own stack. - -- - Left = values that have not yet been examined and need - -- a role added to them (if any). There's still work "left" to do. - -- - Right = values that have been examined and now need to be - -- reassembled into their original value - addRole - :: [Role] - -> [Either PrettyPrintType PrettyPrintType] - -> Either PrettyPrintType PrettyPrintType - -> PrettyPrintType - addRole roles stack pp = case pp of - Left next -> case next of - PPTypeVar t Nothing - | Just (x, xs) <- uncons roles -> - addRole xs stack (Right $ PPTypeVar t (Just $ displayRole x)) - | otherwise -> - internalError "addRole: invalid arguments - number of roles doesn't match number of type parameters" - - PPTypeVar _ (Just _) -> - internalError "addRole: attempted to add a second role to a type parameter that already has one" - - PPTypeApp leftSide rightSide -> do - -- push right-side to stack and continue recursing on left-side - addRole roles (Left rightSide : stack) (Left leftSide) - - other -> - -- nothing to check, so move on - addRole roles stack (Right other) - - - pendingAssembly@(Right rightSideOrFinalValue) -> case stack of - (unfinishedRightSide@(Left _) : remaining) -> - -- We've finished recursing through the left-side of a `TypeApp`. - -- Now we'll recurse through the right-side. - -- We push `pendingAssembly` onto the stack so we can assemble - -- the `PPTypeApp` together once it's right-side is done. - addRole roles (pendingAssembly : remaining) unfinishedRightSide - - (Right leftSide : remaining) -> - -- We've finished recursing through the right-side of a `TypeApp` - -- We'll rebulid it and wrap it in `Right` so any other higher-level - -- `TypeApp`s can be reassembled now, too. - addRole roles remaining (Right (PPTypeApp leftSide rightSideOrFinalValue)) - - [] -> - -- We've reassembled everything. It's time to return. - rightSideOrFinalValue - -renderType' :: PrettyPrintType -> RenderedCode -renderType' - = fromMaybe (internalError "Incomplete pattern") - . PA.pattern_ matchType () - -renderTypeVars :: [(TypeVarVisibility, Text, Maybe PrettyPrintType)] -> RenderedCode -renderTypeVars tyVars = mintersperse sp (map renderTypeVar tyVars) - -renderTypeVar :: (TypeVarVisibility, Text, Maybe PrettyPrintType) -> RenderedCode -renderTypeVar (vis, v, mbK) = case mbK of - Nothing -> syntax (typeVarVisibilityPrefix vis) <> typeVar v - Just k -> mintersperse sp [ mconcat [syntax "(", syntax $ typeVarVisibilityPrefix vis, typeVar v], syntax "::", mconcat [renderType' k, syntax ")"] ] - --- | --- Render code representing a Type, as it should appear inside parentheses --- -renderTypeAtom :: Type a -> RenderedCode -renderTypeAtom = renderTypeAtom' . convertPrettyPrintType maxBound - -renderTypeAtom' :: PrettyPrintType -> RenderedCode -renderTypeAtom' - = fromMaybe (internalError "Incomplete pattern") - . PA.pattern_ matchTypeAtom () diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode/Types.hs deleted file mode 100644 index c1374899..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/RenderedCode/Types.hs +++ /dev/null @@ -1,315 +0,0 @@ --- | Data types and functions for representing a simplified form of PureScript --- code, intended for use in e.g. HTML documentation. - -module Language.PureScript.Docs.RenderedCode.Types - ( RenderedCodeElement(..) - , ContainingModule(..) - , asContainingModule - , maybeToContainingModule - , fromQualified - , Namespace(..) - , Link(..) - , FixityAlias - , RenderedCode - , outputWith - , sp - , syntax - , keyword - , keywordForall - , keywordData - , keywordType - , keywordClass - , keywordWhere - , keywordFixity - , keywordAs - , ident - , dataCtor - , typeCtor - , typeOp - , typeVar - , roleAnn - , alias - , aliasName - ) where - -import Prelude -import GHC.Generics (Generic) - -import Control.DeepSeq (NFData) -import Control.Monad.Error.Class (MonadError(..)) - -import Data.Aeson.BetterErrors (Parse, nth, withText, withValue, toAesonParser, perhaps, asText) -import Data.Aeson qualified as A -import Data.Text (Text) -import Data.Text qualified as T -import Data.ByteString.Lazy qualified as BS -import Data.Text.Encoding qualified as TE - -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, OpName(..), OpNameType(..), ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), moduleNameFromString, runIdent, runModuleName) -import Language.PureScript.AST (Associativity(..)) - --- | Given a list of actions, attempt them all, returning the first success. --- If all the actions fail, 'tryAll' returns the first argument. -tryAll :: MonadError e m => m a -> [m a] -> m a -tryAll = foldr $ \x y -> catchError x (const y) - -firstEq :: Text -> Parse Text a -> Parse Text a -firstEq str p = nth 0 (withText (eq str)) *> p - where - eq s s' = if s == s' then Right () else Left "" - --- | --- Try the given parsers in sequence. If all fail, fail with the given message, --- and include the JSON in the error. --- -tryParse :: Text -> [Parse Text a] -> Parse Text a -tryParse msg = - tryAll (withValue (Left . (fullMsg <>) . showJSON)) - - where - fullMsg = "Invalid " <> msg <> ": " - - showJSON :: A.Value -> Text - showJSON = TE.decodeUtf8 . BS.toStrict . A.encode - --- | --- This type is isomorphic to 'Maybe' 'ModuleName'. It makes code a bit --- easier to read, as the meaning is more explicit. --- -data ContainingModule - = ThisModule - | OtherModule ModuleName - deriving (Show, Eq, Ord) - -instance A.ToJSON ContainingModule where - toJSON = A.toJSON . go - where - go = \case - ThisModule -> ["ThisModule"] - OtherModule mn -> ["OtherModule", runModuleName mn] - -instance A.FromJSON ContainingModule where - parseJSON = toAesonParser id asContainingModule - -asContainingModule :: Parse Text ContainingModule -asContainingModule = - tryParse "containing module" $ - current ++ backwardsCompat - where - current = - [ firstEq "ThisModule" (pure ThisModule) - , firstEq "OtherModule" (OtherModule <$> nth 1 asModuleName) - ] - - -- For JSON produced by compilers up to 0.10.5. - backwardsCompat = - [ maybeToContainingModule <$> perhaps asModuleName - ] - - asModuleName = moduleNameFromString <$> asText - --- | --- Convert a 'Maybe' 'ModuleName' to a 'ContainingModule', using the obvious --- isomorphism. --- -maybeToContainingModule :: Maybe ModuleName -> ContainingModule -maybeToContainingModule Nothing = ThisModule -maybeToContainingModule (Just mn) = OtherModule mn - -fromQualified :: Qualified a -> (ContainingModule, a) -fromQualified (Qualified (ByModuleName mn) x) = (OtherModule mn, x) -fromQualified (Qualified _ x) = (ThisModule, x) - -data Link - = NoLink - | Link ContainingModule - deriving (Show, Eq, Ord) - -instance A.ToJSON Link where - toJSON = \case - NoLink -> A.toJSON ["NoLink" :: Text] - Link mn -> A.toJSON ["Link", A.toJSON mn] - -asLink :: Parse Text Link -asLink = - tryParse "link" - [ firstEq "NoLink" (pure NoLink) - , firstEq "Link" (Link <$> nth 1 asContainingModule) - ] - -instance A.FromJSON Link where - parseJSON = toAesonParser id asLink - -data Namespace - = ValueLevel - | TypeLevel - deriving (Show, Eq, Ord, Generic) - -instance NFData Namespace - -instance A.ToJSON Namespace where - toJSON = A.toJSON . show - -asNamespace :: Parse Text Namespace -asNamespace = - tryParse "namespace" - [ withText $ \case - "ValueLevel" -> Right ValueLevel - "TypeLevel" -> Right TypeLevel - _ -> Left "" - ] - -instance A.FromJSON Namespace where - parseJSON = toAesonParser id asNamespace - --- | --- A single element in a rendered code fragment. The intention is to support --- multiple output formats. For example, plain text, or highlighted HTML. --- -data RenderedCodeElement - = Syntax Text - | Keyword Text - | Space - -- | Any symbol which you might or might not want to link to, in any - -- namespace (value, type, or kind). Note that this is not related to the - -- kind called Symbol for type-level strings. - | Symbol Namespace Text Link - | Role Text - deriving (Show, Eq, Ord) - -instance A.ToJSON RenderedCodeElement where - toJSON (Syntax str) = - A.toJSON ["syntax", str] - toJSON (Keyword str) = - A.toJSON ["keyword", str] - toJSON Space = - A.toJSON ["space" :: Text] - toJSON (Symbol ns str link) = - A.toJSON ["symbol", A.toJSON ns, A.toJSON str, A.toJSON link] - toJSON (Role role) = - A.toJSON ["role", role] - --- | --- A type representing a highly simplified version of PureScript code, intended --- for use in output formats like plain text or HTML. --- -newtype RenderedCode - = RC { unRC :: [RenderedCodeElement] } - deriving (Show, Eq, Ord, Semigroup, Monoid) - -instance A.ToJSON RenderedCode where - toJSON (RC elems) = A.toJSON elems - --- | --- This function allows conversion of a 'RenderedCode' value into a value of --- some other type (for example, plain text, or HTML). The first argument --- is a function specifying how each individual 'RenderedCodeElement' should be --- rendered. --- -outputWith :: Monoid a => (RenderedCodeElement -> a) -> RenderedCode -> a -outputWith f = foldMap f . unRC - --- | --- A 'RenderedCode' fragment representing a space. --- -sp :: RenderedCode -sp = RC [Space] - --- possible TODO: instead of this function, export RenderedCode values for --- each syntax element, eg syntaxArr (== syntax "->"), syntaxLBrace, --- syntaxRBrace, etc. -syntax :: Text -> RenderedCode -syntax x = RC [Syntax x] - -keyword :: Text -> RenderedCode -keyword kw = RC [Keyword kw] - -keywordForall :: RenderedCode -keywordForall = keyword "forall" - -keywordData :: RenderedCode -keywordData = keyword "data" - -keywordType :: RenderedCode -keywordType = keyword "type" - -keywordClass :: RenderedCode -keywordClass = keyword "class" - -keywordWhere :: RenderedCode -keywordWhere = keyword "where" - -keywordFixity :: Associativity -> RenderedCode -keywordFixity Infixl = keyword "infixl" -keywordFixity Infixr = keyword "infixr" -keywordFixity Infix = keyword "infix" - -keywordAs :: RenderedCode -keywordAs = keyword "as" - -ident :: Qualified Ident -> RenderedCode -ident (fromQualified -> (mn, name)) = - RC [Symbol ValueLevel (runIdent name) (Link mn)] - -dataCtor :: Qualified (ProperName 'ConstructorName) -> RenderedCode -dataCtor (fromQualified -> (mn, name)) = - RC [Symbol ValueLevel (runProperName name) (Link mn)] - -typeCtor :: Qualified (ProperName 'TypeName) -> RenderedCode -typeCtor (fromQualified -> (mn, name)) = - RC [Symbol TypeLevel (runProperName name) (Link mn)] - -typeOp :: Qualified (OpName 'TypeOpName) -> RenderedCode -typeOp (fromQualified -> (mn, name)) = - RC [Symbol TypeLevel (runOpName name) (Link mn)] - -typeVar :: Text -> RenderedCode -typeVar x = RC [Symbol TypeLevel x NoLink] - -roleAnn :: Maybe Text -> RenderedCode -roleAnn = RC . maybe [] renderRole - where - renderRole = \case - "nominal" -> [Role "nominal"] - "phantom" -> [Role "phantom"] - _ -> [] - -type FixityAlias = Qualified (Either (ProperName 'TypeName) (Either Ident (ProperName 'ConstructorName))) - -alias :: FixityAlias -> RenderedCode -alias for = - prefix <> RC [Symbol ns name (Link mn)] - where - (ns, name, mn) = unpackFixityAlias for - prefix = case ns of - TypeLevel -> - keywordType <> sp - _ -> - mempty - -aliasName :: FixityAlias -> Text -> RenderedCode -aliasName for name' = - let - (ns, _, _) = unpackFixityAlias for - unParen = T.tail . T.init - name = unParen name' - in - case ns of - ValueLevel -> - ident (Qualified ByNullSourcePos (Ident name)) - TypeLevel -> - typeCtor (Qualified ByNullSourcePos (ProperName name)) - --- | Converts a FixityAlias into a different representation which is more --- useful to other functions in this module. -unpackFixityAlias :: FixityAlias -> (Namespace, Text, ContainingModule) -unpackFixityAlias (fromQualified -> (mn, x)) = - case x of - -- We add some seemingly superfluous type signatures here just to be extra - -- sure we are not mixing up our namespaces. - Left (n :: ProperName 'TypeName) -> - (TypeLevel, runProperName n, mn) - Right (Left n) -> - (ValueLevel, runIdent n, mn) - Right (Right (n :: ProperName 'ConstructorName)) -> - (ValueLevel, runProperName n, mn) diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Tags.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Tags.hs deleted file mode 100644 index e3651c9f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Tags.hs +++ /dev/null @@ -1,53 +0,0 @@ -module Language.PureScript.Docs.Tags - ( tags - , dumpCtags - , dumpEtags - ) where - -import Prelude - -import Control.Arrow (first) -import Data.List (sort) -import Data.Maybe (mapMaybe) -import Data.Text qualified as T -import Language.PureScript.AST (SourceSpan, sourcePosLine, spanStart) -import Language.PureScript.Docs.Types (ChildDeclaration(..), Declaration(..), Module(..)) - -tags :: Module -> [(String, Int)] -tags = map (first T.unpack) . concatMap dtags . modDeclarations - where - dtags :: Declaration -> [(T.Text, Int)] - dtags decl = case declSourceSpan decl of - Just ss -> (declTitle decl, pos ss):mapMaybe subtag (declChildren decl) - Nothing -> mapMaybe subtag $ declChildren decl - - subtag :: ChildDeclaration -> Maybe (T.Text, Int) - subtag cdecl = case cdeclSourceSpan cdecl of - Just ss -> Just (cdeclTitle cdecl, pos ss) - Nothing -> Nothing - - pos :: SourceSpan -> Int - pos = sourcePosLine . spanStart - --- etags files appear to be sorted on module file name: --- from emacs source, `emacs/lib-src/etags.c`: --- "In etags mode, sort by file name." -dumpEtags :: [(String, Module)] -> [String] -dumpEtags = concatMap renderModEtags . sort - -renderModEtags :: (String, Module) -> [String] -renderModEtags (path, mdl) = ["\x0c", path ++ "," ++ show tagsLen] ++ tagLines - where tagsLen = sum $ map length tagLines - tagLines = map tagLine $ tags mdl - tagLine (name, line) = "\x7f" ++ name ++ "\x01" ++ show line ++ "," - --- ctags files are required to be sorted: http://ctags.sourceforge.net/FORMAT --- "The tags file is sorted on {tagname}. This allows for a binary search in --- the file." -dumpCtags :: [(String, Module)] -> [String] -dumpCtags = sort . concatMap renderModCtags - -renderModCtags :: (String, Module) -> [String] -renderModCtags (path, mdl) = sort tagLines - where tagLines = map tagLine $ tags mdl - tagLine (name, line) = name ++ "\t" ++ path ++ "\t" ++ show line diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Types.hs deleted file mode 100644 index ea130665..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Types.hs +++ /dev/null @@ -1,882 +0,0 @@ -module Language.PureScript.Docs.Types - ( module Language.PureScript.Docs.Types - , module ReExports - ) - where - -import Protolude hiding (to, from, unlines) -import Prelude (String, unlines, lookup) - -import Control.Arrow ((***)) - -import Data.Aeson ((.=)) -import Data.Aeson.Key qualified as A.Key -import Data.Aeson.BetterErrors - (Parse, keyOrDefault, throwCustomError, key, asText, - keyMay, withString, eachInArray, asNull, (.!), toAesonParser, toAesonParser', - fromAesonParser, perhaps, withText, asIntegral, nth, eachInObjectWithKey, - asString) -import Data.Map qualified as Map -import Data.Time.Clock (UTCTime) -import Data.Time.Format qualified as TimeFormat -import Data.Version (Version(..), showVersion) -import Data.Aeson qualified as A -import Data.Text qualified as T -import Data.Vector qualified as V - -import Language.PureScript.AST qualified as P -import Language.PureScript.CoreFn.FromJSON qualified as P -import Language.PureScript.Crash qualified as P -import Language.PureScript.Environment qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Roles qualified as P -import Language.PureScript.Types qualified as P -import Paths_purescript qualified as Paths - -import Web.Bower.PackageMeta (BowerError, PackageMeta(..), PackageName, asPackageMeta, parsePackageName, runPackageName, showBowerError) - -import Language.PureScript.Docs.RenderedCode as ReExports - (RenderedCode, - ContainingModule(..), asContainingModule, - RenderedCodeElement(..), - Namespace(..), FixityAlias) -import Language.PureScript.Publish.Registry.Compat (PursJsonError, showPursJsonError) - -type Type' = P.Type () -type Constraint' = P.Constraint () - --------------------- --- Types - -data Package a = Package - { pkgMeta :: PackageMeta - , pkgVersion :: Version - , pkgVersionTag :: Text - -- TODO: When this field was introduced, it was given the Maybe type for the - -- sake of backwards compatibility, as older JSON blobs will not include the - -- field. It should eventually be changed to just UTCTime. - , pkgTagTime :: Maybe UTCTime - , pkgModules :: [Module] - , pkgModuleMap :: Map P.ModuleName PackageName - , pkgResolvedDependencies :: [(PackageName, Version)] - , pkgGithub :: (GithubUser, GithubRepo) - , pkgUploader :: a - , pkgCompilerVersion :: Version - -- ^ The version of the PureScript compiler which was used to generate - -- this data. We store this in order to reject packages which are too old. - } - deriving (Show, Eq, Ord, Generic) - -instance NFData a => NFData (Package a) - -data NotYetKnown = NotYetKnown - deriving (Show, Eq, Ord, Generic) - -instance NFData NotYetKnown - -type UploadedPackage = Package NotYetKnown -type VerifiedPackage = Package GithubUser - -data ManifestError - = BowerManifest BowerError - | PursManifest PursJsonError - deriving (Show, Eq, Ord, Generic) - -instance NFData ManifestError - -showManifestError :: ManifestError -> Text -showManifestError = \case - BowerManifest err -> showBowerError err - PursManifest err -> showPursJsonError err - -verifyPackage :: GithubUser -> UploadedPackage -> VerifiedPackage -verifyPackage verifiedUser Package{..} = - Package pkgMeta - pkgVersion - pkgVersionTag - pkgTagTime - pkgModules - pkgModuleMap - pkgResolvedDependencies - pkgGithub - verifiedUser - pkgCompilerVersion - -packageName :: Package a -> PackageName -packageName = bowerName . pkgMeta - --- | --- The time format used for serializing package tag times in the JSON format. --- This is the ISO 8601 date format which includes a time and a timezone. --- -jsonTimeFormat :: String -jsonTimeFormat = "%Y-%m-%dT%H:%M:%S%z" - --- | --- Convenience function for formatting a time in the format expected by this --- module. --- -formatTime :: UTCTime -> String -formatTime = - TimeFormat.formatTime TimeFormat.defaultTimeLocale jsonTimeFormat - --- | --- Convenience function for parsing a time in the format expected by this --- module. --- -parseTime :: String -> Maybe UTCTime -parseTime = - TimeFormat.parseTimeM False TimeFormat.defaultTimeLocale jsonTimeFormat - -data Module = Module - { modName :: P.ModuleName - , modComments :: Maybe Text - , modDeclarations :: [Declaration] - -- Re-exported values from other modules - , modReExports :: [(InPackage P.ModuleName, [Declaration])] - } - deriving (Show, Eq, Ord, Generic) - -instance NFData Module - -data Declaration = Declaration - { declTitle :: Text - , declComments :: Maybe Text - , declSourceSpan :: Maybe P.SourceSpan - , declChildren :: [ChildDeclaration] - , declInfo :: DeclarationInfo - , declKind :: Maybe KindInfo - } - deriving (Show, Eq, Ord, Generic) - -instance NFData Declaration - --- | --- A value of this type contains information that is specific to a particular --- kind of declaration (as opposed to information which exists in all kinds of --- declarations, which goes into the 'Declaration' type directly). --- --- Many of the constructors are very similar to their equivalents in the real --- PureScript AST, except that they have their name elided, since this is --- already available via the rdTitle field of 'Declaration'. --- -data DeclarationInfo - -- | - -- A value declaration, with its type. - -- - = ValueDeclaration Type' - - -- | - -- A data/newtype declaration, with the kind of declaration (data or - -- newtype) and its type arguments. Constructors are represented as child - -- declarations. - -- - | DataDeclaration P.DataDeclType [(Text, Maybe Type')] [P.Role] - - -- | - -- A data type foreign import, with its kind. - -- - | ExternDataDeclaration Type' [P.Role] - - -- | - -- A type synonym, with its type arguments and its type. - -- - | TypeSynonymDeclaration [(Text, Maybe Type')] Type' - - -- | - -- A type class, with its type arguments, its superclasses and functional - -- dependencies. Instances and members are represented as child declarations. - -- - | TypeClassDeclaration [(Text, Maybe Type')] [Constraint'] [([Text], [Text])] - - -- | - -- An operator alias declaration, with the member the alias is for and the - -- operator's fixity. - -- - | AliasDeclaration P.Fixity FixityAlias - deriving (Show, Eq, Ord, Generic) - -instance NFData DeclarationInfo - --- | --- Wraps enough information to properly render the kind signature --- of a data/newtype/type/class declaration. -data KindInfo = KindInfo - { kiKeyword :: P.KindSignatureFor - , kiKind :: Type' - } - deriving (Show, Eq, Ord, Generic) - -instance NFData KindInfo - -convertFundepsToStrings :: [(Text, Maybe Type')] -> [P.FunctionalDependency] -> [([Text], [Text])] -convertFundepsToStrings args fundeps = - map (\(P.FunctionalDependency from to) -> toArgs from to) fundeps - where - argsVec = V.fromList (map fst args) - getArg i = - fromMaybe - (P.internalError $ unlines - [ "convertDeclaration: Functional dependency index" - , show i - , "is bigger than arguments list" - , show (map fst args) - , "Functional dependencies are" - , show fundeps - ] - ) $ argsVec V.!? i - toArgs from to = (map getArg from, map getArg to) - -declInfoToString :: DeclarationInfo -> Text -declInfoToString (ValueDeclaration _) = "value" -declInfoToString (DataDeclaration _ _ _) = "data" -declInfoToString (ExternDataDeclaration _ _) = "externData" -declInfoToString (TypeSynonymDeclaration _ _) = "typeSynonym" -declInfoToString (TypeClassDeclaration _ _ _) = "typeClass" -declInfoToString (AliasDeclaration _ _) = "alias" - -declInfoNamespace :: DeclarationInfo -> Namespace -declInfoNamespace = \case - ValueDeclaration{} -> - ValueLevel - DataDeclaration{} -> - TypeLevel - ExternDataDeclaration{} -> - TypeLevel - TypeSynonymDeclaration{} -> - TypeLevel - TypeClassDeclaration{} -> - TypeLevel - AliasDeclaration _ alias -> - either (const TypeLevel) (const ValueLevel) (P.disqualify alias) - -isTypeClass :: Declaration -> Bool -isTypeClass Declaration{..} = - case declInfo of - TypeClassDeclaration{} -> True - _ -> False - -isValue :: Declaration -> Bool -isValue Declaration{..} = - case declInfo of - ValueDeclaration{} -> True - _ -> False - -isType :: Declaration -> Bool -isType Declaration{..} = - case declInfo of - TypeSynonymDeclaration{} -> True - DataDeclaration{} -> True - ExternDataDeclaration{} -> True - _ -> False - -isValueAlias :: Declaration -> Bool -isValueAlias Declaration{..} = - case declInfo of - AliasDeclaration _ (P.Qualified _ d) -> isRight d - _ -> False - -isTypeAlias :: Declaration -> Bool -isTypeAlias Declaration{..} = - case declInfo of - AliasDeclaration _ (P.Qualified _ d) -> isLeft d - _ -> False - --- | Discard any children which do not satisfy the given predicate. -filterChildren :: (ChildDeclaration -> Bool) -> Declaration -> Declaration -filterChildren p decl = - decl { declChildren = filter p (declChildren decl) } - -data ChildDeclaration = ChildDeclaration - { cdeclTitle :: Text - , cdeclComments :: Maybe Text - , cdeclSourceSpan :: Maybe P.SourceSpan - , cdeclInfo :: ChildDeclarationInfo - } - deriving (Show, Eq, Ord, Generic) - -instance NFData ChildDeclaration - -data ChildDeclarationInfo - -- | - -- A type instance declaration, with its dependencies and its type. - -- - = ChildInstance [Constraint'] Type' - - -- | - -- A data constructor, with its type arguments. - -- - | ChildDataConstructor [Type'] - - -- | - -- A type class member, with its type. Note that the type does not include - -- the type class constraint; this may be added manually if desired. For - -- example, `pure` from `Applicative` would be `forall a. a -> f a`. - -- - | ChildTypeClassMember Type' - deriving (Show, Eq, Ord, Generic) - -instance NFData ChildDeclarationInfo - -childDeclInfoToString :: ChildDeclarationInfo -> Text -childDeclInfoToString (ChildInstance _ _) = "instance" -childDeclInfoToString (ChildDataConstructor _) = "dataConstructor" -childDeclInfoToString (ChildTypeClassMember _) = "typeClassMember" - -childDeclInfoNamespace :: ChildDeclarationInfo -> Namespace -childDeclInfoNamespace = - -- We could just write this as `const ValueLevel` but by doing it this way, - -- if another constructor is added, we get a warning which acts as a prompt - -- to update this, instead of having this function (possibly incorrectly) - -- just return ValueLevel for the new constructor. - \case - ChildInstance{} -> - ValueLevel - ChildDataConstructor{} -> - ValueLevel - ChildTypeClassMember{} -> - ValueLevel - -isTypeClassMember :: ChildDeclaration -> Bool -isTypeClassMember ChildDeclaration{..} = - case cdeclInfo of - ChildTypeClassMember{} -> True - _ -> False - -isDataConstructor :: ChildDeclaration -> Bool -isDataConstructor ChildDeclaration{..} = - case cdeclInfo of - ChildDataConstructor{} -> True - _ -> False - -newtype GithubUser - = GithubUser { runGithubUser :: Text } - deriving (Show, Eq, Ord, Generic) - -instance NFData GithubUser - -newtype GithubRepo - = GithubRepo { runGithubRepo :: Text } - deriving (Show, Eq, Ord, Generic) - -instance NFData GithubRepo - -data PackageError - = CompilerTooOld Version Version - -- ^ Minimum allowable version for generating data with the current - -- parser, and actual version used. - | ErrorInPackageMeta ManifestError - | InvalidVersion - | InvalidDeclarationType Text - | InvalidChildDeclarationType Text - | InvalidFixity - | InvalidKind Text - | InvalidDataDeclType Text - | InvalidKindSignatureFor Text - | InvalidTime - | InvalidRole Text - deriving (Show, Eq, Ord, Generic) - -instance NFData PackageError - -data InPackage a - = Local a - | FromDep PackageName a - deriving (Show, Eq, Ord, Generic) - -instance NFData a => NFData (InPackage a) - -instance Functor InPackage where - fmap f (Local x) = Local (f x) - fmap f (FromDep pkgName x) = FromDep pkgName (f x) - -ignorePackage :: InPackage a -> a -ignorePackage (Local x) = x -ignorePackage (FromDep _ x) = x - ----------------------------------------------------- --- Types for links between declarations - -data LinksContext = LinksContext - { ctxGithub :: (GithubUser, GithubRepo) - , ctxModuleMap :: Map P.ModuleName PackageName - , ctxResolvedDependencies :: [(PackageName, Version)] - , ctxPackageName :: PackageName - , ctxVersion :: Version - , ctxVersionTag :: Text - } - deriving (Show, Eq, Ord, Generic) - -instance NFData LinksContext - -data DocLink = DocLink - { linkLocation :: LinkLocation - , linkTitle :: Text - , linkNamespace :: Namespace - } - deriving (Show, Eq, Ord, Generic) - -instance NFData DocLink - -data LinkLocation - -- | A link to a declaration in the current package. - = LocalModule P.ModuleName - - -- | A link to a declaration in a different package. The arguments represent - -- the name of the other package, the version of the other package, and the - -- name of the module in the other package that the declaration is in. - | DepsModule PackageName Version P.ModuleName - - -- | A link to a declaration that is built in to the compiler, e.g. the Prim - -- module. In this case we only need to store the module that the builtin - -- comes from. Note that all builtin modules begin with "Prim", and that the - -- compiler rejects attempts to define modules whose names start with "Prim". - | BuiltinModule P.ModuleName - deriving (Show, Eq, Ord, Generic) - -instance NFData LinkLocation - --- | Given a links context, the current module name, the namespace of a thing --- to link to, its title, and its containing module, attempt to create a --- DocLink. -getLink :: LinksContext -> P.ModuleName -> Namespace -> Text -> ContainingModule -> Maybe DocLink -getLink LinksContext{..} curMn namespace target containingMod = do - location <- getLinkLocation - return DocLink - { linkLocation = location - , linkTitle = target - , linkNamespace = namespace - } - - where - getLinkLocation = builtinLinkLocation <|> normalLinkLocation - - normalLinkLocation = do - case containingMod of - ThisModule -> - return $ LocalModule curMn - OtherModule destMn -> - case Map.lookup destMn ctxModuleMap of - Nothing -> - return $ LocalModule destMn - Just pkgName -> do - pkgVersion <- lookup pkgName ctxResolvedDependencies - return $ DepsModule pkgName pkgVersion destMn - - builtinLinkLocation = - case containingMod of - OtherModule mn | P.isBuiltinModuleName mn -> - pure $ BuiltinModule mn - _ -> - empty - -getLinksContext :: Package a -> LinksContext -getLinksContext Package{..} = - LinksContext - { ctxGithub = pkgGithub - , ctxModuleMap = pkgModuleMap - , ctxResolvedDependencies = pkgResolvedDependencies - , ctxPackageName = bowerName pkgMeta - , ctxVersion = pkgVersion - , ctxVersionTag = pkgVersionTag - } - ----------------------- --- Parsing - -asPackage :: Version -> (forall e. Parse e a) -> Parse PackageError (Package a) -asPackage minimumVersion uploader = do - -- If the compilerVersion key is missing, we can be sure that it was produced - -- with 0.7.0.0, since that is the only released version that included the - -- `psc-publish` tool (now `purs publish`) before this key was added. - compilerVersion <- keyOrDefault "compilerVersion" (Version [0,7,0,0] []) asVersion - when (compilerVersion < minimumVersion) - (throwCustomError $ CompilerTooOld minimumVersion compilerVersion) - - Package <$> key "packageMeta" asPackageMeta .! (ErrorInPackageMeta . BowerManifest) - <*> key "version" asVersion - <*> key "versionTag" asText - <*> keyMay "tagTime" (withString parseTimeEither) - <*> key "modules" (eachInArray asModule) - <*> moduleMap - <*> key "resolvedDependencies" asResolvedDependencies - <*> key "github" asGithub - <*> key "uploader" uploader - <*> pure compilerVersion - where - moduleMap = - key "moduleMap" asModuleMap - `pOr` (key "bookmarks" bookmarksAsModuleMap .! ErrorInPackageMeta) - -parseTimeEither :: String -> Either PackageError UTCTime -parseTimeEither = - maybe (Left InvalidTime) Right . parseTime - -asUploadedPackage :: Version -> Parse PackageError UploadedPackage -asUploadedPackage minVersion = asPackage minVersion asNotYetKnown - -asNotYetKnown :: Parse e NotYetKnown -asNotYetKnown = NotYetKnown <$ asNull - -instance A.FromJSON NotYetKnown where - parseJSON = toAesonParser' asNotYetKnown - -displayPackageError :: PackageError -> Text -displayPackageError e = case e of - CompilerTooOld minV usedV -> - "Expecting data produced by at least version " <> T.pack (showVersion minV) - <> " of the compiler, but it appears that " <> T.pack (showVersion usedV) - <> " was used." - ErrorInPackageMeta err -> - "Error in package metadata: " <> showManifestError err - InvalidVersion -> - "Invalid version" - InvalidDeclarationType str -> - "Invalid declaration type: \"" <> str <> "\"" - InvalidChildDeclarationType str -> - "Invalid child declaration type: \"" <> str <> "\"" - InvalidFixity -> - "Invalid fixity" - InvalidKind str -> - "Invalid kind: \"" <> str <> "\"" - InvalidDataDeclType str -> - "Invalid data declaration type: \"" <> str <> "\"" - InvalidKindSignatureFor str -> - "Invalid kind signature keyword: \"" <> str <> "\"" - InvalidTime -> - "Invalid time" - InvalidRole str -> - "Invalid role keyword: \"" <> str <> "\"" - -instance A.FromJSON a => A.FromJSON (Package a) where - parseJSON = toAesonParser displayPackageError - (asPackage (Version [0,0,0,0] []) fromAesonParser) - -asGithubUser :: Parse e GithubUser -asGithubUser = GithubUser <$> asText - -instance A.FromJSON GithubUser where - parseJSON = toAesonParser' asGithubUser - -asVersion :: Parse PackageError Version -asVersion = withString (maybe (Left InvalidVersion) Right . P.parseVersion') - -asModule :: Parse PackageError Module -asModule = - Module <$> key "name" (P.moduleNameFromString <$> asText) - <*> key "comments" (perhaps asText) - <*> key "declarations" (eachInArray asDeclaration) - <*> key "reExports" (eachInArray asReExport) - -asDeclaration :: Parse PackageError Declaration -asDeclaration = - Declaration <$> key "title" asText - <*> key "comments" (perhaps asText) - <*> key "sourceSpan" (perhaps asSourceSpan) - <*> key "children" (eachInArray asChildDeclaration) - <*> key "info" asDeclarationInfo - <*> keyOrDefault "kind" Nothing (perhaps asKindInfo) - -asReExport :: Parse PackageError (InPackage P.ModuleName, [Declaration]) -asReExport = - (,) <$> key "moduleName" asReExportModuleName - <*> key "declarations" (eachInArray asDeclaration) - where - -- This is to preserve backwards compatibility with 0.10.3 and earlier versions - -- of the compiler, where the modReExports field had the type - -- [(P.ModuleName, [Declaration])]. This should eventually be removed, - -- possibly at the same time as the next breaking change to this JSON format. - asReExportModuleName :: Parse PackageError (InPackage P.ModuleName) - asReExportModuleName = - asInPackage fromAesonParser .! ErrorInPackageMeta - `pOr` fmap Local fromAesonParser - -pOr :: Parse e a -> Parse e a -> Parse e a -p `pOr` q = catchError p (const q) - -asInPackage :: Parse ManifestError a -> Parse ManifestError (InPackage a) -asInPackage inner = - build <$> key "package" (perhaps (withText (mapLeft BowerManifest . parsePackageName))) - <*> key "item" inner - where - build Nothing = Local - build (Just pn) = FromDep pn - -asFixity :: Parse PackageError P.Fixity -asFixity = - P.Fixity <$> key "associativity" asAssociativity - <*> key "precedence" asIntegral - -asFixityAlias :: Parse PackageError FixityAlias -asFixityAlias = fromAesonParser - -parseAssociativity :: String -> Maybe P.Associativity -parseAssociativity str = case str of - "infix" -> Just P.Infix - "infixl" -> Just P.Infixl - "infixr" -> Just P.Infixr - _ -> Nothing - -asAssociativity :: Parse PackageError P.Associativity -asAssociativity = withString (maybe (Left InvalidFixity) Right . parseAssociativity) - -asDeclarationInfo :: Parse PackageError DeclarationInfo -asDeclarationInfo = do - ty <- key "declType" asText - case ty of - "value" -> - ValueDeclaration <$> key "type" asType - "data" -> - DataDeclaration <$> key "dataDeclType" asDataDeclType - <*> key "typeArguments" asTypeArguments - <*> keyOrDefault "roles" [] (eachInArray asRole) - "externData" -> - ExternDataDeclaration <$> key "kind" asType - <*> keyOrDefault "roles" [] (eachInArray asRole) - "typeSynonym" -> - TypeSynonymDeclaration <$> key "arguments" asTypeArguments - <*> key "type" asType - "typeClass" -> - TypeClassDeclaration <$> key "arguments" asTypeArguments - <*> key "superclasses" (eachInArray asConstraint) - <*> keyOrDefault "fundeps" [] asFunDeps - "alias" -> - AliasDeclaration <$> key "fixity" asFixity - <*> key "alias" asFixityAlias - -- Backwards compat: kinds are extern data - "kind" -> - pure $ ExternDataDeclaration (P.kindType $> ()) [] - other -> - throwCustomError (InvalidDeclarationType other) - -asKindInfo :: Parse PackageError KindInfo -asKindInfo = - KindInfo <$> key "keyword" asKindSignatureFor - <*> key "kind" asType - -asKindSignatureFor :: Parse PackageError P.KindSignatureFor -asKindSignatureFor = - withText $ \case - "data" -> Right P.DataSig - "newtype" -> Right P.NewtypeSig - "class" -> Right P.ClassSig - "type" -> Right P.TypeSynonymSig - x -> Left (InvalidKindSignatureFor x) - -asTypeArguments :: Parse PackageError [(Text, Maybe Type')] -asTypeArguments = eachInArray asTypeArgument - where - asTypeArgument = (,) <$> nth 0 asText <*> nth 1 (perhaps asType) - -asRole :: Parse PackageError P.Role -asRole = - withText $ \case - "Representational" -> Right P.Representational - "Nominal" -> Right P.Nominal - "Phantom" -> Right P.Phantom - other -> Left (InvalidRole other) - -asType :: Parse e Type' -asType = fromAesonParser - -asFunDeps :: Parse PackageError [([Text], [Text])] -asFunDeps = eachInArray asFunDep - where - asFunDep = (,) <$> nth 0 (eachInArray asText) <*> nth 1 (eachInArray asText) - -asDataDeclType :: Parse PackageError P.DataDeclType -asDataDeclType = - withText $ \case - "data" -> Right P.Data - "newtype" -> Right P.Newtype - other -> Left (InvalidDataDeclType other) - -asChildDeclaration :: Parse PackageError ChildDeclaration -asChildDeclaration = - ChildDeclaration <$> key "title" asText - <*> key "comments" (perhaps asText) - <*> key "sourceSpan" (perhaps asSourceSpan) - <*> key "info" asChildDeclarationInfo - -asChildDeclarationInfo :: Parse PackageError ChildDeclarationInfo -asChildDeclarationInfo = do - ty <- key "declType" asText - case ty of - "instance" -> - ChildInstance <$> key "dependencies" (eachInArray asConstraint) - <*> key "type" asType - "dataConstructor" -> - ChildDataConstructor <$> key "arguments" (eachInArray asType) - "typeClassMember" -> - ChildTypeClassMember <$> key "type" asType - other -> - throwCustomError $ InvalidChildDeclarationType other - -asSourcePos :: Parse e P.SourcePos -asSourcePos = P.SourcePos <$> nth 0 asIntegral - <*> nth 1 asIntegral - -asConstraint :: Parse PackageError Constraint' -asConstraint = P.Constraint () <$> key "constraintClass" asQualifiedProperName - <*> keyOrDefault "constraintKindArgs" [] (eachInArray asType) - <*> key "constraintArgs" (eachInArray asType) - <*> pure Nothing - -asQualifiedProperName :: Parse e (P.Qualified (P.ProperName a)) -asQualifiedProperName = fromAesonParser - -asModuleMap :: Parse PackageError (Map P.ModuleName PackageName) -asModuleMap = - Map.fromList <$> - eachInObjectWithKey (Right . P.moduleNameFromString) - (withText parsePackageName') - --- This is here to preserve backwards compatibility with compilers which used --- to generate a 'bookmarks' field in the JSON (i.e. up to 0.10.5). We should --- remove this after the next breaking change to the JSON. -bookmarksAsModuleMap :: Parse ManifestError (Map P.ModuleName PackageName) -bookmarksAsModuleMap = - convert <$> - eachInArray (asInPackage (nth 0 (P.moduleNameFromString <$> asText))) - - where - convert :: [InPackage P.ModuleName] -> Map P.ModuleName PackageName - convert = Map.fromList . mapMaybe toTuple - - toTuple (Local _) = Nothing - toTuple (FromDep pkgName mn) = Just (mn, pkgName) - -asResolvedDependencies :: Parse PackageError [(PackageName, Version)] -asResolvedDependencies = - eachInObjectWithKey parsePackageName' asVersion - -parsePackageName' :: Text -> Either PackageError PackageName -parsePackageName' = - mapLeft ErrorInPackageMeta . (mapLeft BowerManifest . parsePackageName) - -mapLeft :: (a -> a') -> Either a b -> Either a' b -mapLeft f (Left x) = Left (f x) -mapLeft _ (Right x) = Right x - -asGithub :: Parse e (GithubUser, GithubRepo) -asGithub = (,) <$> nth 0 (GithubUser <$> asText) - <*> nth 1 (GithubRepo <$> asText) - -asSourceSpan :: Parse e P.SourceSpan -asSourceSpan = P.SourceSpan <$> key "name" asString - <*> key "start" asSourcePos - <*> key "end" asSourcePos - ---------------------- --- ToJSON instances - -instance A.ToJSON a => A.ToJSON (Package a) where - toJSON Package{..} = - A.object $ - [ "packageMeta" .= pkgMeta - , "version" .= showVersion pkgVersion - , "versionTag" .= pkgVersionTag - , "modules" .= pkgModules - , "moduleMap" .= assocListToJSON (A.Key.fromText . P.runModuleName) - runPackageName - (Map.toList pkgModuleMap) - , "resolvedDependencies" .= assocListToJSON (A.Key.fromText . runPackageName) - (T.pack . showVersion) - pkgResolvedDependencies - , "github" .= pkgGithub - , "uploader" .= pkgUploader - , "compilerVersion" .= showVersion Paths.version - ] ++ - fmap (\t -> "tagTime" .= formatTime t) (maybeToList pkgTagTime) - -instance A.ToJSON NotYetKnown where - toJSON _ = A.Null - -instance A.ToJSON Module where - toJSON Module{..} = - A.object [ "name" .= P.runModuleName modName - , "comments" .= modComments - , "declarations" .= modDeclarations - , "reExports" .= map toObj modReExports - ] - where - toObj (mn, decls) = A.object [ "moduleName" .= mn - , "declarations" .= decls - ] - -instance A.ToJSON Declaration where - toJSON Declaration{..} = - A.object [ "title" .= declTitle - , "comments" .= declComments - , "sourceSpan" .= declSourceSpan - , "children" .= declChildren - , "info" .= declInfo - , "kind" .= declKind - ] - -instance A.ToJSON KindInfo where - toJSON KindInfo{..} = - A.object [ "keyword" .= kindSignatureForKeyword kiKeyword - , "kind" .= kiKind - ] - -kindSignatureForKeyword :: P.KindSignatureFor -> Text -kindSignatureForKeyword = \case - P.DataSig -> "data" - P.NewtypeSig -> "newtype" - P.TypeSynonymSig -> "type" - P.ClassSig -> "class" - -instance A.ToJSON ChildDeclaration where - toJSON ChildDeclaration{..} = - A.object [ "title" .= cdeclTitle - , "comments" .= cdeclComments - , "sourceSpan" .= cdeclSourceSpan - , "info" .= cdeclInfo - ] - -instance A.ToJSON DeclarationInfo where - toJSON info = A.object $ "declType" .= declInfoToString info : props - where - props = case info of - ValueDeclaration ty -> ["type" .= ty] - DataDeclaration ty args roles -> ["dataDeclType" .= ty, "typeArguments" .= args, "roles" .= roles] - ExternDataDeclaration kind roles -> ["kind" .= kind, "roles" .= roles] - TypeSynonymDeclaration args ty -> ["arguments" .= args, "type" .= ty] - TypeClassDeclaration args super fundeps -> ["arguments" .= args, "superclasses" .= super, "fundeps" .= fundeps] - AliasDeclaration fixity alias -> ["fixity" .= fixity, "alias" .= alias] - -instance A.ToJSON ChildDeclarationInfo where - toJSON info = A.object $ "declType" .= childDeclInfoToString info : props - where - props = case info of - ChildInstance deps ty -> ["dependencies" .= deps, "type" .= ty] - ChildDataConstructor args -> ["arguments" .= args] - ChildTypeClassMember ty -> ["type" .= ty] - -instance A.ToJSON GithubUser where - toJSON = A.toJSON . runGithubUser - -instance A.ToJSON GithubRepo where - toJSON = A.toJSON . runGithubRepo - --- | Given a function for turning association list keys into JSON object keys, --- and a function for turning association list values to JSON string values, --- turns an association list into a JSON object. --- --- For example: --- @assocListToJSON T.pack T.pack [("a", "b")]@ will give @{"a": "b"}@. -assocListToJSON :: (a -> A.Key) -> (b -> Text) -> [(a, b)] -> A.Value -assocListToJSON f g xs = A.object (map (uncurry (.=) . (f *** g)) xs) - -instance A.ToJSON a => A.ToJSON (InPackage a) where - toJSON x = - case x of - Local y -> withPackage (Nothing :: Maybe ()) y - FromDep pn y -> withPackage (Just pn) y - where - withPackage :: (A.ToJSON p, A.ToJSON y) => p -> y -> A.Value - withPackage p y = - A.object [ "package" .= p - , "item" .= y - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/Docs/Utils/MonoidExtras.hs b/claude-help/original-compiler/src/Language/PureScript/Docs/Utils/MonoidExtras.hs deleted file mode 100644 index 6f2bf370..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Docs/Utils/MonoidExtras.hs +++ /dev/null @@ -1,8 +0,0 @@ -module Language.PureScript.Docs.Utils.MonoidExtras where - -import Data.Monoid (Monoid(..), (<>)) - -mintersperse :: (Monoid m) => m -> [m] -> m -mintersperse _ [] = mempty -mintersperse _ [x] = x -mintersperse sep (x:xs) = x <> sep <> mintersperse sep xs diff --git a/claude-help/original-compiler/src/Language/PureScript/Environment.hs b/claude-help/original-compiler/src/Language/PureScript/Environment.hs deleted file mode 100644 index 1f7d2e6f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Environment.hs +++ /dev/null @@ -1,690 +0,0 @@ -module Language.PureScript.Environment where - -import Prelude - -import GHC.Generics (Generic) -import Control.DeepSeq (NFData) -import Control.Monad (unless) -import Codec.Serialise (Serialise) -import Data.Aeson ((.=), (.:), ToJSON, FromJSON) -import Data.Aeson qualified as A -import Data.Foldable (find, fold) -import Data.Functor ((<&>)) -import Data.IntMap qualified as IM -import Data.IntSet qualified as IS -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Maybe (fromMaybe, mapMaybe) -import Data.Semigroup (First(..)) -import Data.Text (Text) -import Data.Text qualified as T -import Data.List.NonEmpty qualified as NEL - -import Language.PureScript.AST.SourcePos (nullSourceAnn) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Names (Ident, ProperName(..), ProperNameType(..), Qualified, QualifiedBy, coerceProperName) -import Language.PureScript.Roles (Role(..)) -import Language.PureScript.TypeClassDictionaries (NamedDict) -import Language.PureScript.Types (SourceConstraint, SourceType, Type(..), TypeVarVisibility(..), eqType, srcTypeConstructor, freeTypeVariables) -import Language.PureScript.Constants.Prim qualified as C - --- | The @Environment@ defines all values and types which are currently in scope: -data Environment = Environment - { names :: M.Map (Qualified Ident) (SourceType, NameKind, NameVisibility) - -- ^ Values currently in scope - , types :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) - -- ^ Type names currently in scope - , dataConstructors :: M.Map (Qualified (ProperName 'ConstructorName)) (DataDeclType, ProperName 'TypeName, SourceType, [Ident]) - -- ^ Data constructors currently in scope, along with their associated type - -- constructor name, argument types and return type. - , typeSynonyms :: M.Map (Qualified (ProperName 'TypeName)) ([(Text, Maybe SourceType)], SourceType) - -- ^ Type synonyms currently in scope - , typeClassDictionaries :: M.Map QualifiedBy (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict))) - -- ^ Available type class dictionaries. When looking up 'Nothing' in the - -- outer map, this returns the map of type class dictionaries in local - -- scope (ie dictionaries brought in by a constrained type). - , typeClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData - -- ^ Type classes - } deriving (Show, Generic) - -instance NFData Environment - --- | Information about a type class -data TypeClassData = TypeClassData - { typeClassArguments :: [(Text, Maybe SourceType)] - -- ^ A list of type argument names, and their kinds, where kind annotations - -- were provided. - , typeClassMembers :: [(Ident, SourceType, Maybe (S.Set (NEL.NonEmpty Int)))] - -- ^ A list of type class members and their types and whether or not - -- they have type variables that must be defined using Visible Type Applications. - -- Type arguments listed above are considered bound in these types. - , typeClassSuperclasses :: [SourceConstraint] - -- ^ A list of superclasses of this type class. Type arguments listed above - -- are considered bound in the types appearing in these constraints. - , typeClassDependencies :: [FunctionalDependency] - -- ^ A list of functional dependencies for the type arguments of this class. - , typeClassDeterminedArguments :: S.Set Int - -- ^ A set of indexes of type argument that are fully determined by other - -- arguments via functional dependencies. This can be computed from both - -- typeClassArguments and typeClassDependencies. - , typeClassCoveringSets :: S.Set (S.Set Int) - -- ^ A sets of arguments that can be used to infer all other arguments. - , typeClassIsEmpty :: Bool - -- ^ Whether or not dictionaries for this type class are necessarily empty. - } deriving (Show, Generic) - -instance NFData TypeClassData - --- | A functional dependency indicates a relationship between two sets of --- type arguments in a class declaration. -data FunctionalDependency = FunctionalDependency - { fdDeterminers :: [Int] - -- ^ the type arguments which determine the determined type arguments - , fdDetermined :: [Int] - -- ^ the determined type arguments - } deriving (Eq, Show, Generic) - -instance NFData FunctionalDependency -instance Serialise FunctionalDependency - -instance A.FromJSON FunctionalDependency where - parseJSON = A.withObject "FunctionalDependency" $ \o -> - FunctionalDependency - <$> o .: "determiners" - <*> o .: "determined" - -instance A.ToJSON FunctionalDependency where - toJSON FunctionalDependency{..} = - A.object [ "determiners" .= fdDeterminers - , "determined" .= fdDetermined - ] - --- | The initial environment with no values and only the default javascript types defined -initEnvironment :: Environment -initEnvironment = Environment M.empty allPrimTypes M.empty M.empty M.empty allPrimClasses - --- | A constructor for TypeClassData that computes which type class arguments are fully determined --- and argument covering sets. --- Fully determined means that this argument cannot be used when selecting a type class instance. --- A covering set is a minimal collection of arguments that can be used to find an instance and --- therefore determine all other type arguments. --- --- An example of the difference between determined and fully determined would be with the class: --- ```class C a b c | a -> b, b -> a, b -> c``` --- In this case, `a` must differ when `b` differs, and vice versa - each is determined by the other. --- Both `a` and `b` can be used in selecting a type class instance. However, `c` cannot - it is --- fully determined by `a` and `b`. --- --- Define a graph of type class arguments with edges being fundep determiners to determined. Each --- argument also has a self looping edge. --- An argument is fully determined if doesn't appear at the start of a path of strongly connected components. --- An argument is not fully determined otherwise. --- --- The way we compute this is by saying: an argument X is fully determined if there are arguments that --- determine X that X does not determine. This is the same thing: everything X determines includes everything --- in its SCC, and everything determining X is either before it in an SCC path, or in the same SCC. -makeTypeClassData - :: [(Text, Maybe SourceType)] - -> [(Ident, SourceType)] - -> [SourceConstraint] - -> [FunctionalDependency] - -> Bool - -> TypeClassData -makeTypeClassData args m s deps = TypeClassData args m' s deps determinedArgs coveringSets - where - ( determinedArgs, coveringSets ) = computeCoveringSets (length args) deps - - coveringSets' = S.toList coveringSets - - m' = map (\(a, b) -> (a, b, addVtaInfo b)) m - - addVtaInfo :: SourceType -> Maybe (S.Set (NEL.NonEmpty Int)) - addVtaInfo memberTy = do - let mentionedArgIndexes = S.fromList (mapMaybe argToIndex $ freeTypeVariables memberTy) - let leftovers = map (`S.difference` mentionedArgIndexes) coveringSets' - S.fromList <$> traverse (NEL.nonEmpty . S.toList) leftovers - - argToIndex :: Text -> Maybe Int - argToIndex = flip M.lookup $ M.fromList (zipWith ((,) . fst) args [0..]) - --- A moving frontier of sets to consider, along with the fundeps that can be --- applied in each case. At each stage, all sets in the frontier will be the --- same size, decreasing by 1 each time. -type Frontier = M.Map IS.IntSet (First (IM.IntMap (NEL.NonEmpty IS.IntSet))) --- ^ ^ ^ ^ --- when *these* parameters | | | --- are still needed, | | | --- *these* parameters | | --- can be determined | | --- from a non-zero | --- number of fundeps, | --- which accept *these* --- parameters as inputs. - -computeCoveringSets :: Int -> [FunctionalDependency] -> (S.Set Int, S.Set (S.Set Int)) -computeCoveringSets nargs deps = ( determinedArgs, coveringSets ) - where - argumentIndices = S.fromList [0 .. nargs - 1] - - -- Compute all sets of arguments that determine the remaining arguments via - -- functional dependencies. This is done in stages, where each stage - -- considers sets of the same size to share work. - allCoveringSets :: S.Set (S.Set Int) - allCoveringSets = S.map (S.fromDistinctAscList . IS.toAscList) $ fst $ search $ - -- The initial frontier consists of just the set of all parameters and all - -- fundeps organized into the map structure. - M.singleton - (IS.fromList [0 .. nargs - 1]) $ - First $ IM.fromListWith (<>) $ do - fd <- deps - let srcs = pure (IS.fromList (fdDeterminers fd)) - tgt <- fdDetermined fd - pure (tgt, srcs) - - where - - -- Recursively advance the frontier until all frontiers are exhausted - -- and coverings sets found. The covering sets found during the process - -- are locally-minimal, in that none can be reduced by a fundep, but - -- there may be subsets found from other frontiers. - search :: Frontier -> (S.Set IS.IntSet, ()) - search frontier = unless (null frontier) $ M.foldMapWithKey step frontier >>= search - - -- The input set from the frontier is known to cover all parameters, but - -- it may be able to be reduced by more fundeps. - step :: IS.IntSet -> First (IM.IntMap (NEL.NonEmpty IS.IntSet)) -> (S.Set IS.IntSet, Frontier) - step needed (First inEdges) - -- If there are no applicable fundeps, record it as a locally minimal - -- covering set. This has already been reduced to only applicable fundeps - | IM.null inEdges = (S.singleton needed, M.empty) - | otherwise = (S.empty, foldMap removeParameter paramsToTry) - - where - - determined = IM.keys inEdges - -- If there is an acyclically determined functional dependency, prefer - -- it to reduce the number of cases to check. That is a dependency - -- that does not help determine other parameters. - acycDetermined = find (`IS.notMember` (IS.unions $ concatMap NEL.toList $ IM.elems inEdges)) determined - paramsToTry = maybe determined pure acycDetermined - - -- For each parameter to be removed to build the next frontier, - -- delete the fundeps that determine it and filter out the fundeps - -- that make use of it. Of course, if it an acyclic fundep we already - -- found that there are none that use it. - removeParameter :: Int -> Frontier - removeParameter y = - M.singleton - (IS.delete y needed) $ - case acycDetermined of - Just _ -> First $ IM.delete y inEdges - Nothing -> - First $ IM.mapMaybe (NEL.nonEmpty . NEL.filter (y `IS.notMember`)) $ IM.delete y inEdges - - -- Reduce to the inclusion-minimal sets - coveringSets = S.filter (\v -> not (any (\c -> c `S.isProperSubsetOf` v) allCoveringSets)) allCoveringSets - - -- An argument is determined if it is in no covering set - determinedArgs = argumentIndices `S.difference` fold coveringSets - --- | The visibility of a name in scope -data NameVisibility - = Undefined - -- ^ The name is defined in the current binding group, but is not visible - | Defined - -- ^ The name is defined in the another binding group, or has been made visible by a function binder - deriving (Show, Eq, Generic) - -instance NFData NameVisibility -instance Serialise NameVisibility - --- | A flag for whether a name is for an private or public value - only public values will be --- included in a generated externs file. -data NameKind - = Private - -- ^ A private value introduced as an artifact of code generation (class instances, class member - -- accessors, etc.) - | Public - -- ^ A public value for a module member or foreign import declaration - | External - -- ^ A name for member introduced by foreign import - deriving (Show, Eq, Generic) - -instance NFData NameKind -instance Serialise NameKind - --- | The kinds of a type -data TypeKind - = DataType DataDeclType [(Text, Maybe SourceType, Role)] [(ProperName 'ConstructorName, [SourceType])] - -- ^ Data type - | TypeSynonym - -- ^ Type synonym - | ExternData [Role] - -- ^ Foreign data - | LocalTypeVariable - -- ^ A local type variable - | ScopedTypeVar - -- ^ A scoped type variable - deriving (Show, Eq, Generic) - -instance ToJSON TypeKind -instance FromJSON TypeKind - -instance NFData TypeKind -instance Serialise TypeKind - --- | The type ('data' or 'newtype') of a data type declaration -data DataDeclType - = Data - -- ^ A standard data constructor - | Newtype - -- ^ A newtype constructor - deriving (Show, Eq, Ord, Generic) - -instance NFData DataDeclType -instance Serialise DataDeclType - -showDataDeclType :: DataDeclType -> Text -showDataDeclType Data = "data" -showDataDeclType Newtype = "newtype" - -instance A.ToJSON DataDeclType where - toJSON = A.toJSON . showDataDeclType - -instance A.FromJSON DataDeclType where - parseJSON = A.withText "DataDeclType" $ \case - "data" -> return Data - "newtype" -> return Newtype - other -> fail $ "invalid type: '" ++ T.unpack other ++ "'" - --- | Kind of ground types -kindType :: SourceType -kindType = srcTypeConstructor C.Type - -kindConstraint :: SourceType -kindConstraint = srcTypeConstructor C.Constraint - -kindSymbol :: SourceType -kindSymbol = srcTypeConstructor C.Symbol - -kindDoc :: SourceType -kindDoc = srcTypeConstructor C.Doc - -kindOrdering :: SourceType -kindOrdering = srcTypeConstructor C.TypeOrdering - -kindRowList :: SourceType -> SourceType -kindRowList = TypeApp nullSourceAnn (srcTypeConstructor C.RowList) - -kindRow :: SourceType -> SourceType -kindRow = TypeApp nullSourceAnn (srcTypeConstructor C.Row) - -kindOfREmpty :: SourceType -kindOfREmpty = tyForall "k" kindType (kindRow (tyVar "k")) - --- | Type constructor for functions -tyFunction :: SourceType -tyFunction = srcTypeConstructor C.Function - --- | Type constructor for strings -tyString :: SourceType -tyString = srcTypeConstructor C.String - --- | Type constructor for strings -tyChar :: SourceType -tyChar = srcTypeConstructor C.Char - --- | Type constructor for numbers -tyNumber :: SourceType -tyNumber = srcTypeConstructor C.Number - --- | Type constructor for integers -tyInt :: SourceType -tyInt = srcTypeConstructor C.Int - --- | Type constructor for booleans -tyBoolean :: SourceType -tyBoolean = srcTypeConstructor C.Boolean - --- | Type constructor for arrays -tyArray :: SourceType -tyArray = srcTypeConstructor C.Array - --- | Type constructor for records -tyRecord :: SourceType -tyRecord = srcTypeConstructor C.Record - -tyVar :: Text -> SourceType -tyVar = TypeVar nullSourceAnn - -tyForall :: Text -> SourceType -> SourceType -> SourceType -tyForall var k ty = ForAll nullSourceAnn TypeVarInvisible var (Just k) ty Nothing - --- | Smart constructor for function types -function :: SourceType -> SourceType -> SourceType -function = TypeApp nullSourceAnn . TypeApp nullSourceAnn tyFunction - --- To make reading the kind signatures below easier -(-:>) :: SourceType -> SourceType -> SourceType -(-:>) = function -infixr 4 -:> - -primClass :: Qualified (ProperName 'ClassName) -> (SourceType -> SourceType) -> [(Qualified (ProperName 'TypeName), (SourceType, TypeKind))] -primClass name mkKind = - [ let k = mkKind kindConstraint - in (coerceProperName <$> name, (k, ExternData (nominalRolesForKind k))) - , let k = mkKind kindType - in (dictTypeName . coerceProperName <$> name, (k, TypeSynonym)) - ] - --- | The primitive types in the external environment with their --- associated kinds. There are also pseudo `Fail`, `Warn`, and `Partial` types --- that correspond to the classes with the same names. -primTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primTypes = - M.fromList - [ (C.Type, (kindType, ExternData [])) - , (C.Constraint, (kindType, ExternData [])) - , (C.Symbol, (kindType, ExternData [])) - , (C.Row, (kindType -:> kindType, ExternData [Phantom])) - , (C.Function, (kindType -:> kindType -:> kindType, ExternData [Representational, Representational])) - , (C.Array, (kindType -:> kindType, ExternData [Representational])) - , (C.Record, (kindRow kindType -:> kindType, ExternData [Representational])) - , (C.String, (kindType, ExternData [])) - , (C.Char, (kindType, ExternData [])) - , (C.Number, (kindType, ExternData [])) - , (C.Int, (kindType, ExternData [])) - , (C.Boolean, (kindType, ExternData [])) - , (C.Partial <&> coerceProperName, (kindConstraint, ExternData [])) - ] - --- | This 'Map' contains all of the prim types from all Prim modules. -allPrimTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -allPrimTypes = M.unions - [ primTypes - , primBooleanTypes - , primCoerceTypes - , primOrderingTypes - , primRowTypes - , primRowListTypes - , primSymbolTypes - , primIntTypes - , primTypeErrorTypes - ] - -primBooleanTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primBooleanTypes = - M.fromList - [ (C.True, (tyBoolean, ExternData [])) - , (C.False, (tyBoolean, ExternData [])) - ] - -primCoerceTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primCoerceTypes = - M.fromList $ mconcat - [ primClass C.Coercible (\kind -> tyForall "k" kindType $ tyVar "k" -:> tyVar "k" -:> kind) - ] - -primOrderingTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primOrderingTypes = - M.fromList - [ (C.TypeOrdering, (kindType, ExternData [])) - , (C.LT, (kindOrdering, ExternData [])) - , (C.EQ, (kindOrdering, ExternData [])) - , (C.GT, (kindOrdering, ExternData [])) - ] - -primRowTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primRowTypes = - M.fromList $ mconcat - [ primClass C.RowUnion (\kind -> tyForall "k" kindType $ kindRow (tyVar "k") -:> kindRow (tyVar "k") -:> kindRow (tyVar "k") -:> kind) - , primClass C.RowNub (\kind -> tyForall "k" kindType $ kindRow (tyVar "k") -:> kindRow (tyVar "k") -:> kind) - , primClass C.RowLacks (\kind -> tyForall "k" kindType $ kindSymbol -:> kindRow (tyVar "k") -:> kind) - , primClass C.RowCons (\kind -> tyForall "k" kindType $ kindSymbol -:> tyVar "k" -:> kindRow (tyVar "k") -:> kindRow (tyVar "k") -:> kind) - ] - -primRowListTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primRowListTypes = - M.fromList $ - [ (C.RowList, (kindType -:> kindType, ExternData [Phantom])) - , (C.RowListCons, (tyForall "k" kindType $ kindSymbol -:> tyVar "k" -:> kindRowList (tyVar "k") -:> kindRowList (tyVar "k"), ExternData [Phantom, Phantom, Phantom])) - , (C.RowListNil, (tyForall "k" kindType $ kindRowList (tyVar "k"), ExternData [])) - ] <> mconcat - [ primClass C.RowToList (\kind -> tyForall "k" kindType $ kindRow (tyVar "k") -:> kindRowList (tyVar "k") -:> kind) - ] - -primSymbolTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primSymbolTypes = - M.fromList $ mconcat - [ primClass C.SymbolAppend (\kind -> kindSymbol -:> kindSymbol -:> kindSymbol -:> kind) - , primClass C.SymbolCompare (\kind -> kindSymbol -:> kindSymbol -:> kindOrdering -:> kind) - , primClass C.SymbolCons (\kind -> kindSymbol -:> kindSymbol -:> kindSymbol -:> kind) - ] - -primIntTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primIntTypes = - M.fromList $ mconcat - [ primClass C.IntAdd (\kind -> tyInt -:> tyInt -:> tyInt -:> kind) - , primClass C.IntCompare (\kind -> tyInt -:> tyInt -:> kindOrdering -:> kind) - , primClass C.IntMul (\kind -> tyInt -:> tyInt -:> tyInt -:> kind) - , primClass C.IntToString (\kind -> tyInt -:> kindSymbol -:> kind) - ] - -primTypeErrorTypes :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) -primTypeErrorTypes = - M.fromList $ - [ (C.Doc, (kindType, ExternData [])) - , (C.Fail <&> coerceProperName, (kindDoc -:> kindConstraint, ExternData [Nominal])) - , (C.Warn <&> coerceProperName, (kindDoc -:> kindConstraint, ExternData [Nominal])) - , (C.Text, (kindSymbol -:> kindDoc, ExternData [Phantom])) - , (C.Quote, (tyForall "k" kindType $ tyVar "k" -:> kindDoc, ExternData [Phantom])) - , (C.QuoteLabel, (kindSymbol -:> kindDoc, ExternData [Phantom])) - , (C.Beside, (kindDoc -:> kindDoc -:> kindDoc, ExternData [Phantom, Phantom])) - , (C.Above, (kindDoc -:> kindDoc -:> kindDoc, ExternData [Phantom, Phantom])) - ] <> mconcat - [ primClass C.Fail (\kind -> kindDoc -:> kind) - , primClass C.Warn (\kind -> kindDoc -:> kind) - ] - --- | The primitive class map. This just contains the `Partial` class. --- `Partial` is used as a kind of magic constraint for partial functions. -primClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primClasses = - M.fromList - [ (C.Partial, makeTypeClassData [] [] [] [] True) - ] - --- | This contains all of the type classes from all Prim modules. -allPrimClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -allPrimClasses = M.unions - [ primClasses - , primCoerceClasses - , primRowClasses - , primRowListClasses - , primSymbolClasses - , primIntClasses - , primTypeErrorClasses - ] - -primCoerceClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primCoerceClasses = - M.fromList - -- class Coercible (a :: k) (b :: k) - [ (C.Coercible, makeTypeClassData - [ ("a", Just (tyVar "k")) - , ("b", Just (tyVar "k")) - ] [] [] [] True) - ] - -primRowClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primRowClasses = - M.fromList - -- class Union (left :: Row k) (right :: Row k) (union :: Row k) | left right -> union, right union -> left, union left -> right - [ (C.RowUnion, makeTypeClassData - [ ("left", Just (kindRow (tyVar "k"))) - , ("right", Just (kindRow (tyVar "k"))) - , ("union", Just (kindRow (tyVar "k"))) - ] [] [] - [ FunctionalDependency [0, 1] [2] - , FunctionalDependency [1, 2] [0] - , FunctionalDependency [2, 0] [1] - ] True) - - -- class Nub (original :: Row k) (nubbed :: Row k) | original -> nubbed - , (C.RowNub, makeTypeClassData - [ ("original", Just (kindRow (tyVar "k"))) - , ("nubbed", Just (kindRow (tyVar "k"))) - ] [] [] - [ FunctionalDependency [0] [1] - ] True) - - -- class Lacks (label :: Symbol) (row :: Row k) - , (C.RowLacks, makeTypeClassData - [ ("label", Just kindSymbol) - , ("row", Just (kindRow (tyVar "k"))) - ] [] [] [] True) - - -- class RowCons (label :: Symbol) (a :: k) (tail :: Row k) (row :: Row k) | label tail a -> row, label row -> tail a - , (C.RowCons, makeTypeClassData - [ ("label", Just kindSymbol) - , ("a", Just (tyVar "k")) - , ("tail", Just (kindRow (tyVar "k"))) - , ("row", Just (kindRow (tyVar "k"))) - ] [] [] - [ FunctionalDependency [0, 1, 2] [3] - , FunctionalDependency [0, 3] [1, 2] - ] True) - ] - -primRowListClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primRowListClasses = - M.fromList - -- class RowToList (row :: Row k) (list :: RowList k) | row -> list - [ (C.RowToList, makeTypeClassData - [ ("row", Just (kindRow (tyVar "k"))) - , ("list", Just (kindRowList (tyVar "k"))) - ] [] [] - [ FunctionalDependency [0] [1] - ] True) - ] - -primSymbolClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primSymbolClasses = - M.fromList - -- class Append (left :: Symbol) (right :: Symbol) (appended :: Symbol) | left right -> appended, right appended -> left, appended left -> right - [ (C.SymbolAppend, makeTypeClassData - [ ("left", Just kindSymbol) - , ("right", Just kindSymbol) - , ("appended", Just kindSymbol) - ] [] [] - [ FunctionalDependency [0, 1] [2] - , FunctionalDependency [1, 2] [0] - , FunctionalDependency [2, 0] [1] - ] True) - - -- class Compare (left :: Symbol) (right :: Symbol) (ordering :: Ordering) | left right -> ordering - , (C.SymbolCompare, makeTypeClassData - [ ("left", Just kindSymbol) - , ("right", Just kindSymbol) - , ("ordering", Just kindOrdering) - ] [] [] - [ FunctionalDependency [0, 1] [2] - ] True) - - -- class Cons (head :: Symbol) (tail :: Symbol) (symbol :: Symbol) | head tail -> symbol, symbol -> head tail - , (C.SymbolCons, makeTypeClassData - [ ("head", Just kindSymbol) - , ("tail", Just kindSymbol) - , ("symbol", Just kindSymbol) - ] [] [] - [ FunctionalDependency [0, 1] [2] - , FunctionalDependency [2] [0, 1] - ] True) - ] - -primIntClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primIntClasses = - M.fromList - -- class Add (left :: Int) (right :: Int) (sum :: Int) | left right -> sum, left sum -> right, right sum -> left - [ (C.IntAdd, makeTypeClassData - [ ("left", Just tyInt) - , ("right", Just tyInt) - , ("sum", Just tyInt) - ] [] [] - [ FunctionalDependency [0, 1] [2] - , FunctionalDependency [0, 2] [1] - , FunctionalDependency [1, 2] [0] - ] True) - - -- class Compare (left :: Int) (right :: Int) (ordering :: Ordering) | left right -> ordering - , (C.IntCompare, makeTypeClassData - [ ("left", Just tyInt) - , ("right", Just tyInt) - , ("ordering", Just kindOrdering) - ] [] [] - [ FunctionalDependency [0, 1] [2] - ] True) - - -- class Mul (left :: Int) (right :: Int) (product :: Int) | left right -> product - , (C.IntMul, makeTypeClassData - [ ("left", Just tyInt) - , ("right", Just tyInt) - , ("product", Just tyInt) - ] [] [] - [ FunctionalDependency [0, 1] [2] - ] True) - - -- class ToString (int :: Int) (string :: Symbol) | int -> string - , (C.IntToString, makeTypeClassData - [ ("int", Just tyInt) - , ("string", Just kindSymbol) - ] [] [] - [ FunctionalDependency [0] [1] - ] True) - ] - -primTypeErrorClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData -primTypeErrorClasses = - M.fromList - -- class Fail (message :: Symbol) - [ (C.Fail, makeTypeClassData - [("message", Just kindDoc)] [] [] [] True) - - -- class Warn (message :: Symbol) - , (C.Warn, makeTypeClassData - [("message", Just kindDoc)] [] [] [] True) - ] - --- | Finds information about data constructors from the current environment. -lookupConstructor :: Environment -> Qualified (ProperName 'ConstructorName) -> (DataDeclType, ProperName 'TypeName, SourceType, [Ident]) -lookupConstructor env ctor = - fromMaybe (internalError "Data constructor not found") $ ctor `M.lookup` dataConstructors env - --- | Finds information about values from the current environment. -lookupValue :: Environment -> Qualified Ident -> Maybe (SourceType, NameKind, NameVisibility) -lookupValue env ident = ident `M.lookup` names env - -dictTypeName' :: Text -> Text -dictTypeName' = (<> "$Dict") - -dictTypeName :: ProperName a -> ProperName a -dictTypeName = ProperName . dictTypeName' . runProperName - -isDictTypeName :: ProperName a -> Bool -isDictTypeName = T.isSuffixOf "$Dict" . runProperName - --- | --- Given the kind of a type, generate a list @Nominal@ roles. This is used for --- opaque foreign types as well as type classes. -nominalRolesForKind :: Type a -> [Role] -nominalRolesForKind k = replicate (kindArity k) Nominal - -kindArity :: Type a -> Int -kindArity = length . fst . unapplyKinds - -unapplyKinds :: Type a -> ([Type a], Type a) -unapplyKinds = go [] where - go kinds (TypeApp _ (TypeApp _ fn k1) k2) - | eqType fn tyFunction = go (k1 : kinds) k2 - go kinds (ForAll _ _ _ _ k _) = go kinds k - go kinds k = (reverse kinds, k) diff --git a/claude-help/original-compiler/src/Language/PureScript/Errors.hs b/claude-help/original-compiler/src/Language/PureScript/Errors.hs deleted file mode 100644 index 309a4e9b..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Errors.hs +++ /dev/null @@ -1,2078 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -module Language.PureScript.Errors - ( module Language.PureScript.AST - , module Language.PureScript.Errors - ) where - -import Prelude -import Protolude (unsnoc) - -import Control.Arrow ((&&&)) -import Control.DeepSeq (NFData) -import Control.Lens (both, head1, over) -import Control.Monad (forM, unless) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Trans.State.Lazy (State, evalState, get, put) -import Control.Monad.Writer (MonadWriter(..), censor) -import Data.Monoid (Last(..)) -import Data.Bifunctor (first, second) -import Data.Bitraversable (bitraverse) -import Data.Char (isSpace) -import Data.Containers.ListUtils (nubOrdOn) -import Data.Either (partitionEithers) -import Data.Foldable (fold) -import Data.Function (on) -import Data.Functor (($>)) -import Data.Functor.Identity (Identity(..)) -import Data.List (transpose, nubBy, partition, dropWhileEnd, sortOn, uncons) -import Data.List.NonEmpty qualified as NEL -import Data.List.NonEmpty (NonEmpty((:|))) -import Data.Maybe (maybeToList, fromMaybe, isJust, mapMaybe) -import Data.IntMap.Strict qualified as M -import Data.Ord (Down(..)) -import Data.Set qualified as S -import Data.Text qualified as T -import Data.Text (Text) -import Data.Traversable (for) -import GHC.Generics (Generic) -import GHC.Stack qualified -import Language.PureScript.AST -import Language.PureScript.Bundle qualified as Bundle -import Language.PureScript.Constants.Libs qualified as C -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.Crash (internalError) -import Language.PureScript.CST.Errors qualified as CST -import Language.PureScript.CST.Print qualified as CST -import Language.PureScript.Label (Label(..)) -import Language.PureScript.Names -import Language.PureScript.Pretty (prettyPrintBinderAtom, prettyPrintLabel, prettyPrintObjectKey, prettyPrintSuggestedType, prettyPrintValue, typeAsBox, typeAtomAsBox, typeDiffAsBox) -import Language.PureScript.Pretty.Common (endWith) -import Language.PureScript.PSString (decodeStringWithReplacement) -import Language.PureScript.Roles (Role, displayRole) -import Language.PureScript.Traversals (sndM) -import Language.PureScript.Types (Constraint(..), ConstraintData(..), RowListItem(..), SourceConstraint, SourceType, Type(..), eraseForAllKindAnnotations, eraseKindApps, everywhereOnTypesTopDownM, getAnnForType, isMonoType, overConstraintArgs, rowFromList, rowToList, srcTUnknown) -import Language.PureScript.Publish.BoxesHelpers qualified as BoxHelpers -import System.Console.ANSI qualified as ANSI -import System.FilePath (makeRelative) -import Text.PrettyPrint.Boxes qualified as Box -import Witherable (wither) - --- | A type of error messages -data SimpleErrorMessage - = InternalCompilerError Text Text - | ModuleNotFound ModuleName - | ErrorParsingFFIModule FilePath (Maybe Bundle.ErrorMessage) - | ErrorParsingCSTModule CST.ParserError - | WarningParsingCSTModule CST.ParserWarning - | MissingFFIModule ModuleName - | UnnecessaryFFIModule ModuleName FilePath - | MissingFFIImplementations ModuleName [Ident] - | UnusedFFIImplementations ModuleName [Ident] - | InvalidFFIIdentifier ModuleName Text - | DeprecatedFFIPrime ModuleName Text - | DeprecatedFFICommonJSModule ModuleName FilePath - | UnsupportedFFICommonJSExports ModuleName [Text] - | UnsupportedFFICommonJSImports ModuleName [Text] - | FileIOError Text Text -- ^ A description of what we were trying to do, and the error which occurred - | InfiniteType SourceType - | InfiniteKind SourceType - | MultipleValueOpFixities (OpName 'ValueOpName) - | MultipleTypeOpFixities (OpName 'TypeOpName) - | OrphanTypeDeclaration Ident - | OrphanKindDeclaration (ProperName 'TypeName) - | OrphanRoleDeclaration (ProperName 'TypeName) - | RedefinedIdent Ident - | OverlappingNamesInLet Ident - | UnknownName (Qualified Name) - | UnknownImport ModuleName Name - | UnknownImportDataConstructor ModuleName (ProperName 'TypeName) (ProperName 'ConstructorName) - | UnknownExport Name - | UnknownExportDataConstructor (ProperName 'TypeName) (ProperName 'ConstructorName) - | ScopeConflict Name [ModuleName] - | ScopeShadowing Name (Maybe ModuleName) [ModuleName] - | DeclConflict Name Name - | ExportConflict (Qualified Name) (Qualified Name) - | DuplicateModule ModuleName - | DuplicateTypeClass (ProperName 'ClassName) SourceSpan - | DuplicateInstance Ident SourceSpan - | DuplicateTypeArgument Text - | InvalidDoBind - | InvalidDoLet - | CycleInDeclaration Ident - | CycleInTypeSynonym (NEL.NonEmpty (ProperName 'TypeName)) - | CycleInTypeClassDeclaration (NEL.NonEmpty (Qualified (ProperName 'ClassName))) - | CycleInKindDeclaration (NEL.NonEmpty (Qualified (ProperName 'TypeName))) - | CycleInModules (NEL.NonEmpty ModuleName) - | NameIsUndefined Ident - | UndefinedTypeVariable (ProperName 'TypeName) - | PartiallyAppliedSynonym (Qualified (ProperName 'TypeName)) - | EscapedSkolem Text (Maybe SourceSpan) SourceType - | TypesDoNotUnify SourceType SourceType - | KindsDoNotUnify SourceType SourceType - | ConstrainedTypeUnified SourceType SourceType - | OverlappingInstances (Qualified (ProperName 'ClassName)) [SourceType] [Qualified (Either SourceType Ident)] - | NoInstanceFound - SourceConstraint -- ^ constraint that could not be solved - [Qualified (Either SourceType Ident)] -- ^ a list of instances that stopped further progress in instance chains due to ambiguity - UnknownsHint -- ^ whether eliminating unknowns with annotations might help or if visible type applications are required - | AmbiguousTypeVariables SourceType [(Text, Int)] - | UnknownClass (Qualified (ProperName 'ClassName)) - | PossiblyInfiniteInstance (Qualified (ProperName 'ClassName)) [SourceType] - | PossiblyInfiniteCoercibleInstance - | CannotDerive (Qualified (ProperName 'ClassName)) [SourceType] - | InvalidDerivedInstance (Qualified (ProperName 'ClassName)) [SourceType] Int - | ExpectedTypeConstructor (Qualified (ProperName 'ClassName)) [SourceType] SourceType - | InvalidNewtypeInstance (Qualified (ProperName 'ClassName)) [SourceType] - | MissingNewtypeSuperclassInstance (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'ClassName)) [SourceType] - | UnverifiableSuperclassInstance (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'ClassName)) [SourceType] - | CannotFindDerivingType (ProperName 'TypeName) - | DuplicateLabel Label (Maybe Expr) - | DuplicateValueDeclaration Ident - | ArgListLengthsDiffer Ident - | OverlappingArgNames (Maybe Ident) - | MissingClassMember (NEL.NonEmpty (Ident, SourceType)) - | ExtraneousClassMember Ident (Qualified (ProperName 'ClassName)) - | ExpectedType SourceType SourceType - -- | constructor name, expected argument count, actual argument count - | IncorrectConstructorArity (Qualified (ProperName 'ConstructorName)) Int Int - | ExprDoesNotHaveType Expr SourceType - | PropertyIsMissing Label - | AdditionalProperty Label - | OrphanInstance Ident (Qualified (ProperName 'ClassName)) (S.Set ModuleName) [SourceType] - | InvalidNewtype (ProperName 'TypeName) - | InvalidInstanceHead SourceType - | TransitiveExportError DeclarationRef [DeclarationRef] - | TransitiveDctorExportError DeclarationRef [ProperName 'ConstructorName] - | HiddenConstructors DeclarationRef (Qualified (ProperName 'ClassName)) - | ShadowedName Ident - | ShadowedTypeVar Text - | UnusedTypeVar Text - | UnusedName Ident - | UnusedDeclaration Ident - | WildcardInferredType SourceType Context - | HoleInferredType Text SourceType Context (Maybe TypeSearch) - | MissingTypeDeclaration Ident SourceType - | MissingKindDeclaration KindSignatureFor (ProperName 'TypeName) SourceType - | OverlappingPattern [[Binder]] Bool - | IncompleteExhaustivityCheck - | ImportHidingModule ModuleName - | UnusedImport ModuleName (Maybe ModuleName) - | UnusedExplicitImport ModuleName [Name] (Maybe ModuleName) [DeclarationRef] - | UnusedDctorImport ModuleName (ProperName 'TypeName) (Maybe ModuleName) [DeclarationRef] - | UnusedDctorExplicitImport ModuleName (ProperName 'TypeName) [ProperName 'ConstructorName] (Maybe ModuleName) [DeclarationRef] - | DuplicateSelectiveImport ModuleName - | DuplicateImport ModuleName ImportDeclarationType (Maybe ModuleName) - | DuplicateImportRef Name - | DuplicateExportRef Name - | IntOutOfRange Integer Text Integer Integer - | ImplicitQualifiedImport ModuleName ModuleName [DeclarationRef] - | ImplicitQualifiedImportReExport ModuleName ModuleName [DeclarationRef] - | ImplicitImport ModuleName [DeclarationRef] - | HidingImport ModuleName [DeclarationRef] - | CaseBinderLengthDiffers Int [Binder] - | IncorrectAnonymousArgument - | InvalidOperatorInBinder (Qualified (OpName 'ValueOpName)) (Qualified Ident) - | CannotGeneralizeRecursiveFunction Ident SourceType - | CannotDeriveNewtypeForData (ProperName 'TypeName) - | ExpectedWildcard (ProperName 'TypeName) - | CannotUseBindWithDo Ident - -- | instance name, type class, expected argument count, actual argument count - | ClassInstanceArityMismatch Ident (Qualified (ProperName 'ClassName)) Int Int - -- | a user-defined warning raised by using the Warn type class - | UserDefinedWarning SourceType - | CannotDefinePrimModules ModuleName - | MixedAssociativityError (NEL.NonEmpty (Qualified (OpName 'AnyOpName), Associativity)) - | NonAssociativeError (NEL.NonEmpty (Qualified (OpName 'AnyOpName))) - | QuantificationCheckFailureInKind Text - | QuantificationCheckFailureInType [Int] SourceType - | VisibleQuantificationCheckFailureInType Text - | UnsupportedTypeInKind SourceType - -- | Declared role was more permissive than inferred. - | RoleMismatch - Text -- ^ Type variable in question - Role -- ^ inferred role - Role -- ^ declared role - | InvalidCoercibleInstanceDeclaration [SourceType] - | UnsupportedRoleDeclaration - | RoleDeclarationArityMismatch (ProperName 'TypeName) Int Int - | DuplicateRoleDeclaration (ProperName 'TypeName) - | CannotDeriveInvalidConstructorArg (Qualified (ProperName 'ClassName)) [Qualified (ProperName 'ClassName)] Bool - | CannotSkipTypeApplication SourceType - | CannotApplyExpressionOfTypeOnType SourceType SourceType - deriving (Show, Generic, NFData) - -data ErrorMessage = ErrorMessage - [ErrorMessageHint] - SimpleErrorMessage - deriving (Show, Generic, NFData) - -newtype ErrorSuggestion = ErrorSuggestion Text - --- | Get the source span for an error -errorSpan :: ErrorMessage -> Maybe (NEL.NonEmpty SourceSpan) -errorSpan = findHint matchPE <> findHint matchRP - where - matchPE (PositionedError sss) = Just sss - matchPE _ = Nothing - matchRP (RelatedPositions sss) = Just sss - matchRP _ = Nothing - --- | Get the module name for an error -errorModule :: ErrorMessage -> Maybe ModuleName -errorModule = findHint matchModule - where - matchModule (ErrorInModule mn) = Just mn - matchModule _ = Nothing - -findHint :: (ErrorMessageHint -> Maybe a) -> ErrorMessage -> Maybe a -findHint f (ErrorMessage hints _) = getLast . foldMap (Last . f) $ hints - --- | Remove the module name and span hints from an error -stripModuleAndSpan :: ErrorMessage -> ErrorMessage -stripModuleAndSpan (ErrorMessage hints e) = ErrorMessage (filter (not . shouldStrip) hints) e - where - shouldStrip (ErrorInModule _) = True - shouldStrip (PositionedError _) = True - shouldStrip _ = False - --- | Get the error code for a particular error type -errorCode :: ErrorMessage -> Text -errorCode em = case unwrapErrorMessage em of - InternalCompilerError{} -> "InternalCompilerError" - ModuleNotFound{} -> "ModuleNotFound" - ErrorParsingFFIModule{} -> "ErrorParsingFFIModule" - ErrorParsingCSTModule{} -> "ErrorParsingModule" - WarningParsingCSTModule{} -> "WarningParsingModule" - MissingFFIModule{} -> "MissingFFIModule" - UnnecessaryFFIModule{} -> "UnnecessaryFFIModule" - MissingFFIImplementations{} -> "MissingFFIImplementations" - UnusedFFIImplementations{} -> "UnusedFFIImplementations" - InvalidFFIIdentifier{} -> "InvalidFFIIdentifier" - DeprecatedFFIPrime{} -> "DeprecatedFFIPrime" - DeprecatedFFICommonJSModule {} -> "DeprecatedFFICommonJSModule" - UnsupportedFFICommonJSExports {} -> "UnsupportedFFICommonJSExports" - UnsupportedFFICommonJSImports {} -> "UnsupportedFFICommonJSImports" - FileIOError{} -> "FileIOError" - InfiniteType{} -> "InfiniteType" - InfiniteKind{} -> "InfiniteKind" - MultipleValueOpFixities{} -> "MultipleValueOpFixities" - MultipleTypeOpFixities{} -> "MultipleTypeOpFixities" - OrphanTypeDeclaration{} -> "OrphanTypeDeclaration" - OrphanKindDeclaration{} -> "OrphanKindDeclaration" - OrphanRoleDeclaration{} -> "OrphanRoleDeclaration" - RedefinedIdent{} -> "RedefinedIdent" - OverlappingNamesInLet{} -> "OverlappingNamesInLet" - UnknownName{} -> "UnknownName" - UnknownImport{} -> "UnknownImport" - UnknownImportDataConstructor{} -> "UnknownImportDataConstructor" - UnknownExport{} -> "UnknownExport" - UnknownExportDataConstructor{} -> "UnknownExportDataConstructor" - ScopeConflict{} -> "ScopeConflict" - ScopeShadowing{} -> "ScopeShadowing" - DeclConflict{} -> "DeclConflict" - ExportConflict{} -> "ExportConflict" - DuplicateModule{} -> "DuplicateModule" - DuplicateTypeClass{} -> "DuplicateTypeClass" - DuplicateInstance{} -> "DuplicateInstance" - DuplicateTypeArgument{} -> "DuplicateTypeArgument" - InvalidDoBind -> "InvalidDoBind" - InvalidDoLet -> "InvalidDoLet" - CycleInDeclaration{} -> "CycleInDeclaration" - CycleInTypeSynonym{} -> "CycleInTypeSynonym" - CycleInTypeClassDeclaration{} -> "CycleInTypeClassDeclaration" - CycleInKindDeclaration{} -> "CycleInKindDeclaration" - CycleInModules{} -> "CycleInModules" - NameIsUndefined{} -> "NameIsUndefined" - UndefinedTypeVariable{} -> "UndefinedTypeVariable" - PartiallyAppliedSynonym{} -> "PartiallyAppliedSynonym" - EscapedSkolem{} -> "EscapedSkolem" - TypesDoNotUnify{} -> "TypesDoNotUnify" - KindsDoNotUnify{} -> "KindsDoNotUnify" - ConstrainedTypeUnified{} -> "ConstrainedTypeUnified" - OverlappingInstances{} -> "OverlappingInstances" - NoInstanceFound{} -> "NoInstanceFound" - AmbiguousTypeVariables{} -> "AmbiguousTypeVariables" - UnknownClass{} -> "UnknownClass" - PossiblyInfiniteInstance{} -> "PossiblyInfiniteInstance" - PossiblyInfiniteCoercibleInstance -> "PossiblyInfiniteCoercibleInstance" - CannotDerive{} -> "CannotDerive" - InvalidNewtypeInstance{} -> "InvalidNewtypeInstance" - MissingNewtypeSuperclassInstance{} -> "MissingNewtypeSuperclassInstance" - UnverifiableSuperclassInstance{} -> "UnverifiableSuperclassInstance" - InvalidDerivedInstance{} -> "InvalidDerivedInstance" - ExpectedTypeConstructor{} -> "ExpectedTypeConstructor" - CannotFindDerivingType{} -> "CannotFindDerivingType" - DuplicateLabel{} -> "DuplicateLabel" - DuplicateValueDeclaration{} -> "DuplicateValueDeclaration" - ArgListLengthsDiffer{} -> "ArgListLengthsDiffer" - OverlappingArgNames{} -> "OverlappingArgNames" - MissingClassMember{} -> "MissingClassMember" - ExtraneousClassMember{} -> "ExtraneousClassMember" - ExpectedType{} -> "ExpectedType" - IncorrectConstructorArity{} -> "IncorrectConstructorArity" - ExprDoesNotHaveType{} -> "ExprDoesNotHaveType" - PropertyIsMissing{} -> "PropertyIsMissing" - AdditionalProperty{} -> "AdditionalProperty" - OrphanInstance{} -> "OrphanInstance" - InvalidNewtype{} -> "InvalidNewtype" - InvalidInstanceHead{} -> "InvalidInstanceHead" - TransitiveExportError{} -> "TransitiveExportError" - TransitiveDctorExportError{} -> "TransitiveDctorExportError" - HiddenConstructors{} -> "HiddenConstructors" - ShadowedName{} -> "ShadowedName" - UnusedName{} -> "UnusedName" - UnusedDeclaration{} -> "UnusedDeclaration" - ShadowedTypeVar{} -> "ShadowedTypeVar" - UnusedTypeVar{} -> "UnusedTypeVar" - WildcardInferredType{} -> "WildcardInferredType" - HoleInferredType{} -> "HoleInferredType" - MissingTypeDeclaration{} -> "MissingTypeDeclaration" - MissingKindDeclaration{} -> "MissingKindDeclaration" - OverlappingPattern{} -> "OverlappingPattern" - IncompleteExhaustivityCheck{} -> "IncompleteExhaustivityCheck" - ImportHidingModule{} -> "ImportHidingModule" - UnusedImport{} -> "UnusedImport" - UnusedExplicitImport{} -> "UnusedExplicitImport" - UnusedDctorImport{} -> "UnusedDctorImport" - UnusedDctorExplicitImport{} -> "UnusedDctorExplicitImport" - DuplicateSelectiveImport{} -> "DuplicateSelectiveImport" - DuplicateImport{} -> "DuplicateImport" - DuplicateImportRef{} -> "DuplicateImportRef" - DuplicateExportRef{} -> "DuplicateExportRef" - IntOutOfRange{} -> "IntOutOfRange" - ImplicitQualifiedImport{} -> "ImplicitQualifiedImport" - ImplicitQualifiedImportReExport{} -> "ImplicitQualifiedImportReExport" - ImplicitImport{} -> "ImplicitImport" - HidingImport{} -> "HidingImport" - CaseBinderLengthDiffers{} -> "CaseBinderLengthDiffers" - IncorrectAnonymousArgument -> "IncorrectAnonymousArgument" - InvalidOperatorInBinder{} -> "InvalidOperatorInBinder" - CannotGeneralizeRecursiveFunction{} -> "CannotGeneralizeRecursiveFunction" - CannotDeriveNewtypeForData{} -> "CannotDeriveNewtypeForData" - ExpectedWildcard{} -> "ExpectedWildcard" - CannotUseBindWithDo{} -> "CannotUseBindWithDo" - ClassInstanceArityMismatch{} -> "ClassInstanceArityMismatch" - UserDefinedWarning{} -> "UserDefinedWarning" - CannotDefinePrimModules{} -> "CannotDefinePrimModules" - MixedAssociativityError{} -> "MixedAssociativityError" - NonAssociativeError{} -> "NonAssociativeError" - QuantificationCheckFailureInKind {} -> "QuantificationCheckFailureInKind" - QuantificationCheckFailureInType {} -> "QuantificationCheckFailureInType" - VisibleQuantificationCheckFailureInType {} -> "VisibleQuantificationCheckFailureInType" - UnsupportedTypeInKind {} -> "UnsupportedTypeInKind" - RoleMismatch {} -> "RoleMismatch" - InvalidCoercibleInstanceDeclaration {} -> "InvalidCoercibleInstanceDeclaration" - UnsupportedRoleDeclaration {} -> "UnsupportedRoleDeclaration" - RoleDeclarationArityMismatch {} -> "RoleDeclarationArityMismatch" - DuplicateRoleDeclaration {} -> "DuplicateRoleDeclaration" - CannotDeriveInvalidConstructorArg{} -> "CannotDeriveInvalidConstructorArg" - CannotSkipTypeApplication{} -> "CannotSkipTypeApplication" - CannotApplyExpressionOfTypeOnType{} -> "CannotApplyExpressionOfTypeOnType" - --- | A stack trace for an error -newtype MultipleErrors = MultipleErrors - { runMultipleErrors :: [ErrorMessage] - } - deriving stock (Show) - deriving newtype (Semigroup, Monoid, NFData) - --- | Check whether a collection of errors is empty or not. -nonEmpty :: MultipleErrors -> Bool -nonEmpty = not . null . runMultipleErrors - --- | Create an error set from a single simple error message -errorMessage :: SimpleErrorMessage -> MultipleErrors -errorMessage err = MultipleErrors [ErrorMessage [] err] - --- | Create an error set from a single simple error message and source annotation -errorMessage' :: SourceSpan -> SimpleErrorMessage -> MultipleErrors -errorMessage' ss err = MultipleErrors [ErrorMessage [positionedError ss] err] - --- | Create an error set from a single simple error message and source annotations -errorMessage'' :: NEL.NonEmpty SourceSpan -> SimpleErrorMessage -> MultipleErrors -errorMessage'' sss err = MultipleErrors [ErrorMessage [PositionedError sss] err] - --- | Create an error from multiple (possibly empty) source spans, reversed sorted. -errorMessage''' :: [SourceSpan] -> SimpleErrorMessage -> MultipleErrors -errorMessage''' sss err = - maybe (errorMessage err) (flip errorMessage'' err) - . NEL.nonEmpty - . sortOn Down - $ filter (/= NullSourceSpan) sss - --- | Create an error set from a single error message -singleError :: ErrorMessage -> MultipleErrors -singleError = MultipleErrors . pure - --- | Lift a function on ErrorMessage to a function on MultipleErrors -onErrorMessages :: (ErrorMessage -> ErrorMessage) -> MultipleErrors -> MultipleErrors -onErrorMessages f = MultipleErrors . map f . runMultipleErrors - --- | Add a hint to an error message -addHint :: ErrorMessageHint -> MultipleErrors -> MultipleErrors -addHint hint = addHints [hint] - --- | Add hints to an error message -addHints :: [ErrorMessageHint] -> MultipleErrors -> MultipleErrors -addHints hints = onErrorMessages $ \(ErrorMessage hints' se) -> ErrorMessage (hints ++ hints') se - --- | A map from rigid type variable name/unknown variable pairs to new variables. -data TypeMap = TypeMap - { umSkolemMap :: M.IntMap (String, Int, Maybe SourceSpan) - -- ^ a map from skolems to their new names, including source and naming info - , umUnknownMap :: M.IntMap Int - -- ^ a map from unification variables to their new names - , umNextIndex :: Int - -- ^ unknowns and skolems share a source of names during renaming, to - -- avoid overlaps in error messages. This is the next label for either case. - } deriving Show - -defaultUnknownMap :: TypeMap -defaultUnknownMap = TypeMap M.empty M.empty 0 - --- | How critical the issue is -data Level = Error | Warning deriving Show - --- | Extract nested error messages from wrapper errors -unwrapErrorMessage :: ErrorMessage -> SimpleErrorMessage -unwrapErrorMessage (ErrorMessage _ se) = se - -replaceUnknowns :: SourceType -> State TypeMap SourceType -replaceUnknowns = everywhereOnTypesTopDownM replaceTypes where - replaceTypes :: SourceType -> State TypeMap SourceType - replaceTypes (TUnknown ann u) = do - m <- get - case M.lookup u (umUnknownMap m) of - Nothing -> do - let u' = umNextIndex m - put $ m { umUnknownMap = M.insert u u' (umUnknownMap m), umNextIndex = u' + 1 } - return (TUnknown ann u') - Just u' -> return (TUnknown ann u') - -- We intentionally remove the kinds from skolems, because they are never - -- presented when pretty-printing. Any unknowns in those kinds shouldn't - -- appear in the list of unknowns unless used somewhere else. - replaceTypes (Skolem ann name _ s sko) = do - m <- get - case M.lookup s (umSkolemMap m) of - Nothing -> do - let s' = umNextIndex m - put $ m { umSkolemMap = M.insert s (T.unpack name, s', Just (fst ann)) (umSkolemMap m), umNextIndex = s' + 1 } - return (Skolem ann name Nothing s' sko) - Just (_, s', _) -> return (Skolem ann name Nothing s' sko) - replaceTypes other = return other - -onTypesInErrorMessage :: (SourceType -> SourceType) -> ErrorMessage -> ErrorMessage -onTypesInErrorMessage f = runIdentity . onTypesInErrorMessageM (Identity . f) - -onTypesInErrorMessageM :: Applicative m => (SourceType -> m SourceType) -> ErrorMessage -> m ErrorMessage -onTypesInErrorMessageM f (ErrorMessage hints simple) = ErrorMessage <$> traverse gHint hints <*> gSimple simple - where - gSimple (InfiniteType t) = InfiniteType <$> f t - gSimple (TypesDoNotUnify t1 t2) = TypesDoNotUnify <$> f t1 <*> f t2 - gSimple (ConstrainedTypeUnified t1 t2) = ConstrainedTypeUnified <$> f t1 <*> f t2 - gSimple (ExprDoesNotHaveType e t) = ExprDoesNotHaveType e <$> f t - gSimple (InvalidInstanceHead t) = InvalidInstanceHead <$> f t - gSimple (NoInstanceFound con ambig unks) = NoInstanceFound <$> overConstraintArgs (traverse f) con <*> pure ambig <*> pure unks - gSimple (AmbiguousTypeVariables t uis) = AmbiguousTypeVariables <$> f t <*> pure uis - gSimple (OverlappingInstances cl ts insts) = OverlappingInstances cl <$> traverse f ts <*> traverse (traverse $ bitraverse f pure) insts - gSimple (PossiblyInfiniteInstance cl ts) = PossiblyInfiniteInstance cl <$> traverse f ts - gSimple (CannotDerive cl ts) = CannotDerive cl <$> traverse f ts - gSimple (InvalidNewtypeInstance cl ts) = InvalidNewtypeInstance cl <$> traverse f ts - gSimple (MissingNewtypeSuperclassInstance cl1 cl2 ts) = MissingNewtypeSuperclassInstance cl1 cl2 <$> traverse f ts - gSimple (UnverifiableSuperclassInstance cl1 cl2 ts) = UnverifiableSuperclassInstance cl1 cl2 <$> traverse f ts - gSimple (InvalidDerivedInstance cl ts n) = InvalidDerivedInstance cl <$> traverse f ts <*> pure n - gSimple (ExpectedTypeConstructor cl ts ty) = ExpectedTypeConstructor cl <$> traverse f ts <*> f ty - gSimple (ExpectedType ty k) = ExpectedType <$> f ty <*> pure k - gSimple (OrphanInstance nm cl noms ts) = OrphanInstance nm cl noms <$> traverse f ts - gSimple (WildcardInferredType ty ctx) = WildcardInferredType <$> f ty <*> traverse (sndM f) ctx - gSimple (HoleInferredType name ty ctx env) = HoleInferredType name <$> f ty <*> traverse (sndM f) ctx <*> traverse (onTypeSearchTypesM f) env - gSimple (MissingTypeDeclaration nm ty) = MissingTypeDeclaration nm <$> f ty - gSimple (MissingKindDeclaration sig nm ty) = MissingKindDeclaration sig nm <$> f ty - gSimple (CannotGeneralizeRecursiveFunction nm ty) = CannotGeneralizeRecursiveFunction nm <$> f ty - gSimple (InvalidCoercibleInstanceDeclaration tys) = InvalidCoercibleInstanceDeclaration <$> traverse f tys - gSimple other = pure other - - gHint (ErrorInSubsumption t1 t2) = ErrorInSubsumption <$> f t1 <*> f t2 - gHint (ErrorUnifyingTypes t1 t2) = ErrorUnifyingTypes <$> f t1 <*> f t2 - gHint (ErrorCheckingType e t) = ErrorCheckingType e <$> f t - gHint (ErrorCheckingKind t k) = ErrorCheckingKind <$> f t <*> f k - gHint (ErrorInferringKind t) = ErrorInferringKind <$> f t - gHint (ErrorInApplication e1 t1 e2) = ErrorInApplication e1 <$> f t1 <*> pure e2 - gHint (ErrorInInstance cl ts) = ErrorInInstance cl <$> traverse f ts - gHint (ErrorSolvingConstraint con) = ErrorSolvingConstraint <$> overConstraintArgs (traverse f) con - gHint other = pure other - -errorDocUri :: ErrorMessage -> Text -errorDocUri e = "https://github.com/purescript/documentation/blob/master/errors/" <> errorCode e <> ".md" - --- TODO Other possible suggestions: --- WildcardInferredType - source span not small enough --- DuplicateSelectiveImport - would require 2 ranges to remove and 1 insert -errorSuggestion :: SimpleErrorMessage -> Maybe ErrorSuggestion -errorSuggestion err = - case err of - UnusedImport{} -> emptySuggestion - DuplicateImport{} -> emptySuggestion - UnusedExplicitImport mn _ qual refs -> suggest $ importSuggestion mn refs qual - UnusedDctorImport mn _ qual refs -> suggest $ importSuggestion mn refs qual - UnusedDctorExplicitImport mn _ _ qual refs -> suggest $ importSuggestion mn refs qual - ImplicitImport mn refs -> suggest $ importSuggestion mn refs Nothing - ImplicitQualifiedImport mn asModule refs -> suggest $ importSuggestion mn refs (Just asModule) - ImplicitQualifiedImportReExport mn asModule refs -> suggest $ importSuggestion mn refs (Just asModule) - HidingImport mn refs -> suggest $ importSuggestion mn refs Nothing - MissingTypeDeclaration ident ty -> suggest $ showIdent ident <> " :: " <> T.pack (prettyPrintSuggestedTypeSimplified ty) <> "\n" - MissingKindDeclaration sig name ty -> suggest $ prettyPrintKindSignatureFor sig <> " " <> runProperName name <> " :: " <> T.pack (prettyPrintSuggestedTypeSimplified ty) <> "\n" - WildcardInferredType ty _ -> suggest $ T.pack (prettyPrintSuggestedTypeSimplified ty) - WarningParsingCSTModule pe -> do - let toks = CST.errToks pe - case CST.errType pe of - CST.WarnDeprecatedRowSyntax -> do - let kind = CST.printTokens $ drop 1 toks - sugg | " " `T.isPrefixOf` kind = "Row" <> kind - | otherwise = "Row " <> kind - suggest sugg - CST.WarnDeprecatedForeignKindSyntax -> suggest $ "data " <> CST.printTokens (drop 3 toks) - CST.WarnDeprecatedKindImportSyntax -> suggest $ CST.printTokens $ drop 1 toks - CST.WarnDeprecatedKindExportSyntax -> suggest $ CST.printTokens $ drop 1 toks - CST.WarnDeprecatedCaseOfOffsideSyntax -> Nothing - _ -> Nothing - where - emptySuggestion = Just $ ErrorSuggestion "" - suggest = Just . ErrorSuggestion - - importSuggestion :: ModuleName -> [ DeclarationRef ] -> Maybe ModuleName -> Text - importSuggestion mn refs qual = - "import " <> runModuleName mn <> " (" <> T.intercalate ", " (mapMaybe prettyPrintRef refs) <> ")" <> qstr qual - - qstr :: Maybe ModuleName -> Text - qstr (Just mn) = " as " <> runModuleName mn - qstr Nothing = "" - -suggestionSpan :: ErrorMessage -> Maybe SourceSpan -suggestionSpan e = - -- The `NEL.head` is a bit arbitrary here, but I don't think we'll - -- have errors-with-suggestions that also have multiple source - -- spans. -garyb - getSpan (unwrapErrorMessage e) . NEL.head <$> errorSpan e - where - startOnly SourceSpan{spanName, spanStart} = SourceSpan {spanName, spanStart, spanEnd = spanStart} - - getSpan simple ss = - case simple of - MissingTypeDeclaration{} -> startOnly ss - MissingKindDeclaration{} -> startOnly ss - _ -> ss - -showSuggestion :: SimpleErrorMessage -> Text -showSuggestion suggestion = case errorSuggestion suggestion of - Just (ErrorSuggestion x) -> x - _ -> "" - -ansiColor :: (ANSI.ColorIntensity, ANSI.Color) -> String -ansiColor (intensity, color) = - ANSI.setSGRCode [ANSI.SetColor ANSI.Foreground intensity color] - -ansiColorReset :: String -ansiColorReset = - ANSI.setSGRCode [ANSI.Reset] - -colorCode :: Maybe (ANSI.ColorIntensity, ANSI.Color) -> Text -> Text -colorCode codeColor code = case codeColor of - Nothing -> code - Just cc -> T.pack (ansiColor cc) <> code <> T.pack ansiColorReset - -colorCodeBox :: Maybe (ANSI.ColorIntensity, ANSI.Color) -> Box.Box -> Box.Box -colorCodeBox codeColor b = case codeColor of - Nothing -> b - Just cc - | Box.rows b == 1 -> - Box.text (ansiColor cc) Box.<> b `endWith` Box.text ansiColorReset - - | otherwise -> Box.hcat Box.left -- making two boxes, one for each side of the box so that it will set each row it's own color and will reset it afterwards - [ Box.vcat Box.top $ replicate (Box.rows b) $ Box.text $ ansiColor cc - , b - , Box.vcat Box.top $ replicate (Box.rows b) $ Box.text ansiColorReset - ] - -commasAndConjunction :: Text -> [Text] -> Text -commasAndConjunction conj = \case - [x] -> x - [x, y] -> x <> " " <> conj <> " " <> y - (unsnoc -> Just (rest, z)) -> foldMap (<> ", ") rest <> conj <> " " <> z - _ -> "" - --- | Default color intensity and color for code -defaultCodeColor :: (ANSI.ColorIntensity, ANSI.Color) -defaultCodeColor = (ANSI.Dull, ANSI.Yellow) - --- | `prettyPrintSingleError` Options -data PPEOptions = PPEOptions - { ppeCodeColor :: Maybe (ANSI.ColorIntensity, ANSI.Color) -- ^ Color code with this color... or not - , ppeFull :: Bool -- ^ Should write a full error message? - , ppeLevel :: Level -- ^ Should this report an error or a warning? - , ppeShowDocs :: Bool -- ^ Should show a link to error message's doc page? - , ppeRelativeDirectory :: FilePath -- ^ FilePath to which the errors are relative - , ppeFileContents :: [(FilePath, Text)] -- ^ Unparsed contents of source files - } - --- | Default options for PPEOptions -defaultPPEOptions :: PPEOptions -defaultPPEOptions = PPEOptions - { ppeCodeColor = Just defaultCodeColor - , ppeFull = False - , ppeLevel = Error - , ppeShowDocs = True - , ppeRelativeDirectory = mempty - , ppeFileContents = [] - } - --- | Pretty print a single error, simplifying if necessary -prettyPrintSingleError :: PPEOptions -> ErrorMessage -> Box.Box -prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath fileContents) e = flip evalState defaultUnknownMap $ do - em <- onTypesInErrorMessageM replaceUnknowns (if full then e else simplifyErrorMessage e) - um <- get - return (prettyPrintErrorMessage um em) - where - (markCode, markCodeBox) = (colorCode &&& colorCodeBox) codeColor - - -- Pretty print an ErrorMessage - prettyPrintErrorMessage :: TypeMap -> ErrorMessage -> Box.Box - prettyPrintErrorMessage typeMap (ErrorMessage hints simple) = - paras $ - [ foldr renderHint (indent (renderSimpleErrorMessage simple)) hints - ] ++ - maybe [] (return . Box.moveDown 1) typeInformation ++ - [ Box.moveDown 1 $ paras - [ line $ "See " <> errorDocUri e <> " for more information, " - , line $ "or to contribute content related to this " <> levelText <> "." - ] - | showDocs - ] - where - typeInformation :: Maybe Box.Box - typeInformation | not (null types) = Just $ Box.hsep 1 Box.left [ line "where", paras types ] - | otherwise = Nothing - where - types :: [Box.Box] - types = map skolemInfo (M.elems (umSkolemMap typeMap)) ++ - map unknownInfo (M.elems (umUnknownMap typeMap)) - - skolemInfo :: (String, Int, Maybe SourceSpan) -> Box.Box - skolemInfo (name, s, ss) = - paras $ - line (markCode (T.pack (name <> show s)) <> " is a rigid type variable") - : foldMap (return . line . (" bound at " <>) . displayStartEndPos) ss - - unknownInfo :: Int -> Box.Box - unknownInfo u = line $ markCode ("t" <> T.pack (show u)) <> " is an unknown type" - - renderSimpleErrorMessage :: SimpleErrorMessage -> Box.Box - renderSimpleErrorMessage (InternalCompilerError ctx err) = - paras [ line "Internal compiler error:" - , indent $ line err - , line ctx - , line "Please report this at https://github.com/purescript/purescript/issues" - ] - renderSimpleErrorMessage (ModuleNotFound mn) = - paras [ line $ "Module " <> markCode (runModuleName mn) <> " was not found." - , line $ - if isBuiltinModuleName mn - then - "Module names in the Prim namespace are reserved for built-in modules, but this version of the compiler does not provide module " <> markCode (runModuleName mn) <> ". You may be able to fix this by updating your compiler to a newer version." - else - "Make sure the source file exists, and that it has been provided as an input to the compiler." - ] - renderSimpleErrorMessage (FileIOError doWhat err) = - paras [ line $ "I/O error while trying to " <> doWhat - , indent . line $ err - ] - renderSimpleErrorMessage (ErrorParsingFFIModule path extra) = - paras $ [ line "Unable to parse foreign module:" - , indent . lineS $ path - ] ++ - map (indent . lineS) (concatMap Bundle.printErrorMessage (maybeToList extra)) - renderSimpleErrorMessage (ErrorParsingCSTModule err) = - paras [ line "Unable to parse module: " - , line $ T.pack $ CST.prettyPrintErrorMessage err - ] - renderSimpleErrorMessage (WarningParsingCSTModule err) = - paras [ line $ T.pack $ CST.prettyPrintWarningMessage err - ] - renderSimpleErrorMessage (MissingFFIModule mn) = - line $ "The foreign module implementation for module " <> markCode (runModuleName mn) <> " is missing." - renderSimpleErrorMessage (UnnecessaryFFIModule mn path) = - paras [ line $ "An unnecessary foreign module implementation was provided for module " <> markCode (runModuleName mn) <> ": " - , indent . lineS $ path - , line $ "Module " <> markCode (runModuleName mn) <> " does not contain any foreign import declarations, so a foreign module is not necessary." - ] - renderSimpleErrorMessage (MissingFFIImplementations mn idents) = - paras [ line $ "The following values are not defined in the foreign module for module " <> markCode (runModuleName mn) <> ": " - , indent . paras $ map (line . runIdent) idents - ] - renderSimpleErrorMessage (UnusedFFIImplementations mn idents) = - paras [ line $ "The following definitions in the foreign module for module " <> markCode (runModuleName mn) <> " are unused: " - , indent . paras $ map (line . runIdent) idents - ] - renderSimpleErrorMessage (InvalidFFIIdentifier mn ident) = - paras [ line $ "In the FFI module for " <> markCode (runModuleName mn) <> ":" - , indent . paras $ - [ line $ "The identifier " <> markCode ident <> " is not valid in PureScript." - , line "Note that exported identifiers in FFI modules must be valid PureScript identifiers." - ] - ] - renderSimpleErrorMessage (DeprecatedFFIPrime mn ident) = - paras [ line $ "In the FFI module for " <> markCode (runModuleName mn) <> ":" - , indent . paras $ - [ line $ "The identifier " <> markCode ident <> " contains a prime (" <> markCode "'" <> ")." - , line "Primes are not allowed in identifiers exported from FFI modules." - ] - ] - renderSimpleErrorMessage (DeprecatedFFICommonJSModule mn path) = - paras [ line $ "A CommonJS foreign module implementation was provided for module " <> markCode (runModuleName mn) <> ": " - , indent . lineS $ path - , line "CommonJS foreign modules are no longer supported. Use native JavaScript/ECMAScript module syntax instead." - ] - renderSimpleErrorMessage (UnsupportedFFICommonJSExports mn idents) = - paras [ line $ "The following CommonJS exports are not supported in the ES foreign module for module " <> markCode (runModuleName mn) <> ": " - , indent . paras $ map line idents - ] - renderSimpleErrorMessage (UnsupportedFFICommonJSImports mn mids) = - paras [ line $ "The following CommonJS imports are not supported in the ES foreign module for module " <> markCode (runModuleName mn) <> ": " - , indent . paras $ map line mids - ] - renderSimpleErrorMessage InvalidDoBind = - line "The last statement in a 'do' block must be an expression, but this block ends with a binder." - renderSimpleErrorMessage InvalidDoLet = - line "The last statement in a 'do' block must be an expression, but this block ends with a let binding." - renderSimpleErrorMessage (OverlappingNamesInLet name) = - line $ "The name " <> markCode (showIdent name) <> " was defined multiple times in a binding group" - renderSimpleErrorMessage (InfiniteType ty) = - paras [ line "An infinite type was inferred for an expression: " - , markCodeBox $ indent $ prettyType ty - ] - renderSimpleErrorMessage (InfiniteKind ki) = - paras [ line "An infinite kind was inferred for a type: " - , markCodeBox $ indent $ prettyType ki - ] - renderSimpleErrorMessage (MultipleValueOpFixities op) = - line $ "There are multiple fixity/precedence declarations for operator " <> markCode (showOp op) - renderSimpleErrorMessage (MultipleTypeOpFixities op) = - line $ "There are multiple fixity/precedence declarations for type operator " <> markCode (showOp op) - renderSimpleErrorMessage (OrphanTypeDeclaration nm) = - line $ "The type declaration for " <> markCode (showIdent nm) <> " should be followed by its definition." - renderSimpleErrorMessage (OrphanKindDeclaration nm) = - line $ "The kind declaration for " <> markCode (runProperName nm) <> " should be followed by its definition." - renderSimpleErrorMessage (OrphanRoleDeclaration nm) = - line $ "The role declaration for " <> markCode (runProperName nm) <> " should follow its definition." - renderSimpleErrorMessage (RedefinedIdent name) = - line $ "The value " <> markCode (showIdent name) <> " has been defined multiple times" - renderSimpleErrorMessage (UnknownName name@(Qualified (BySourcePos _) (IdentName (Ident i)))) | i `elem` [ C.S_bind, C.S_discard ] = - line $ "Unknown " <> printName name <> ". You're probably using do-notation, which the compiler replaces with calls to the " <> markCode "bind" <> " and " <> markCode "discard" <> " functions. Please import " <> markCode i <> " from module " <> markCode "Prelude" - renderSimpleErrorMessage (UnknownName name@(Qualified (BySourcePos _) (IdentName (Ident C.S_negate)))) = - line $ "Unknown " <> printName name <> ". You're probably using numeric negation (the unary " <> markCode "-" <> " operator), which the compiler replaces with calls to the " <> markCode C.S_negate <> " function. Please import " <> markCode C.S_negate <> " from module " <> markCode "Prelude" - renderSimpleErrorMessage (UnknownName name) = - line $ "Unknown " <> printName name - renderSimpleErrorMessage (UnknownImport mn name) = - paras [ line $ "Cannot import " <> printName (Qualified ByNullSourcePos name) <> " from module " <> markCode (runModuleName mn) - , line "It either does not exist or the module does not export it." - ] - renderSimpleErrorMessage (UnknownImportDataConstructor mn tcon dcon) = - line $ "Module " <> runModuleName mn <> " does not export data constructor " <> markCode (runProperName dcon) <> " for type " <> markCode (runProperName tcon) - renderSimpleErrorMessage (UnknownExport name) = - line $ "Cannot export unknown " <> printName (Qualified ByNullSourcePos name) - renderSimpleErrorMessage (UnknownExportDataConstructor tcon dcon) = - line $ "Cannot export data constructor " <> markCode (runProperName dcon) <> " for type " <> markCode (runProperName tcon) <> ", as it has not been declared." - renderSimpleErrorMessage (ScopeConflict nm ms) = - paras [ line $ "Conflicting definitions are in scope for " <> printName (Qualified ByNullSourcePos nm) <> " from the following modules:" - , indent $ paras $ map (line . markCode . runModuleName) ms - ] - renderSimpleErrorMessage (ScopeShadowing nm exmn ms) = - paras [ line $ "Shadowed definitions are in scope for " <> printName (Qualified ByNullSourcePos nm) <> " from the following open imports:" - , indent $ paras $ map (line . markCode . ("import " <>) . runModuleName) ms - , line $ "These will be ignored and the " <> case exmn of - Just exmn' -> "declaration from " <> markCode (runModuleName exmn') <> " will be used." - Nothing -> "local declaration will be used." - ] - renderSimpleErrorMessage (DeclConflict new existing) = - line $ "Declaration for " <> printName (Qualified ByNullSourcePos new) <> " conflicts with an existing " <> nameType existing <> " of the same name." - renderSimpleErrorMessage (ExportConflict new existing) = - line $ "Export for " <> printName new <> " conflicts with " <> printName existing - renderSimpleErrorMessage (DuplicateModule mn) = - line $ "Module " <> markCode (runModuleName mn) <> " has been defined multiple times" - renderSimpleErrorMessage (DuplicateTypeClass pn ss) = - paras [ line ("Type class " <> markCode (runProperName pn) <> " has been defined multiple times:") - , indent $ line $ displaySourceSpan relPath ss - ] - renderSimpleErrorMessage (DuplicateInstance pn ss) = - paras [ line ("Instance " <> markCode (showIdent pn) <> " has been defined multiple times:") - , indent $ line $ displaySourceSpan relPath ss - ] - renderSimpleErrorMessage (CycleInDeclaration nm) = - line $ "The value of " <> markCode (showIdent nm) <> " is undefined here, so this reference is not allowed." - renderSimpleErrorMessage (CycleInModules mns) = - case mns of - mn :| [] -> - line $ "Module " <> markCode (runModuleName mn) <> " imports itself." - _ -> - paras [ line "There is a cycle in module dependencies in these modules: " - , indent $ paras (line . markCode . runModuleName <$> NEL.toList mns) - ] - renderSimpleErrorMessage (CycleInTypeSynonym names) = - paras $ cycleError <> - [ line "Cycles are disallowed because they can lead to loops in the type checker." - , line "Consider using a 'newtype' instead." - ] - where - cycleError = case names of - pn :| [] -> pure . line $ "A cycle appears in the definition of type synonym " <> markCode (runProperName pn) - _ -> [ line " A cycle appears in a set of type synonym definitions:" - , indent $ line $ "{" <> T.intercalate ", " (markCode . runProperName <$> NEL.toList names) <> "}" - ] - renderSimpleErrorMessage (CycleInTypeClassDeclaration (name :| [])) = - paras [ line $ "A type class '" <> markCode (runProperName (disqualify name)) <> "' may not have itself as a superclass." ] - renderSimpleErrorMessage (CycleInTypeClassDeclaration names) = - paras [ line "A cycle appears in a set of type class definitions:" - , indent $ line $ "{" <> T.intercalate ", " (markCode . runProperName . disqualify <$> NEL.toList names) <> "}" - , line "Cycles are disallowed because they can lead to loops in the type checker." - ] - renderSimpleErrorMessage (CycleInKindDeclaration (name :| [])) = - paras [ line $ "A kind declaration '" <> markCode (runProperName (disqualify name)) <> "' may not refer to itself in its own signature." ] - renderSimpleErrorMessage (CycleInKindDeclaration names) = - paras [ line "A cycle appears in a set of kind declarations:" - , indent $ line $ "{" <> T.intercalate ", " (markCode . runProperName . disqualify <$> NEL.toList names) <> "}" - , line "Kind declarations may not refer to themselves in their own signatures." - ] - renderSimpleErrorMessage (NameIsUndefined ident) = - line $ "Value " <> markCode (showIdent ident) <> " is undefined." - renderSimpleErrorMessage (UndefinedTypeVariable name) = - line $ "Type variable " <> markCode (runProperName name) <> " is undefined." - renderSimpleErrorMessage (PartiallyAppliedSynonym name) = - paras [ line $ "Type synonym " <> markCode (showQualified runProperName name) <> " is partially applied." - , line "Type synonyms must be applied to all of their type arguments." - ] - renderSimpleErrorMessage (EscapedSkolem name Nothing ty) = - paras [ line $ "The type variable " <> markCode name <> " has escaped its scope, appearing in the type" - , markCodeBox $ indent $ prettyType ty - ] - renderSimpleErrorMessage (EscapedSkolem name (Just srcSpan) ty) = - paras [ line $ "The type variable " <> markCode name <> ", bound at" - , indent $ line $ displaySourceSpan relPath srcSpan - , line "has escaped its scope, appearing in the type" - , markCodeBox $ indent $ prettyType ty - ] - renderSimpleErrorMessage (TypesDoNotUnify u1 u2) - = let (row1Box, row2Box) = printRows u1 u2 - - in paras [ line "Could not match type" - , row1Box - , line "with type" - , row2Box - ] - - renderSimpleErrorMessage (KindsDoNotUnify k1 k2) = - paras [ line "Could not match kind" - , markCodeBox $ indent $ prettyType k1 - , line "with kind" - , markCodeBox $ indent $ prettyType k2 - ] - renderSimpleErrorMessage (ConstrainedTypeUnified t1 t2) = - paras [ line "Could not match constrained type" - , markCodeBox $ indent $ prettyType t1 - , line "with type" - , markCodeBox $ indent $ prettyType t2 - ] - renderSimpleErrorMessage (OverlappingInstances _ _ []) = internalError "OverlappingInstances: empty instance list" - renderSimpleErrorMessage (OverlappingInstances nm ts ds) = - paras [ line "Overlapping type class instances found for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line "The following instances were found:" - , indent $ paras (map prettyInstanceName ds) - ] - renderSimpleErrorMessage (UnknownClass nm) = - paras [ line "No type class instance was found for class" - , markCodeBox $ indent $ line (showQualified runProperName nm) - , line "because the class was not in scope. Perhaps it was not exported." - ] - renderSimpleErrorMessage (NoInstanceFound (Constraint _ C.Fail _ [ ty ] _) _ _) | Just box <- toTypelevelString ty = - paras [ line "Custom error:" - , indent box - ] - renderSimpleErrorMessage (NoInstanceFound (Constraint _ C.Partial - _ - _ - (Just (PartialConstraintData bs b))) _ _) = - paras [ line "A case expression could not be determined to cover all inputs." - , line "The following additional cases are required to cover all inputs:" - , indent $ paras $ - Box.hsep 1 Box.left - (map (paras . map (line . markCode)) (transpose bs)) - : [line "..." | not b] - , line "Alternatively, add a Partial constraint to the type of the enclosing value." - ] - renderSimpleErrorMessage (NoInstanceFound (Constraint _ C.Discard _ [ty] _) _ _) = - paras [ line "A result of type" - , markCodeBox $ indent $ prettyType ty - , line "was implicitly discarded in a do notation block." - , line ("You can use " <> markCode "_ <- ..." <> " to explicitly discard the result.") - ] - renderSimpleErrorMessage (NoInstanceFound (Constraint _ nm _ ts _) ambiguous unks) = - paras $ - [ line "No type class instance was found for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , paras $ let useMessage msg = - [ line msg - , indent $ paras (map prettyInstanceName ambiguous) - ] - in case ambiguous of - [] -> [] - [_] -> useMessage "The following instance partially overlaps the above constraint, which means the rest of its instance chain will not be considered:" - _ -> useMessage "The following instances partially overlap the above constraint, which means the rest of their instance chains will not be considered:" - ] <> case unks of - NoUnknowns -> - [] - Unknowns -> - [ line "The instance head contains unknown type variables. Consider adding a type annotation." ] - UnknownsWithVtaRequiringArgs tyClassMembersRequiringVtas -> - let - renderSingleTyClassMember (tyClassMember, argsRequiringVtas) = - Box.moveRight 2 $ paras $ - [ line $ markCode (showQualified showIdent tyClassMember) ] - <> case argsRequiringVtas of - [required] -> - [ Box.moveRight 2 $ line $ T.intercalate ", " required ] - options -> - [ Box.moveRight 2 $ line "One of the following sets of type variables:" - , Box.moveRight 2 $ paras $ - map (\set -> Box.moveRight 2 $ line $ T.intercalate ", " set) options - ] - in - [ paras - [ line "The instance head contains unknown type variables." - , Box.moveDown 1 $ paras $ - [ line $ "Note: The following type class members found in the expression require visible type applications to be unambiguous (e.g. " <> markCode "tyClassMember @Int" <> ")."] - <> map renderSingleTyClassMember (NEL.toList tyClassMembersRequiringVtas) - ] - ] - renderSimpleErrorMessage (AmbiguousTypeVariables t uis) = - paras [ line "The inferred type" - , markCodeBox $ indent $ prettyType t - , line "has type variables which are not determined by those mentioned in the body of the type:" - , indent $ Box.hsep 1 Box.left - [ Box.vcat Box.left - [ line $ markCode (u <> T.pack (show i)) <> " could not be determined" - | (u, i) <- uis ] - ] - , line "Consider adding a type annotation." - ] - renderSimpleErrorMessage (PossiblyInfiniteInstance nm ts) = - paras [ line "Type class instance for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line "is possibly infinite." - ] - renderSimpleErrorMessage PossiblyInfiniteCoercibleInstance = - line $ "A " <> markCode "Coercible" <> " instance is possibly infinite." - renderSimpleErrorMessage (CannotDerive nm ts) = - paras [ line "Cannot derive a type class instance for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line "since instances of this type class are not derivable." - ] - renderSimpleErrorMessage (InvalidNewtypeInstance nm ts) = - paras [ line "Cannot derive newtype instance for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line "Make sure this is a newtype." - ] - renderSimpleErrorMessage (MissingNewtypeSuperclassInstance su cl ts) = - paras [ line "The derived newtype instance for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName cl) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line $ "does not include a derived superclass instance for " <> markCode (showQualified runProperName su) <> "." - ] - renderSimpleErrorMessage (UnverifiableSuperclassInstance su cl ts) = - paras [ line "The derived newtype instance for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName cl) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line $ "implies an superclass instance for " <> markCode (showQualified runProperName su) <> " which could not be verified." - ] - renderSimpleErrorMessage (InvalidDerivedInstance nm ts argCount) = - paras [ line "Cannot derive the type class instance" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , line $ fold - [ "because the " - , markCode (showQualified runProperName nm) - , " type class has " - , T.pack (show argCount) - , " type " - , if argCount == 1 then "argument" else "arguments" - , ", but the declaration specifies " <> T.pack (show (length ts)) <> "." - ] - ] - renderSimpleErrorMessage (ExpectedTypeConstructor nm ts ty) = - paras [ line "Cannot derive the type class instance" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , "because the type" - , markCodeBox $ indent $ prettyType ty - , line "is not of the required form T a_1 ... a_n, where T is a type constructor defined in the same module." - ] - renderSimpleErrorMessage (CannotFindDerivingType nm) = - line $ "Cannot derive a type class instance, because the type declaration for " <> markCode (runProperName nm) <> " could not be found." - renderSimpleErrorMessage (DuplicateLabel l expr) = - paras $ [ line $ "Label " <> markCode (prettyPrintLabel l) <> " appears more than once in a row type." ] - <> foldMap (\expr' -> [ line "Relevant expression: " - , markCodeBox $ indent $ prettyPrintValue prettyDepth expr' - ]) expr - renderSimpleErrorMessage (DuplicateTypeArgument name) = - line $ "Type argument " <> markCode name <> " appears more than once." - renderSimpleErrorMessage (DuplicateValueDeclaration nm) = - line $ "Multiple value declarations exist for " <> markCode (showIdent nm) <> "." - renderSimpleErrorMessage (ArgListLengthsDiffer ident) = - line $ "Argument list lengths differ in declaration " <> markCode (showIdent ident) - renderSimpleErrorMessage (OverlappingArgNames ident) = - line $ "Overlapping names in function/binder" <> foldMap ((" in declaration " <>) . showIdent) ident - renderSimpleErrorMessage (MissingClassMember identsAndTypes) = - paras [ line "The following type class members have not been implemented:" - , Box.vcat Box.left - [ markCodeBox $ Box.text (T.unpack (showIdent ident)) Box.<> " :: " Box.<> prettyType ty - | (ident, ty) <- NEL.toList identsAndTypes ] - ] - renderSimpleErrorMessage (ExtraneousClassMember ident className) = - line $ "" <> markCode (showIdent ident) <> " is not a member of type class " <> markCode (showQualified runProperName className) - renderSimpleErrorMessage (ExpectedType ty kind) = - paras [ line $ "In a type-annotated expression " <> markCode "x :: t" <> ", the type " <> markCode "t" <> " must have kind " <> markCode (runProperName . disqualify $ C.Type) <> "." - , line "The error arises from the type" - , markCodeBox $ indent $ prettyType ty - , line "having the kind" - , markCodeBox $ indent $ prettyType kind - , line "instead." - ] - renderSimpleErrorMessage (IncorrectConstructorArity nm expected actual) = - paras [ line $ "Data constructor " <> markCode (showQualified runProperName nm) <> " was given " <> T.pack (show actual) <> " arguments in a case expression, but expected " <> T.pack (show expected) <> " arguments." - , line $ "This problem can be fixed by giving " <> markCode (showQualified runProperName nm) <> " " <> T.pack (show expected) <> " arguments." - ] - renderSimpleErrorMessage (ExprDoesNotHaveType expr ty) = - paras [ line "Expression" - , markCodeBox $ indent $ prettyPrintValue prettyDepth expr - , line "does not have type" - , markCodeBox $ indent $ prettyType ty - ] - renderSimpleErrorMessage (PropertyIsMissing prop) = - line $ "Type of expression lacks required label " <> markCode (prettyPrintLabel prop) <> "." - renderSimpleErrorMessage (AdditionalProperty prop) = - line $ "Type of expression contains additional label " <> markCode (prettyPrintLabel prop) <> "." - renderSimpleErrorMessage (OrphanInstance nm cnm nonOrphanModules ts) = - paras [ line $ "Orphan instance" <> prettyPrintPlainIdent nm <> " found for " - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName cnm) - , Box.vcat Box.left (map prettyTypeAtom ts) - ] - , Box.vcat Box.left $ case modulesToList of - [] -> [ line "There is nowhere this instance can be placed without being an orphan." - , line "A newtype wrapper can be used to avoid this problem." - ] - _ -> [ Box.text $ "This problem can be resolved by declaring the instance in " - <> T.unpack formattedModules - <> ", or by defining the instance on a newtype wrapper." - ] - ] - where - modulesToList = S.toList $ S.delete (moduleNameFromString "Prim") nonOrphanModules - formattedModules = T.intercalate " or " (markCode . runModuleName <$> modulesToList) - renderSimpleErrorMessage (InvalidNewtype name) = - paras [ line $ "Newtype " <> markCode (runProperName name) <> " is invalid." - , line "Newtypes must define a single constructor with a single argument." - ] - renderSimpleErrorMessage (InvalidInstanceHead ty) = - paras [ line "Type class instance head is invalid due to use of type" - , markCodeBox $ indent $ prettyType ty - , line "All types appearing in instance declarations must be of the form T a_1 .. a_n, where each type a_i is of the same form, unless the type is fully determined by other type class arguments via functional dependencies." - ] - renderSimpleErrorMessage (TransitiveExportError x ys) = - paras [ line $ "An export for " <> markCode (prettyPrintExport x) <> " requires the following to also be exported: " - , indent $ paras $ map (line . markCode . prettyPrintExport) ys - ] - renderSimpleErrorMessage (TransitiveDctorExportError x ctors) = - paras [ line $ "An export for " <> markCode (prettyPrintExport x) <> " requires the following data constructor" <> (if length ctors == 1 then "" else "s") <> " to also be exported: " - , indent $ paras $ map (line . markCode . runProperName) ctors - ] - renderSimpleErrorMessage (HiddenConstructors x className) = - paras [ line $ "An export for " <> markCode (prettyPrintExport x) <> " hides data constructors but the type declares an instance of " <> markCode (showQualified runProperName className) <> "." - , line "Such instance allows to match and construct values of this type, effectively making the constructors public." - ] - renderSimpleErrorMessage (ShadowedName nm) = - line $ "Name " <> markCode (showIdent nm) <> " was shadowed." - renderSimpleErrorMessage (ShadowedTypeVar tv) = - line $ "Type variable " <> markCode tv <> " was shadowed." - renderSimpleErrorMessage (UnusedName nm) = - line $ "Name " <> markCode (showIdent nm) <> " was introduced but not used." - renderSimpleErrorMessage (UnusedDeclaration nm) = - line $ "Declaration " <> markCode (showIdent nm) <> " was not used, and is not exported." - renderSimpleErrorMessage (UnusedTypeVar tv) = - line $ "Type variable " <> markCode tv <> " is ambiguous, since it is unused in the polymorphic type which introduces it." - renderSimpleErrorMessage (ImportHidingModule name) = - paras [ line "hiding imports cannot be used to hide modules." - , line $ "An attempt was made to hide the import of " <> markCode (runModuleName name) - ] - renderSimpleErrorMessage (WildcardInferredType ty ctx) = - paras $ [ line "Wildcard type definition has the inferred type " - , markCodeBox $ indent $ prettyType ty - ] <> renderContext ctx - renderSimpleErrorMessage (HoleInferredType name ty ctx ts) = - let - maxTSResults = 15 - tsResult = case ts of - Just TSAfter{tsAfterIdentifiers=idents} | not (null idents) -> - let - formatTS (names, types) = - let - idBoxes = Box.text . T.unpack . showQualified id <$> names - tyBoxes = (\t -> BoxHelpers.indented - (Box.text ":: " Box.<> prettyType t)) <$> types - longestId = maximum (map Box.cols idBoxes) - in - Box.vcat Box.top $ - zipWith (Box.<>) - (Box.alignHoriz Box.left longestId <$> idBoxes) - tyBoxes - in [ line "You could substitute the hole with one of these values:" - , markCodeBox (indent (formatTS (unzip (take maxTSResults idents)))) - ] - _ -> [] - in - paras $ [ line $ "Hole '" <> markCode name <> "' has the inferred type " - , markCodeBox (indent (prettyTypeWithDepth maxBound ty)) - ] ++ tsResult ++ renderContext ctx - renderSimpleErrorMessage (MissingTypeDeclaration ident ty) = - paras [ line $ "No type declaration was provided for the top-level declaration of " <> markCode (showIdent ident) <> "." - , line "It is good practice to provide type declarations as a form of documentation." - , line $ "The inferred type of " <> markCode (showIdent ident) <> " was:" - , markCodeBox $ indent $ prettyTypeWithDepth maxBound ty - ] - renderSimpleErrorMessage (MissingKindDeclaration sig name ty) = - let sigKw = prettyPrintKindSignatureFor sig in - paras [ line $ "The inferred kind for the " <> sigKw <> " declaration " <> markCode (runProperName name) <> " contains polymorphic kinds." - , line "Consider adding a top-level kind signature as a form of documentation." - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line $ sigKw <> " " <> runProperName name <> " ::" - , prettyTypeWithDepth maxBound ty - ] - ] - renderSimpleErrorMessage (OverlappingPattern bs b) = - paras $ [ line "A case expression contains unreachable cases:\n" - , Box.hsep 1 Box.left (map (paras . map (line . prettyPrintBinderAtom)) (transpose bs)) - ] ++ - [ line "..." | not b ] - renderSimpleErrorMessage IncompleteExhaustivityCheck = - paras [ line "An exhaustivity check was abandoned due to too many possible cases." - , line "You may want to decompose your data types into smaller types." - ] - - renderSimpleErrorMessage (UnusedImport mn qualifier) = - let - mark = markCode . runModuleName - unqualified = "The import of " <> mark mn <> " is redundant" - msg' q = "The qualified import of " <> mark mn <> " as " <> mark q <> " is redundant" - msg = maybe unqualified msg' - in line $ msg qualifier - - renderSimpleErrorMessage msg@(UnusedExplicitImport mn names _ _) = - paras [ line $ "The import of module " <> markCode (runModuleName mn) <> " contains the following unused references:" - , indent $ paras $ map (line . markCode . runName . Qualified ByNullSourcePos) names - , line "It could be replaced with:" - , indent $ line $ markCode $ showSuggestion msg ] - - renderSimpleErrorMessage msg@(UnusedDctorImport mn name _ _) = - paras [line $ "The import of type " <> markCode (runProperName name) - <> " from module " <> markCode (runModuleName mn) <> " includes data constructors but only the type is used" - , line "It could be replaced with:" - , indent $ line $ markCode $ showSuggestion msg ] - - renderSimpleErrorMessage msg@(UnusedDctorExplicitImport mn name names _ _) = - paras [ line $ "The import of type " <> markCode (runProperName name) - <> " from module " <> markCode (runModuleName mn) <> " includes the following unused data constructors:" - , indent $ paras $ map (line . markCode . runProperName) names - , line "It could be replaced with:" - , indent $ line $ markCode $ showSuggestion msg ] - - renderSimpleErrorMessage (DuplicateSelectiveImport name) = - line $ "There is an existing import of " <> markCode (runModuleName name) <> ", consider merging the import lists" - - renderSimpleErrorMessage (DuplicateImport name imp qual) = - line $ "Duplicate import of " <> markCode (prettyPrintImport name imp qual) - - renderSimpleErrorMessage (DuplicateImportRef name) = - line $ "Import list contains multiple references to " <> printName (Qualified ByNullSourcePos name) - - renderSimpleErrorMessage (DuplicateExportRef name) = - line $ "Export list contains multiple references to " <> printName (Qualified ByNullSourcePos name) - - renderSimpleErrorMessage (IntOutOfRange value backend lo hi) = - paras [ line $ "Integer value " <> markCode (T.pack (show value)) <> " is out of range for the " <> backend <> " backend." - , line $ "Acceptable values fall within the range " <> markCode (T.pack (show lo)) <> " to " <> markCode (T.pack (show hi)) <> " (inclusive)." ] - - renderSimpleErrorMessage msg@(ImplicitQualifiedImport importedModule asModule _) = - paras [ line $ "Module " <> markCode (runModuleName importedModule) <> " was imported as " <> markCode (runModuleName asModule) <> " with unspecified imports." - , line $ "As there are multiple modules being imported as " <> markCode (runModuleName asModule) <> ", consider using the explicit form:" - , indent $ line $ markCode $ showSuggestion msg - ] - renderSimpleErrorMessage msg@(ImplicitQualifiedImportReExport importedModule asModule _) = - paras [ line $ "Module " <> markCode (runModuleName importedModule) <> " was imported as " <> markCode (runModuleName asModule) <> " with unspecified imports." - , line "As this module is being re-exported, consider using the explicit form:" - , indent $ line $ markCode $ showSuggestion msg - ] - - renderSimpleErrorMessage msg@(ImplicitImport mn _) = - paras [ line $ "Module " <> markCode (runModuleName mn) <> " has unspecified imports, consider using the explicit form: " - , indent $ line $ markCode $ showSuggestion msg - ] - - renderSimpleErrorMessage msg@(HidingImport mn _) = - paras [ line $ "Module " <> markCode (runModuleName mn) <> " has unspecified imports, consider using the inclusive form: " - , indent $ line $ markCode $ showSuggestion msg - ] - - renderSimpleErrorMessage (CaseBinderLengthDiffers l bs) = - paras [ line "Binder list length differs in case alternative:" - , indent $ line $ T.intercalate ", " $ fmap prettyPrintBinderAtom bs - , line $ "Expecting " <> T.pack (show l) <> " binder" <> (if l == 1 then "" else "s") <> "." - ] - - renderSimpleErrorMessage IncorrectAnonymousArgument = - line "An anonymous function argument appears in an invalid context." - - renderSimpleErrorMessage (InvalidOperatorInBinder op fn) = - paras [ line $ "Operator " <> markCode (showQualified showOp op) <> " cannot be used in a pattern as it is an alias for function " <> showQualified showIdent fn <> "." - , line "Only aliases for data constructors may be used in patterns." - ] - - renderSimpleErrorMessage (CannotGeneralizeRecursiveFunction ident ty) = - paras [ line $ "Unable to generalize the type of the recursive function " <> markCode (showIdent ident) <> "." - , line $ "The inferred type of " <> markCode (showIdent ident) <> " was:" - , markCodeBox $ indent $ prettyType ty - , line "Try adding a type signature." - ] - - renderSimpleErrorMessage (CannotDeriveNewtypeForData tyName) = - paras [ line $ "Cannot derive an instance of the " <> markCode "Newtype" <> " class for non-newtype " <> markCode (runProperName tyName) <> "." - ] - - renderSimpleErrorMessage (ExpectedWildcard tyName) = - paras [ line $ "Expected a type wildcard (_) when deriving an instance for " <> markCode (runProperName tyName) <> "." - ] - - renderSimpleErrorMessage (CannotUseBindWithDo name) = - paras [ line $ "The name " <> markCode (showIdent name) <> " cannot be brought into scope in a do notation block, since do notation uses the same name." - ] - - renderSimpleErrorMessage (ClassInstanceArityMismatch dictName className expected actual) = - paras [ line $ "The type class " <> markCode (showQualified runProperName className) <> - " expects " <> T.pack (show expected) <> " " <> argsMsg <> "." - , line $ "But the instance" <> prettyPrintPlainIdent dictName <> mismatchMsg <> T.pack (show actual) <> "." - ] - where - mismatchMsg = if actual > expected then " provided " else " only provided " - argsMsg = if expected > 1 then "arguments" else "argument" - - renderSimpleErrorMessage (UserDefinedWarning msgTy) = - let msg = fromMaybe (prettyType msgTy) (toTypelevelString msgTy) in - paras [ line "A custom warning occurred while solving type class constraints:" - , indent msg - ] - - renderSimpleErrorMessage (CannotDefinePrimModules mn) = - paras - [ line $ "The module name " <> markCode (runModuleName mn) <> " is in the Prim namespace." - , line "The Prim namespace is reserved for compiler-defined terms." - ] - - renderSimpleErrorMessage (MixedAssociativityError opsWithAssoc) = - paras - [ line "Cannot parse an expression that uses operators of the same precedence but mixed associativity:" - , indent $ paras $ map (\(name, assoc) -> line $ markCode (showQualified showOp name) <> " is " <> markCode (T.pack (showAssoc assoc))) (NEL.toList opsWithAssoc) - , line "Use parentheses to resolve this ambiguity." - ] - - renderSimpleErrorMessage (NonAssociativeError ops) = - if NEL.length ops == 1 - then - paras - [ line $ "Cannot parse an expression that uses multiple instances of the non-associative operator " <> markCode (showQualified showOp (NEL.head ops)) <> "." - , line "Use parentheses to resolve this ambiguity." - ] - else - paras - [ line "Cannot parse an expression that uses multiple non-associative operators of the same precedence:" - , indent $ paras $ map (line . markCode . showQualified showOp) (NEL.toList ops) - , line "Use parentheses to resolve this ambiguity." - ] - - renderSimpleErrorMessage (QuantificationCheckFailureInKind var) = - paras - [ line $ "Cannot generalize the kind of type variable " <> markCode var <> " since it would not be well-scoped." - , line "Try adding a kind annotation." - ] - - renderSimpleErrorMessage (QuantificationCheckFailureInType us ty) = - let unks = - fmap (\u -> Box.hsep 1 Box.top [ "where" - , markCodeBox (prettyType (srcTUnknown u)) - , "is an unknown kind." - ]) us - in paras - [ line "Cannot unambiguously generalize kinds appearing in the elaborated type:" - , indent $ markCodeBox $ typeAsBox prettyDepth ty - , paras unks - , line "Try adding additional kind signatures or polymorphic kind variables." - ] - - renderSimpleErrorMessage (VisibleQuantificationCheckFailureInType var) = - paras - [ line $ "Visible dependent quantification of type variable " <> markCode var <> " is not supported." - , line "If you would like this feature supported, please bother Liam Goodacre (@LiamGoodacre)." - ] - - renderSimpleErrorMessage (UnsupportedTypeInKind ty) = - paras - [ line "The type:" - , indent $ markCodeBox $ prettyType ty - , line "is not supported in kinds." - ] - - renderSimpleErrorMessage (RoleMismatch var inferred declared) = - paras - [ line $ "Role mismatch for the type parameter " <> markCode var <> ":" - , indent . line $ - "The annotation says " <> markCode (displayRole declared) <> - " but the role " <> markCode (displayRole inferred) <> - " is required." - ] - - renderSimpleErrorMessage (InvalidCoercibleInstanceDeclaration tys) = - paras - [ line "Invalid type class instance declaration for" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName C.Coercible) - , Box.vcat Box.left (map prettyTypeAtom tys) - ] - , line "Instance declarations of this type class are disallowed." - ] - - renderSimpleErrorMessage UnsupportedRoleDeclaration = - line "Role declarations are only supported for data types, not for type synonyms nor type classes." - - renderSimpleErrorMessage (RoleDeclarationArityMismatch name expected actual) = - line $ T.intercalate " " - [ "The type" - , markCode (runProperName name) - , "expects" - , T.pack (show expected) - , if expected == 1 then "argument" else "arguments" - , "but its role declaration lists" - <> if actual > expected then "" else " only" - , T.pack (show actual) - , if actual > 1 then "roles" else "role" - ] <> "." - - renderSimpleErrorMessage (DuplicateRoleDeclaration name) = - line $ "Duplicate role declaration for " <> markCode (runProperName name) <> "." - - renderSimpleErrorMessage (CannotDeriveInvalidConstructorArg className relatedClasses checkVariance) = - paras - [ line $ "One or more type variables are in positions that prevent " <> markCode (runProperName $ disqualify className) <> " from being derived." - , line $ "To derive this class, make sure that these variables are only used as the final arguments to type constructors, " - <> (if checkVariance then "that their variance matches the variance of " <> markCode (runProperName $ disqualify className) <> ", " else "") - <> "and that those type constructors themselves have instances of " <> commasAndConjunction "or" (markCode . showQualified runProperName <$> relatedClasses) <> "." - ] - - renderSimpleErrorMessage (CannotSkipTypeApplication tyFn) = - paras - [ "An expression of type:" - , markCodeBox $ indent $ prettyType tyFn - , "cannot be skipped." - ] - - renderSimpleErrorMessage (CannotApplyExpressionOfTypeOnType tyFn tyAr) = - paras $ infoLine <> - [ markCodeBox $ indent $ prettyType tyFn - , "cannot be applied to:" - , markCodeBox $ indent $ prettyType tyAr - ] - where - infoLine = - if isMonoType tyFn then - [ "An expression of monomorphic type:" ] - else - [ "An expression of polymorphic type" - , line $ "with the invisible type variable " <> markCode typeVariable <> ":" - ] - - typeVariable = case tyFn of - ForAll _ _ v _ _ _ -> v - _ -> internalError "renderSimpleErrorMessage: Impossible!" - - renderHint :: ErrorMessageHint -> Box.Box -> Box.Box - renderHint (ErrorUnifyingTypes t1@RCons{} t2@RCons{}) detail = - let (row1Box, row2Box) = printRows t1 t2 - in paras [ detail - , Box.hsep 1 Box.top [ line "while trying to match type" - , row1Box - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "with type" - , row2Box - ] - ] - renderHint (ErrorUnifyingTypes t1 t2) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while trying to match type" - , markCodeBox $ typeAsBox prettyDepth t1 - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "with type" - , markCodeBox $ typeAsBox prettyDepth t2 - ] - ] - renderHint (ErrorInExpression expr) detail = - paras [ detail - , Box.hsep 1 Box.top [ Box.text "in the expression" - , markCodeBox $ markCodeBox $ prettyPrintValue prettyDepth expr - ] - ] - renderHint (ErrorInModule mn) detail = - paras [ line $ "in module " <> markCode (runModuleName mn) - , detail - ] - renderHint (ErrorInSubsumption t1 t2) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while checking that type" - , markCodeBox $ typeAsBox prettyDepth t1 - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "is at least as general as type" - , markCodeBox $ typeAsBox prettyDepth t2 - ] - ] - renderHint (ErrorInRowLabel lb) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while matching label" - , markCodeBox $ line $ prettyPrintObjectKey (runLabel lb) - ] - ] - renderHint (ErrorInInstance nm ts) detail = - paras [ detail - , line "in type class instance" - , markCodeBox $ indent $ Box.hsep 1 Box.top - [ line $ showQualified runProperName nm - , Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts) - ] - ] - renderHint (ErrorCheckingKind ty kd) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while checking that type" - , markCodeBox $ typeAsBox prettyDepth ty - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "has kind" - , markCodeBox $ typeAsBox prettyDepth kd - ] - ] - renderHint (ErrorInferringKind ty) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while inferring the kind of" - , markCodeBox $ typeAsBox prettyDepth ty - ] - ] - renderHint ErrorCheckingGuard detail = - paras [ detail - , line "while checking the type of a guard clause" - ] - renderHint (ErrorInferringType expr) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while inferring the type of" - , markCodeBox $ prettyPrintValue prettyDepth expr - ] - ] - renderHint (ErrorCheckingType expr ty) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while checking that expression" - , markCodeBox $ prettyPrintValue prettyDepth expr - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "has type" - , markCodeBox $ typeAsBox prettyDepth ty - ] - ] - renderHint (ErrorCheckingAccessor expr prop) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while checking type of property accessor" - , markCodeBox $ prettyPrintValue prettyDepth (Accessor prop expr) - ] - ] - renderHint (ErrorInApplication f t a) detail = - paras [ detail - , Box.hsep 1 Box.top [ line "while applying a function" - , markCodeBox $ prettyPrintValue prettyDepth f - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "of type" - , markCodeBox $ typeAsBox prettyDepth t - ] - , Box.moveRight 2 $ Box.hsep 1 Box.top [ line "to argument" - , markCodeBox $ prettyPrintValue prettyDepth a - ] - ] - renderHint (ErrorInDataConstructor nm) detail = - paras [ detail - , line $ "in data constructor " <> markCode (runProperName nm) - ] - renderHint (ErrorInTypeConstructor nm) detail = - paras [ detail - , line $ "in type constructor " <> markCode (runProperName nm) - ] - renderHint (ErrorInBindingGroup nms) detail = - paras [ detail - , line $ "in binding group " <> T.intercalate ", " (NEL.toList (fmap showIdent nms)) - ] - renderHint (ErrorInDataBindingGroup nms) detail = - paras [ detail - , line $ "in data binding group " <> T.intercalate ", " (map runProperName nms) - ] - renderHint (ErrorInTypeSynonym name) detail = - paras [ detail - , line $ "in type synonym " <> markCode (runProperName name) - ] - renderHint (ErrorInValueDeclaration n) detail = - paras [ detail - , line $ "in value declaration " <> markCode (showIdent n) - ] - renderHint (ErrorInTypeDeclaration n) detail = - paras [ detail - , line $ "in type declaration for " <> markCode (showIdent n) - ] - renderHint (ErrorInTypeClassDeclaration name) detail = - paras [ detail - , line $ "in type class declaration for " <> markCode (runProperName name) - ] - renderHint (ErrorInKindDeclaration name) detail = - paras [ detail - , line $ "in kind declaration for " <> markCode (runProperName name) - ] - renderHint (ErrorInRoleDeclaration name) detail = - paras [ detail - , line $ "in role declaration for " <> markCode (runProperName name) - ] - renderHint (ErrorInForeignImport nm) detail = - paras [ detail - , line $ "in foreign import " <> markCode (showIdent nm) - ] - renderHint (ErrorInForeignImportData nm) detail = - paras [ detail - , line $ "in foreign data type declaration for " <> markCode (runProperName nm) - ] - renderHint (ErrorSolvingConstraint (Constraint _ nm _ ts _)) detail = - paras [ detail - , line "while solving type class constraint" - , markCodeBox $ indent $ Box.hsep 1 Box.left - [ line (showQualified runProperName nm) - , Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts) - ] - ] - renderHint (MissingConstructorImportForCoercible name) detail = - paras - [ detail - , Box.moveUp 1 $ Box.moveRight 2 $ line $ "Solving this instance requires the newtype constructor " <> markCode (showQualified runProperName name) <> " to be in scope." - ] - renderHint (PositionedError srcSpan) detail = - paras [ line $ "at " <> displaySourceSpan relPath (NEL.head srcSpan) - , detail - ] - renderHint (RelatedPositions srcSpans) detail = - paras - [ detail - , Box.moveRight 2 $ showSourceSpansInContext srcSpans - ] - - printRow :: (Int -> Type a -> Box.Box) -> Type a -> Box.Box - printRow f = markCodeBox . indent . f prettyDepth . - if full then id else eraseForAllKindAnnotations . eraseKindApps - - -- If both rows are not empty, print them as diffs - -- If verbose print all rows else only print unique rows - printRows :: Type a -> Type a -> (Box.Box, Box.Box) - printRows r1 r2 = case (full, r1, r2) of - (True, _ , _) -> (printRow typeAsBox r1, printRow typeAsBox r2) - - (_, RCons{}, RCons{}) -> - let (sorted1, sorted2) = filterRows (rowToList r1) (rowToList r2) - in (printRow typeDiffAsBox sorted1, printRow typeDiffAsBox sorted2) - - (_, _, _) -> (printRow typeAsBox r1, printRow typeAsBox r2) - - - -- Keep the unique labels only - filterRows :: ([RowListItem a], Type a) -> ([RowListItem a], Type a) -> (Type a, Type a) - filterRows (s1, r1) (s2, r2) = - let sort' = sortOn $ \(RowListItem _ name ty) -> (name, ty) - (unique1, unique2) = diffSortedRowLists (sort' s1, sort' s2) - in ( rowFromList (unique1, r1) - , rowFromList (unique2, r2) - ) - - -- Importantly, this removes exactly the same number of elements from - -- both lists, even if there are repeated (name, ty) keys. It requires - -- the inputs to be sorted but ensures that the outputs remain sorted. - diffSortedRowLists :: ([RowListItem a], [RowListItem a]) -> ([RowListItem a], [RowListItem a]) - diffSortedRowLists = go where - go = \case - (s1@(h1@(RowListItem _ name1 ty1) : t1), s2@(h2@(RowListItem _ name2 ty2) : t2)) -> - case (name1, ty1) `compare` (name2, ty2) of - EQ -> go (t1, t2) - LT -> first (h1:) $ go (t1, s2) - GT -> second (h2:) $ go (s1, t2) - other -> other - - renderContext :: Context -> [Box.Box] - renderContext [] = [] - renderContext ctx = - [ line "in the following context:" - , indent $ paras - [ Box.hcat Box.left [ Box.text (T.unpack (showIdent ident) ++ " :: ") - , markCodeBox $ typeAsBox prettyDepth ty' - ] - | (ident, ty') <- take 30 ctx - ] - ] - - printName :: Qualified Name -> Text - printName qn = nameType (disqualify qn) <> " " <> markCode (runName qn) - - nameType :: Name -> Text - nameType (IdentName _) = "value" - nameType (ValOpName _) = "operator" - nameType (TyName _) = "type" - nameType (TyOpName _) = "type operator" - nameType (DctorName _) = "data constructor" - nameType (TyClassName _) = "type class" - nameType (ModName _) = "module" - - runName :: Qualified Name -> Text - runName (Qualified qb (IdentName name)) = - showQualified showIdent (Qualified qb name) - runName (Qualified qb (ValOpName op)) = - showQualified showOp (Qualified qb op) - runName (Qualified qb (TyName name)) = - showQualified runProperName (Qualified qb name) - runName (Qualified qb (TyOpName op)) = - showQualified showOp (Qualified qb op) - runName (Qualified qb (DctorName name)) = - showQualified runProperName (Qualified qb name) - runName (Qualified qb (TyClassName name)) = - showQualified runProperName (Qualified qb name) - runName (Qualified (BySourcePos _) (ModName name)) = - runModuleName name - runName (Qualified _ ModName{}) = - internalError "qualified ModName in runName" - - prettyDepth :: Int - prettyDepth | full = 1000 - | otherwise = 3 - - prettyType :: Type a -> Box.Box - prettyType = prettyTypeWithDepth prettyDepth - - prettyTypeWithDepth :: Int -> Type a -> Box.Box - prettyTypeWithDepth depth - | full = typeAsBox depth - | otherwise = typeAsBox depth . eraseForAllKindAnnotations . eraseKindApps - - prettyTypeAtom :: Type a -> Box.Box - prettyTypeAtom - | full = typeAtomAsBox prettyDepth - | otherwise = typeAtomAsBox prettyDepth . eraseForAllKindAnnotations . eraseKindApps - - levelText :: Text - levelText = case level of - Error -> "error" - Warning -> "warning" - - paras :: forall f. Foldable f => f Box.Box -> Box.Box - paras = Box.vcat Box.left - - -- Simplify an error message - simplifyErrorMessage :: ErrorMessage -> ErrorMessage - simplifyErrorMessage (ErrorMessage hints simple) = ErrorMessage (simplifyHints hints) simple - where - -- Take the last instance of each "hint category" - simplifyHints :: [ErrorMessageHint] -> [ErrorMessageHint] - simplifyHints = reverse . nubBy categoriesEqual . stripRedundantHints simple . reverse - - -- Don't remove hints in the "other" category - categoriesEqual :: ErrorMessageHint -> ErrorMessageHint -> Bool - categoriesEqual x y = - case (hintCategory x, hintCategory y) of - (OtherHint, _) -> False - (_, OtherHint) -> False - (c1, c2) -> c1 == c2 - - -- See https://github.com/purescript/purescript/issues/1802 - stripRedundantHints :: SimpleErrorMessage -> [ErrorMessageHint] -> [ErrorMessageHint] - stripRedundantHints ExprDoesNotHaveType{} = stripFirst isCheckHint - where - isCheckHint ErrorCheckingType{} = True - isCheckHint _ = False - stripRedundantHints TypesDoNotUnify{} = stripFirst isUnifyHint - where - isUnifyHint ErrorUnifyingTypes{} = True - isUnifyHint _ = False - stripRedundantHints (NoInstanceFound (Constraint _ C.Coercible _ args _) _ _) = filter (not . isSolverHint) - where - isSolverHint (ErrorSolvingConstraint (Constraint _ C.Coercible _ args' _)) = args == args' - isSolverHint _ = False - stripRedundantHints NoInstanceFound{} = stripFirst isSolverHint - where - isSolverHint ErrorSolvingConstraint{} = True - isSolverHint _ = False - stripRedundantHints _ = id - - stripFirst :: (ErrorMessageHint -> Bool) -> [ErrorMessageHint] -> [ErrorMessageHint] - stripFirst p (PositionedError pos : hs) = PositionedError pos : stripFirst p hs - stripFirst p (ErrorInModule mn : hs) = ErrorInModule mn : stripFirst p hs - stripFirst p (hint : hs) - | p hint = hs - | otherwise = hint : hs - stripFirst _ [] = [] - - hintCategory :: ErrorMessageHint -> HintCategory - hintCategory ErrorCheckingType{} = ExprHint - hintCategory ErrorInferringType{} = ExprHint - hintCategory ErrorInExpression{} = ExprHint - hintCategory ErrorUnifyingTypes{} = CheckHint - hintCategory ErrorInSubsumption{} = CheckHint - hintCategory ErrorInApplication{} = CheckHint - hintCategory ErrorCheckingKind{} = CheckHint - hintCategory ErrorSolvingConstraint{} = SolverHint - hintCategory PositionedError{} = PositionHint - hintCategory ErrorInDataConstructor{} = DeclarationHint - hintCategory ErrorInTypeConstructor{} = DeclarationHint - hintCategory ErrorInBindingGroup{} = DeclarationHint - hintCategory ErrorInDataBindingGroup{} = DeclarationHint - hintCategory ErrorInTypeSynonym{} = DeclarationHint - hintCategory ErrorInValueDeclaration{} = DeclarationHint - hintCategory ErrorInTypeDeclaration{} = DeclarationHint - hintCategory ErrorInTypeClassDeclaration{} = DeclarationHint - hintCategory ErrorInKindDeclaration{} = DeclarationHint - hintCategory ErrorInRoleDeclaration{} = DeclarationHint - hintCategory ErrorInForeignImport{} = DeclarationHint - hintCategory _ = OtherHint - - prettyPrintPlainIdent :: Ident -> Text - prettyPrintPlainIdent ident = - if isPlainIdent ident - then " " <> markCode (showIdent ident) - else "" - - prettyInstanceName :: Qualified (Either SourceType Ident) -> Box.Box - prettyInstanceName = \case - Qualified qb (Left ty) -> - "instance " - Box.<> (case qb of - ByModuleName mn -> "in module " - Box.<> line (markCode $ runModuleName mn) - Box.<> " " - _ -> Box.nullBox) - Box.<> "with type " - Box.<> markCodeBox (prettyType ty) - Box.<> " " - Box.<> (line . displayStartEndPos . fst $ getAnnForType ty) - Qualified mn (Right inst) -> line . markCode . showQualified showIdent $ Qualified mn inst - - -- As of this writing, this function assumes that all provided SourceSpans - -- are non-overlapping (except for exact duplicates) and span no line breaks. A - -- more sophisticated implementation without this limitation would be possible - -- but isn't yet needed. - showSourceSpansInContext :: NonEmpty SourceSpan -> Box.Box - showSourceSpansInContext - = maybe Box.nullBox (paras . fmap renderFile . NEL.groupWith1 spanName . NEL.sort) - . NEL.nonEmpty - . NEL.filter ((> 0) . sourcePosLine . spanStart) - where - renderFile :: NonEmpty SourceSpan -> Box.Box - renderFile sss = maybe Box.nullBox (linesToBox . T.lines) $ lookup fileName fileContents - where - fileName = spanName $ NEL.head sss - header = lineS . (<> ":") . makeRelative relPath $ fileName - lineBlocks = makeLineBlocks $ NEL.groupWith1 (sourcePosLine . spanStart) sss - - linesToBox fileLines = Box.moveUp 1 $ header Box.// body - where - body - = Box.punctuateV Box.left (lineNumberStyle "...") - . map (paras . fmap renderLine) - . flip evalState (fileLines, 1) - . traverse (wither (\(i, x) -> fmap (i, , x) <$> ascLookupInState i) . NEL.toList) - $ NEL.toList lineBlocks - - makeLineBlocks :: NonEmpty (NonEmpty SourceSpan) -> NonEmpty (NonEmpty (Int, [SourceSpan])) - makeLineBlocks = startBlock - where - startBlock (h :| t) = over head1 (NEL.cons (pred $ headLineNumber h, [])) $ continueBlock h t - - continueBlock :: NonEmpty SourceSpan -> [NonEmpty SourceSpan] -> NonEmpty (NonEmpty (Int, [SourceSpan])) - continueBlock lineGroup = \case - [] -> - endBlock lineGroup [] - nextGroup : groups -> case pred $ ((-) `on` headLineNumber) nextGroup lineGroup of - n | n <= 3 -> - over head1 (appendExtraLines n lineGroup <>) $ continueBlock nextGroup groups - _ -> - endBlock lineGroup . NEL.toList . startBlock $ nextGroup :| groups - - endBlock :: NonEmpty SourceSpan -> [NonEmpty (Int, [SourceSpan])] -> NonEmpty (NonEmpty (Int, [SourceSpan])) - endBlock h t = appendExtraLines 1 h :| t - - headLineNumber = sourcePosLine . spanStart . NEL.head - - appendExtraLines :: Int -> NonEmpty SourceSpan -> NonEmpty (Int, [SourceSpan]) - appendExtraLines n lineGroup = (lineNum, NEL.toList lineGroup) :| [(lineNum + i, []) | i <- [1..n]] - where - lineNum = headLineNumber lineGroup - - renderLine :: (Int, Text, [SourceSpan]) -> Box.Box - renderLine (lineNum, text, sss) = numBox Box.<+> lineBox - where - colSpans = nubOrdOn fst $ map (over both (pred . sourcePosColumn) . (spanStart &&& spanEnd)) sss - numBox = lineNumberStyle $ show lineNum - lineBox = - if isJust codeColor - then colorCodeBox codeColor $ line $ foldr highlightSpan text colSpans - else line text Box.// line (finishUnderline $ foldr underlineSpan (T.length text, "") colSpans) - - highlightSpan :: (Int, Int) -> Text -> Text - highlightSpan (startCol, endCol) text - = prefix - <> T.pack (ANSI.setSGRCode [ANSI.SetSwapForegroundBackground True]) - <> spanText - <> T.pack (ANSI.setSGRCode [ANSI.SetSwapForegroundBackground False]) - <> suffix - where - (prefix, rest) = T.splitAt startCol text - (spanText, suffix) = T.splitAt (endCol - startCol) rest - - underlineSpan :: (Int, Int) -> (Int, Text) -> (Int, Text) - underlineSpan (startCol, endCol) (len, accum) = (startCol, T.replicate (endCol - startCol) "^" <> T.replicate (len - endCol) " " <> accum) - - finishUnderline :: (Int, Text) -> Text - finishUnderline (len, accum) = T.replicate len " " <> accum - - lineNumberStyle :: String -> Box.Box - lineNumberStyle = colorCodeBox (codeColor $> (ANSI.Vivid, ANSI.Black)) . Box.alignHoriz Box.right 5 . lineS - - -- Lookup the nth element of a list, but without retraversing the list every - -- time, by instead keeping a tail of the list and the current element number - -- in State. Only works if the argument provided is strictly ascending over - -- the life of the State. - ascLookupInState :: forall a. Int -> State ([a], Int) (Maybe a) - ascLookupInState j = get >>= \(as, i) -> for (uncons $ drop (j - i) as) $ \(a, as') -> put (as', succ j) $> a - --- Pretty print and export declaration -prettyPrintExport :: DeclarationRef -> Text -prettyPrintExport (TypeRef _ pn _) = runProperName pn -prettyPrintExport ref = - fromMaybe - (internalError "prettyPrintRef returned Nothing in prettyPrintExport") - (prettyPrintRef ref) - -prettyPrintImport :: ModuleName -> ImportDeclarationType -> Maybe ModuleName -> Text -prettyPrintImport mn idt qual = - let i = case idt of - Implicit -> runModuleName mn - Explicit refs -> runModuleName mn <> " (" <> T.intercalate ", " (mapMaybe prettyPrintRef refs) <> ")" - Hiding refs -> runModuleName mn <> " hiding (" <> T.intercalate ", " (mapMaybe prettyPrintRef refs) <> ")" - in i <> maybe "" (\q -> " as " <> runModuleName q) qual - -prettyPrintRef :: DeclarationRef -> Maybe Text -prettyPrintRef (TypeRef _ pn Nothing) = - Just $ runProperName pn <> "(..)" -prettyPrintRef (TypeRef _ pn (Just [])) = - Just $ runProperName pn -prettyPrintRef (TypeRef _ pn (Just dctors)) = - Just $ runProperName pn <> "(" <> T.intercalate ", " (map runProperName dctors) <> ")" -prettyPrintRef (TypeOpRef _ op) = - Just $ "type " <> showOp op -prettyPrintRef (ValueRef _ ident) = - Just $ showIdent ident -prettyPrintRef (ValueOpRef _ op) = - Just $ showOp op -prettyPrintRef (TypeClassRef _ pn) = - Just $ "class " <> runProperName pn -prettyPrintRef (TypeInstanceRef _ ident UserNamed) = - Just $ showIdent ident -prettyPrintRef (TypeInstanceRef _ _ CompilerNamed) = - Nothing -prettyPrintRef (ModuleRef _ name) = - Just $ "module " <> runModuleName name -prettyPrintRef ReExportRef{} = - Nothing - -prettyPrintKindSignatureFor :: KindSignatureFor -> Text -prettyPrintKindSignatureFor DataSig = "data" -prettyPrintKindSignatureFor NewtypeSig = "newtype" -prettyPrintKindSignatureFor TypeSynonymSig = "type" -prettyPrintKindSignatureFor ClassSig = "class" - -prettyPrintSuggestedTypeSimplified :: Type a -> String -prettyPrintSuggestedTypeSimplified = prettyPrintSuggestedType . eraseForAllKindAnnotations . eraseKindApps - --- | Pretty print multiple errors -prettyPrintMultipleErrors :: PPEOptions -> MultipleErrors -> String -prettyPrintMultipleErrors ppeOptions = unlines . map renderBox . prettyPrintMultipleErrorsBox ppeOptions - --- | Pretty print multiple warnings -prettyPrintMultipleWarnings :: PPEOptions -> MultipleErrors -> String -prettyPrintMultipleWarnings ppeOptions = unlines . map renderBox . prettyPrintMultipleWarningsBox ppeOptions - --- | Pretty print warnings as a Box -prettyPrintMultipleWarningsBox :: PPEOptions -> MultipleErrors -> [Box.Box] -prettyPrintMultipleWarningsBox ppeOptions = prettyPrintMultipleErrorsWith (ppeOptions { ppeLevel = Warning }) "Warning found:" "Warning" - --- | Pretty print errors as a Box -prettyPrintMultipleErrorsBox :: PPEOptions -> MultipleErrors -> [Box.Box] -prettyPrintMultipleErrorsBox ppeOptions = prettyPrintMultipleErrorsWith (ppeOptions { ppeLevel = Error }) "Error found:" "Error" - -prettyPrintMultipleErrorsWith :: PPEOptions -> String -> String -> MultipleErrors -> [Box.Box] -prettyPrintMultipleErrorsWith ppeOptions intro _ (MultipleErrors [e]) = - let result = prettyPrintSingleError ppeOptions e - in [ Box.vcat Box.left [ Box.text intro - , result - ] - ] -prettyPrintMultipleErrorsWith ppeOptions _ intro (MultipleErrors es) = - let result = map (prettyPrintSingleError ppeOptions) es - in concat $ zipWith withIntro [1 :: Int ..] result - where - withIntro i err = [ Box.text (intro ++ " " ++ show i ++ " of " ++ show (length es) ++ ":") - , Box.moveRight 2 err - ] - --- | Indent to the right, and pad on top and bottom. -indent :: Box.Box -> Box.Box -indent = Box.moveUp 1 . Box.moveDown 1 . Box.moveRight 2 - -line :: Text -> Box.Box -line = Box.text . T.unpack - -lineS :: String -> Box.Box -lineS = Box.text - -renderBox :: Box.Box -> String -renderBox = unlines - . map (dropWhileEnd isSpace) - . dropWhile whiteSpace - . dropWhileEnd whiteSpace - . lines - . Box.render - where - whiteSpace = all isSpace - -toTypelevelString :: Type a -> Maybe Box.Box -toTypelevelString (TypeLevelString _ s) = - Just . Box.text $ decodeStringWithReplacement s -toTypelevelString (TypeApp _ (TypeConstructor _ C.Text) x) = - toTypelevelString x -toTypelevelString (TypeApp _ (KindApp _ (TypeConstructor _ C.Quote) _) x) = - Just (typeAsBox maxBound x) -toTypelevelString (TypeApp _ (TypeConstructor _ C.QuoteLabel) (TypeLevelString _ x)) = - Just . line . prettyPrintLabel . Label $ x -toTypelevelString (TypeApp _ (TypeApp _ (TypeConstructor _ C.Beside) x) ret) = - (Box.<>) <$> toTypelevelString x <*> toTypelevelString ret -toTypelevelString (TypeApp _ (TypeApp _ (TypeConstructor _ C.Above) x) ret) = - (Box.//) <$> toTypelevelString x <*> toTypelevelString ret -toTypelevelString _ = Nothing - --- | Rethrow an error with a more detailed error message in the case of failure -rethrow :: (MonadError e m) => (e -> e) -> m a -> m a -rethrow f = flip catchError (throwError . f) - -warnAndRethrow :: (MonadError e m, MonadWriter e m) => (e -> e) -> m a -> m a -warnAndRethrow f = rethrow f . censor f - --- | Rethrow an error with source position information -rethrowWithPosition :: (MonadError MultipleErrors m) => SourceSpan -> m a -> m a -rethrowWithPosition pos = rethrow (onErrorMessages (withPosition pos)) - -warnWithPosition :: (MonadWriter MultipleErrors m) => SourceSpan -> m a -> m a -warnWithPosition pos = censor (onErrorMessages (withPosition pos)) - -warnAndRethrowWithPosition :: (MonadError MultipleErrors m, MonadWriter MultipleErrors m) => SourceSpan -> m a -> m a -warnAndRethrowWithPosition pos = rethrowWithPosition pos . warnWithPosition pos - -withPosition :: SourceSpan -> ErrorMessage -> ErrorMessage -withPosition NullSourceSpan err = err -withPosition pos (ErrorMessage hints se) = ErrorMessage (positionedError pos : hints) se - -withoutPosition :: ErrorMessage -> ErrorMessage -withoutPosition (ErrorMessage hints se) = ErrorMessage (filter go hints) se - where - go (PositionedError _) = False - go _ = True - -positionedError :: SourceSpan -> ErrorMessageHint -positionedError = PositionedError . pure - --- | Runs a computation listening for warnings and then escalating any warnings --- that match the predicate to error status. -escalateWarningWhen - :: (MonadWriter MultipleErrors m, MonadError MultipleErrors m) - => (ErrorMessage -> Bool) - -> m a - -> m a -escalateWarningWhen isError ma = do - (a, w) <- censor (const mempty) $ listen ma - let (errors, warnings) = partition isError (runMultipleErrors w) - tell $ MultipleErrors warnings - unless (null errors) $ throwError $ MultipleErrors errors - return a - --- | Collect errors in in parallel -parU - :: forall m a b - . MonadError MultipleErrors m - => [a] - -> (a -> m b) - -> m [b] -parU xs f = - forM xs (withError . f) >>= collectErrors - where - withError :: m b -> m (Either MultipleErrors b) - withError u = catchError (Right <$> u) (return . Left) - - collectErrors :: [Either MultipleErrors b] -> m [b] - collectErrors es = case partitionEithers es of - ([], rs) -> return rs - (errs, _) -> throwError $ fold errs - -internalCompilerError - :: (MonadError MultipleErrors m, GHC.Stack.HasCallStack) - => Text - -> m a -internalCompilerError = - throwError - . errorMessage - . InternalCompilerError (T.pack (GHC.Stack.prettyCallStack GHC.Stack.callStack)) diff --git a/claude-help/original-compiler/src/Language/PureScript/Errors/JSON.hs b/claude-help/original-compiler/src/Language/PureScript/Errors/JSON.hs deleted file mode 100644 index ceeb76fd..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Errors/JSON.hs +++ /dev/null @@ -1,77 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - -module Language.PureScript.Errors.JSON where - -import Prelude - -import Data.Aeson.TH qualified as A -import Data.List.NonEmpty qualified as NEL -import Data.Text (Text) -import Language.PureScript.AST.SourcePos qualified as P -import Language.PureScript.Errors qualified as P -import Language.PureScript.Names qualified as P - - -data ErrorPosition = ErrorPosition - { startLine :: Int - , startColumn :: Int - , endLine :: Int - , endColumn :: Int - } deriving (Show, Eq, Ord) - -data ErrorSuggestion = ErrorSuggestion - { replacement :: Text - , replaceRange :: Maybe ErrorPosition - } deriving (Show, Eq) - -data JSONError = JSONError - { position :: Maybe ErrorPosition - , message :: String - , errorCode :: Text - , errorLink :: Text - , filename :: Maybe String - , moduleName :: Maybe Text - , suggestion :: Maybe ErrorSuggestion - , allSpans :: [P.SourceSpan] - } deriving (Show, Eq) - -data JSONResult = JSONResult - { warnings :: [JSONError] - , errors :: [JSONError] - } deriving (Show, Eq) - -$(A.deriveJSON A.defaultOptions ''ErrorPosition) -$(A.deriveJSON A.defaultOptions ''ErrorSuggestion) -$(A.deriveJSON A.defaultOptions ''JSONError) -$(A.deriveJSON A.defaultOptions ''JSONResult) - -toJSONErrors :: Bool -> P.Level -> [(FilePath, Text)] -> P.MultipleErrors -> [JSONError] -toJSONErrors verbose level files = map (toJSONError verbose level files) . P.runMultipleErrors - -toJSONError :: Bool -> P.Level -> [(FilePath, Text)] -> P.ErrorMessage -> JSONError -toJSONError verbose level files e = - JSONError (toErrorPosition <$> fmap NEL.head spans) - (P.renderBox (P.prettyPrintSingleError (P.PPEOptions Nothing verbose level False mempty files) (P.stripModuleAndSpan e))) - (P.errorCode e) - (P.errorDocUri e) - (P.spanName <$> fmap NEL.head spans) - (P.runModuleName <$> P.errorModule e) - (toSuggestion e) - (maybe [] NEL.toList spans) - where - spans :: Maybe (NEL.NonEmpty P.SourceSpan) - spans = P.errorSpan e - - toErrorPosition :: P.SourceSpan -> ErrorPosition - toErrorPosition ss = - ErrorPosition (P.sourcePosLine (P.spanStart ss)) - (P.sourcePosColumn (P.spanStart ss)) - (P.sourcePosLine (P.spanEnd ss)) - (P.sourcePosColumn (P.spanEnd ss)) - toSuggestion :: P.ErrorMessage -> Maybe ErrorSuggestion - toSuggestion em = - case P.errorSuggestion $ P.unwrapErrorMessage em of - Nothing -> Nothing - Just s -> Just $ ErrorSuggestion (suggestionText s) (toErrorPosition <$> P.suggestionSpan em) - - suggestionText (P.ErrorSuggestion s) = s diff --git a/claude-help/original-compiler/src/Language/PureScript/Externs.hs b/claude-help/original-compiler/src/Language/PureScript/Externs.hs deleted file mode 100644 index 3a310676..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Externs.hs +++ /dev/null @@ -1,290 +0,0 @@ -{-# Language DeriveAnyClass #-} --- | --- This module generates code for \"externs\" files, i.e. files containing only --- foreign import declarations. --- -module Language.PureScript.Externs - ( ExternsFile(..) - , ExternsImport(..) - , ExternsFixity(..) - , ExternsTypeFixity(..) - , ExternsDeclaration(..) - , externsIsCurrentVersion - , moduleToExternsFile - , applyExternsFileToEnvironment - , externsFileName - , currentVersion - ) where - -import Prelude - -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Control.Monad (join) -import Data.Maybe (fromMaybe, mapMaybe, maybeToList) -import Data.List (foldl', find) -import Data.Foldable (fold) -import Data.Text (Text) -import Data.Text qualified as T -import Data.Version (showVersion) -import Data.Map qualified as M -import Data.List.NonEmpty qualified as NEL -import GHC.Generics (Generic) - -import Language.PureScript.AST (Associativity, Declaration(..), DeclarationRef(..), Fixity(..), ImportDeclarationType, Module(..), NameSource(..), Precedence, SourceSpan, pattern TypeFixityDeclaration, pattern ValueFixityDeclaration, getTypeOpRef, getValueOpRef) -import Language.PureScript.AST.Declarations.ChainId (ChainId) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType, Environment(..), FunctionalDependency, NameKind(..), NameVisibility(..), TypeClassData(..), TypeKind(..), dictTypeName, makeTypeClassData) -import Language.PureScript.Names (Ident, ModuleName, OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, isPlainIdent) -import Language.PureScript.TypeClassDictionaries (NamedDict, TypeClassDictionaryInScope(..)) -import Language.PureScript.Types (SourceConstraint, SourceType, srcInstanceType) - -import Paths_purescript as Paths -import Data.Aeson (ToJSON, FromJSON) - --- | The data which will be serialized to an externs file -data ExternsFile = ExternsFile - -- NOTE: Make sure to keep `efVersion` as the first field in this - -- record, so the derived Serialise instance produces CBOR that can - -- be checked for its version independent of the remaining format - { efVersion :: Text - -- ^ The externs version - , efModuleName :: ModuleName - -- ^ Module name - , efExports :: [DeclarationRef] - -- ^ List of module exports - , efImports :: [ExternsImport] - -- ^ List of module imports - , efFixities :: [ExternsFixity] - -- ^ List of operators and their fixities - , efTypeFixities :: [ExternsTypeFixity] - -- ^ List of type operators and their fixities - , efDeclarations :: [ExternsDeclaration] - -- ^ List of type and value declaration - , efSourceSpan :: SourceSpan - -- ^ Source span for error reporting - } deriving (Show, Generic, NFData, ToJSON, FromJSON) - -instance Serialise ExternsFile - - --- | A module import in an externs file -data ExternsImport = ExternsImport - { - -- | The imported module - eiModule :: ModuleName - -- | The import type: regular, qualified or hiding - , eiImportType :: ImportDeclarationType - -- | The imported-as name, for qualified imports - , eiImportedAs :: Maybe ModuleName - } deriving (Show, Generic, NFData) - -instance Serialise ExternsImport -instance ToJSON ExternsImport -instance FromJSON ExternsImport - --- | A fixity declaration in an externs file -data ExternsFixity = ExternsFixity - { - -- | The associativity of the operator - efAssociativity :: Associativity - -- | The precedence level of the operator - , efPrecedence :: Precedence - -- | The operator symbol - , efOperator :: OpName 'ValueOpName - -- | The value the operator is an alias for - , efAlias :: Qualified (Either Ident (ProperName 'ConstructorName)) - } deriving (Eq, Show, Generic, NFData) - -instance Serialise ExternsFixity -instance ToJSON ExternsFixity -instance FromJSON ExternsFixity - --- | A type fixity declaration in an externs file -data ExternsTypeFixity = ExternsTypeFixity - { - -- | The associativity of the operator - efTypeAssociativity :: Associativity - -- | The precedence level of the operator - , efTypePrecedence :: Precedence - -- | The operator symbol - , efTypeOperator :: OpName 'TypeOpName - -- | The value the operator is an alias for - , efTypeAlias :: Qualified (ProperName 'TypeName) - } deriving (Eq, Show, Generic, NFData, ToJSON, FromJSON) - -instance Serialise ExternsTypeFixity - --- | A type or value declaration appearing in an externs file -data ExternsDeclaration = - -- | A type declaration - EDType - { edTypeName :: ProperName 'TypeName - , edTypeKind :: SourceType - , edTypeDeclarationKind :: TypeKind - } - -- | A type synonym - | EDTypeSynonym - { edTypeSynonymName :: ProperName 'TypeName - , edTypeSynonymArguments :: [(Text, Maybe SourceType)] - , edTypeSynonymType :: SourceType - } - -- | A data constructor - | EDDataConstructor - { edDataCtorName :: ProperName 'ConstructorName - , edDataCtorOrigin :: DataDeclType - , edDataCtorTypeCtor :: ProperName 'TypeName - , edDataCtorType :: SourceType - , edDataCtorFields :: [Ident] - } - -- | A value declaration - | EDValue - { edValueName :: Ident - , edValueType :: SourceType - } - -- | A type class declaration - | EDClass - { edClassName :: ProperName 'ClassName - , edClassTypeArguments :: [(Text, Maybe SourceType)] - , edClassMembers :: [(Ident, SourceType)] - , edClassConstraints :: [SourceConstraint] - , edFunctionalDependencies :: [FunctionalDependency] - , edIsEmpty :: Bool - } - -- | An instance declaration - | EDInstance - { edInstanceClassName :: Qualified (ProperName 'ClassName) - , edInstanceName :: Ident - , edInstanceForAll :: [(Text, SourceType)] - , edInstanceKinds :: [SourceType] - , edInstanceTypes :: [SourceType] - , edInstanceConstraints :: Maybe [SourceConstraint] - , edInstanceChain :: Maybe ChainId - , edInstanceChainIndex :: Integer - , edInstanceNameSource :: NameSource - , edInstanceSourceSpan :: SourceSpan - } - deriving (Eq, Show, Generic, NFData, ToJSON, FromJSON) - -instance Serialise ExternsDeclaration - -currentVersion :: String -currentVersion = showVersion Paths.version - --- | Check whether the version in an externs file matches the currently running --- version. -externsIsCurrentVersion :: ExternsFile -> Bool -externsIsCurrentVersion ef = - T.unpack (efVersion ef) == currentVersion - --- | Convert an externs file back into a module -applyExternsFileToEnvironment :: ExternsFile -> Environment -> Environment -applyExternsFileToEnvironment ExternsFile{..} = flip (foldl' applyDecl) efDeclarations - where - applyDecl :: Environment -> ExternsDeclaration -> Environment - applyDecl env (EDType pn kind tyKind) = env { types = M.insert (qual pn) (kind, tyKind) (types env) } - applyDecl env (EDTypeSynonym pn args ty) = env { typeSynonyms = M.insert (qual pn) (args, ty) (typeSynonyms env) } - applyDecl env (EDDataConstructor pn dTy tNm ty nms) = env { dataConstructors = M.insert (qual pn) (dTy, tNm, ty, nms) (dataConstructors env) } - applyDecl env (EDValue ident ty) = env { names = M.insert (Qualified (ByModuleName efModuleName) ident) (ty, External, Defined) (names env) } - applyDecl env (EDClass pn args members cs deps tcIsEmpty) = env { typeClasses = M.insert (qual pn) (makeTypeClassData args members cs deps tcIsEmpty) (typeClasses env) } - applyDecl env (EDInstance className ident vars kinds tys cs ch idx ns ss) = - env { typeClassDictionaries = - updateMap - (updateMap (M.insertWith (<>) (qual ident) (pure dict)) className) - (ByModuleName efModuleName) (typeClassDictionaries env) } - where - dict :: NamedDict - dict = TypeClassDictionaryInScope ch idx (qual ident) [] className vars kinds tys cs instTy - - updateMap :: (Ord k, Monoid a) => (a -> a) -> k -> M.Map k a -> M.Map k a - updateMap f = M.alter (Just . f . fold) - - instTy :: Maybe SourceType - instTy = case ns of - CompilerNamed -> Just $ srcInstanceType ss vars className tys - UserNamed -> Nothing - - qual :: a -> Qualified a - qual = Qualified (ByModuleName efModuleName) - --- | Generate an externs file for all declarations in a module. --- --- The `Map Ident Ident` argument should contain any top-level `GenIdent`s that --- were rewritten to `Ident`s when the module was compiled; this rewrite only --- happens in the CoreFn, not the original module AST, so it needs to be --- applied to the exported names here also. (The appropriate map is returned by --- `L.P.Renamer.renameInModule`.) -moduleToExternsFile :: Module -> Environment -> M.Map Ident Ident -> ExternsFile -moduleToExternsFile (Module _ _ _ _ Nothing) _ _ = internalError "moduleToExternsFile: module exports were not elaborated" -moduleToExternsFile (Module ss _ mn ds (Just exps)) env renamedIdents = ExternsFile{..} - where - efVersion = T.pack currentVersion - efModuleName = mn - efExports = map renameRef exps - efImports = mapMaybe importDecl ds - efFixities = mapMaybe fixityDecl ds - efTypeFixities = mapMaybe typeFixityDecl ds - efDeclarations = concatMap toExternsDeclaration exps - efSourceSpan = ss - - fixityDecl :: Declaration -> Maybe ExternsFixity - fixityDecl (ValueFixityDeclaration _ (Fixity assoc prec) name op) = - fmap (const (ExternsFixity assoc prec op name)) (find ((== Just op) . getValueOpRef) exps) - fixityDecl _ = Nothing - - typeFixityDecl :: Declaration -> Maybe ExternsTypeFixity - typeFixityDecl (TypeFixityDeclaration _ (Fixity assoc prec) name op) = - fmap (const (ExternsTypeFixity assoc prec op name)) (find ((== Just op) . getTypeOpRef) exps) - typeFixityDecl _ = Nothing - - importDecl :: Declaration -> Maybe ExternsImport - importDecl (ImportDeclaration _ m mt qmn) = Just (ExternsImport m mt qmn) - importDecl _ = Nothing - - toExternsDeclaration :: DeclarationRef -> [ExternsDeclaration] - toExternsDeclaration (TypeRef _ pn dctors) = - case Qualified (ByModuleName mn) pn `M.lookup` types env of - Nothing -> internalError "toExternsDeclaration: no kind in toExternsDeclaration" - Just (kind, TypeSynonym) - | Just (args, synTy) <- Qualified (ByModuleName mn) pn `M.lookup` typeSynonyms env -> [ EDType pn kind TypeSynonym, EDTypeSynonym pn args synTy ] - Just (kind, ExternData rs) -> [ EDType pn kind (ExternData rs) ] - Just (kind, tk@(DataType _ _ tys)) -> - EDType pn kind tk : [ EDDataConstructor dctor dty pn ty args - | dctor <- fromMaybe (map fst tys) dctors - , (dty, _, ty, args) <- maybeToList (Qualified (ByModuleName mn) dctor `M.lookup` dataConstructors env) - ] - _ -> internalError "toExternsDeclaration: Invalid input" - toExternsDeclaration (ValueRef _ ident) - | Just (ty, _, _) <- Qualified (ByModuleName mn) ident `M.lookup` names env - = [ EDValue (lookupRenamedIdent ident) ty ] - toExternsDeclaration (TypeClassRef _ className) - | let dictName = dictTypeName . coerceProperName $ className - , Just TypeClassData{..} <- Qualified (ByModuleName mn) className `M.lookup` typeClasses env - , Just (kind, tk) <- Qualified (ByModuleName mn) (coerceProperName className) `M.lookup` types env - , Just (dictKind, dictData@(DataType _ _ [(dctor, _)])) <- Qualified (ByModuleName mn) dictName `M.lookup` types env - , Just (dty, _, ty, args) <- Qualified (ByModuleName mn) dctor `M.lookup` dataConstructors env - = [ EDType (coerceProperName className) kind tk - , EDType dictName dictKind dictData - , EDDataConstructor dctor dty dictName ty args - , EDClass className typeClassArguments ((\(a, b, _) -> (a, b)) <$> typeClassMembers) typeClassSuperclasses typeClassDependencies typeClassIsEmpty - ] - toExternsDeclaration (TypeInstanceRef ss' ident ns) - = [ EDInstance tcdClassName (lookupRenamedIdent ident) tcdForAll tcdInstanceKinds tcdInstanceTypes tcdDependencies tcdChain tcdIndex ns ss' - | m1 <- maybeToList (M.lookup (ByModuleName mn) (typeClassDictionaries env)) - , m2 <- M.elems m1 - , nel <- maybeToList (M.lookup (Qualified (ByModuleName mn) ident) m2) - , TypeClassDictionaryInScope{..} <- NEL.toList nel - ] - toExternsDeclaration _ = [] - - renameRef :: DeclarationRef -> DeclarationRef - renameRef = \case - ValueRef ss' ident -> ValueRef ss' $ lookupRenamedIdent ident - TypeInstanceRef ss' ident _ | not $ isPlainIdent ident -> TypeInstanceRef ss' (lookupRenamedIdent ident) CompilerNamed - other -> other - - lookupRenamedIdent :: Ident -> Ident - lookupRenamedIdent = flip (join M.findWithDefault) renamedIdents - -externsFileName :: FilePath -externsFileName = "externs.cbor" diff --git a/claude-help/original-compiler/src/Language/PureScript/Glob.hs b/claude-help/original-compiler/src/Language/PureScript/Glob.hs deleted file mode 100644 index 3493cd96..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Glob.hs +++ /dev/null @@ -1,44 +0,0 @@ -module Language.PureScript.Glob where - -import Prelude - -import Control.Monad (when) -import Data.List (nub, (\\)) -import Data.Text qualified as T -import System.FilePath.Glob (glob) -import System.IO (hPutStrLn, stderr) -import System.IO.UTF8 (readUTF8FileT) - -data PSCGlobs = PSCGlobs - { pscInputGlobs :: [FilePath] - , pscInputGlobsFromFile :: Maybe FilePath - , pscExcludeGlobs :: [FilePath] - , pscWarnFileTypeNotFound :: FilePath -> IO () - } - -toInputGlobs :: PSCGlobs -> IO [FilePath] -toInputGlobs (PSCGlobs {..}) = do - globsFromFile <- inputGlobsFromFile pscInputGlobsFromFile - included <- globWarningOnMisses pscWarnFileTypeNotFound $ nub $ pscInputGlobs <> globsFromFile - excluded <- globWarningOnMisses pscWarnFileTypeNotFound pscExcludeGlobs - pure $ included \\ excluded - -inputGlobsFromFile :: Maybe FilePath -> IO [FilePath] -inputGlobsFromFile globsFromFile = do - mbInputsFromFile <- traverse readUTF8FileT globsFromFile - let - excludeBlankLines = not . T.null . T.strip - excludeComments = not . T.isPrefixOf "#" - toInputs = map (T.unpack . T.strip) . filter (\x -> excludeBlankLines x && excludeComments x) . T.lines - pure $ foldMap toInputs mbInputsFromFile - -globWarningOnMisses :: (String -> IO ()) -> [FilePath] -> IO [FilePath] -globWarningOnMisses warn = foldMap globWithWarning - where - globWithWarning pattern' = do - paths <- glob pattern' - when (null paths) $ warn pattern' - return paths - -warnFileTypeNotFound :: String -> String -> IO () -warnFileTypeNotFound pursCmd = hPutStrLn stderr . ("purs " <> pursCmd <> ": No files found using pattern: " ++) diff --git a/claude-help/original-compiler/src/Language/PureScript/Graph.hs b/claude-help/original-compiler/src/Language/PureScript/Graph.hs deleted file mode 100644 index fc2ae68f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Graph.hs +++ /dev/null @@ -1,58 +0,0 @@ -module Language.PureScript.Graph (graph) where - -import Prelude - -import Data.Aeson qualified as Json -import Data.Aeson.Key qualified as Json.Key -import Data.Aeson.KeyMap qualified as Json.Map -import Data.Map qualified as Map - -import Control.Monad (forM) -import Data.Aeson ((.=)) -import Data.Foldable (foldl') -import Data.Map (Map) -import Data.Maybe (fromMaybe) -import Data.Text (Text) -import System.IO.UTF8 (readUTF8FileT) - -import Language.PureScript.Crash qualified as Crash -import Language.PureScript.CST qualified as CST -import Language.PureScript.Make qualified as Make -import Language.PureScript.ModuleDependencies qualified as Dependencies -import Language.PureScript.Options qualified as Options - -import Language.PureScript.Errors (MultipleErrors) -import Language.PureScript.Names (ModuleName, runModuleName) - - --- | Given a set of filepaths, try to build the dependency graph and return --- that as its JSON representation (or a bunch of errors, if any) -graph :: [FilePath] -> IO (Either MultipleErrors Json.Value, MultipleErrors) -graph input = do - moduleFiles <- readInput input - Make.runMake Options.defaultOptions $ do - ms <- CST.parseModulesFromFiles id moduleFiles - let parsedModuleSig = Dependencies.moduleSignature . CST.resPartial - (_sorted, moduleGraph) <- Dependencies.sortModules Dependencies.Direct (parsedModuleSig . snd) ms - let pathMap = Map.fromList $ - map (\(p, m) -> (Dependencies.sigModuleName (parsedModuleSig m), p)) ms - pure (moduleGraphToJSON pathMap moduleGraph) - -moduleGraphToJSON - :: Map ModuleName FilePath - -> Dependencies.ModuleGraph - -> Json.Value -moduleGraphToJSON paths = Json.Object . foldl' insert mempty - where - insert :: Json.Object -> (ModuleName, [ModuleName]) -> Json.Object - insert obj (mn, depends) = Json.Map.insert (Json.Key.fromText (runModuleName mn)) value obj - where - path = fromMaybe (Crash.internalError "missing module name in graph") $ Map.lookup mn paths - value = Json.object - [ "path" .= path - , "depends" .= fmap runModuleName depends - ] - -readInput :: [FilePath] -> IO [(FilePath, Text)] -readInput inputFiles = - forM inputFiles $ \inFile -> (inFile, ) <$> readUTF8FileT inFile diff --git a/claude-help/original-compiler/src/Language/PureScript/Hierarchy.hs b/claude-help/original-compiler/src/Language/PureScript/Hierarchy.hs deleted file mode 100644 index c4919fb6..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Hierarchy.hs +++ /dev/null @@ -1,85 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Hierarchy --- Copyright : (c) Hardy Jones 2014 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Hardy Jones --- Stability : experimental --- Portability : --- --- | --- Generate Directed Graphs of PureScript TypeClasses --- ------------------------------------------------------------------------------ - -module Language.PureScript.Hierarchy where - -import Prelude -import Protolude (ordNub) - -import Data.List (sort) -import Data.Text qualified as T -import Language.PureScript qualified as P - -newtype SuperMap = SuperMap - { _unSuperMap :: Either (P.ProperName 'P.ClassName) (P.ProperName 'P.ClassName, P.ProperName 'P.ClassName) - } - deriving Eq - -instance Ord SuperMap where - compare (SuperMap s) (SuperMap s') = getCls s `compare` getCls s' - where - getCls = either id snd - -data Graph = Graph - { graphName :: GraphName - , digraph :: Digraph - } - deriving (Eq, Show) - -newtype GraphName = GraphName - { _unGraphName :: T.Text - } - deriving (Eq, Show) - -newtype Digraph = Digraph - { _unDigraph :: T.Text - } - deriving (Eq, Show) - -prettyPrint :: SuperMap -> T.Text -prettyPrint (SuperMap (Left sub)) = " " <> P.runProperName sub <> ";" -prettyPrint (SuperMap (Right (super, sub))) = - " " <> P.runProperName super <> " -> " <> P.runProperName sub <> ";" - -runModuleName :: P.ModuleName -> GraphName -runModuleName (P.ModuleName name) = - GraphName $ T.replace "." "_" name - -typeClasses :: Functor f => f P.Module -> f (Maybe Graph) -typeClasses = - fmap typeClassGraph - -typeClassGraph :: P.Module -> Maybe Graph -typeClassGraph (P.Module _ _ moduleName decls _) = - if null supers then Nothing else Just (Graph name graph) - where - name = runModuleName moduleName - supers = sort . ordNub $ concatMap superClasses decls - graph = Digraph $ typeClassPrologue name <> typeClassBody supers <> typeClassEpilogue - -typeClassPrologue :: GraphName -> T.Text -typeClassPrologue (GraphName name) = "digraph " <> name <> " {\n" - -typeClassBody :: [SuperMap] -> T.Text -typeClassBody supers = T.intercalate "\n" (prettyPrint <$> supers) - -typeClassEpilogue :: T.Text -typeClassEpilogue = "\n}" - -superClasses :: P.Declaration -> [SuperMap] -superClasses (P.TypeClassDeclaration _ sub _ supers@(_:_) _ _) = - fmap (\(P.Constraint _ (P.Qualified _ super) _ _ _) -> SuperMap (Right (super, sub))) supers -superClasses (P.TypeClassDeclaration _ sub _ _ _ _) = [SuperMap (Left sub)] -superClasses _ = [] diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide.hs b/claude-help/original-compiler/src/Language/PureScript/Ide.hs deleted file mode 100644 index 4412c12d..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide.hs +++ /dev/null @@ -1,307 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide --- Description : Interface for the psc-ide-server --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Interface for the psc-ide-server ------------------------------------------------------------------------------ - -{-# LANGUAGE PackageImports #-} - -module Language.PureScript.Ide - ( handleCommand - ) where - -import Protolude hiding (moduleName) - -import Language.PureScript.Ide.Imports qualified as IDEImports -import "monad-logger" Control.Monad.Logger (MonadLogger, logWarnN) -import Data.Map qualified as Map -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.Glob (toInputGlobs, PSCGlobs(..)) -import Language.PureScript.Ide.CaseSplit qualified as CS -import Language.PureScript.Ide.Command (Command(..), ImportCommand(..), ListType(..)) -import Language.PureScript.Ide.Completion (CompletionOptions (coMaxResults), completionFromMatch, defaultCompletionOptions, getCompletions, getExactCompletions, simpleExport) -import Language.PureScript.Ide.Error (IdeError(..)) -import Language.PureScript.Ide.Externs (readExternFile) -import Language.PureScript.Ide.Filter qualified as F -import Language.PureScript.Ide.Imports (parseImportsFromFile) -import Language.PureScript.Ide.Imports.Actions (addImplicitImport, addImportForIdentifier, addQualifiedImport, answerRequest) -import Language.PureScript.Ide.Matcher (Matcher, Matcher' (..)) -import Language.PureScript.Ide.Prim (idePrimDeclarations) -import Language.PureScript.Ide.Rebuild (rebuildFileAsync, rebuildFileSync) -import Language.PureScript.Ide.SourceFile (parseModulesFromFiles) -import Language.PureScript.Ide.State (getAllModules, getLoadedModulenames, insertExterns, insertModule, populateVolatileState, populateVolatileStateSync, resetIdeState, getSqliteFilePath, runQuery, escapeSQL) -import Language.PureScript.Ide.Types (Annotation(..), Ide, IdeConfiguration(..), IdeDeclarationAnn(..), IdeEnvironment(..), Success(..), Completion (..), toText, Match (..)) -import Language.PureScript.Ide.Util (discardAnn, identifierFromIdeDeclaration, namespaceForDeclaration, withEmptyAnn) -import Language.PureScript.Ide.Usage (findUsages) -import System.Directory (getCurrentDirectory, getDirectoryContents, doesDirectoryExist, doesFileExist) -import System.FilePath ((), normalise) -import Language.PureScript.Names (ModuleName(ModuleName)) -import Language.PureScript.AST.SourcePos (SourceSpan(SourceSpan)) -import Language.PureScript.Errors (SourcePos(..)) -import Database.SQLite.Simple qualified as SQLite -import Language.PureScript (cacheDbFile, runModuleName) -import Debug.Trace qualified as Debug -import Data.Maybe (catMaybes) -import Protolude (head) -import Data.Foldable (find, Foldable (toList, foldMap)) -import Data.Text qualified -import Data.Either (isLeft) -import Codec.Serialise (deserialise) -import Data.ByteString.Lazy qualified -import Database.SQLite.Simple (Only(Only)) -import Database.SQLite.Simple.ToField (ToField(..)) -import Language.PureScript.Ide.Filter.Declaration (declarationTypeToText) -import Data.ByteString.Lazy qualified as Lazy -import Data.Aeson qualified as Aeson - --- | Accepts a Command and runs it against psc-ide's State. This is the main --- entry point for the server. -handleCommand - :: (Ide m, MonadLogger m, MonadError IdeError m) - => Command - -> m Success -handleCommand c = case c of - Load [] -> - -- Clearing the State before populating it to avoid a space leak - pure $ TextResult "Done" - -- resetIdeState *> findAvailableExterns >>= loadModulesAsync - Load modules -> - pure $ TextResult "Done" - -- loadModulesAsync modules - LoadSync [] -> - pure $ TextResult "Done" - -- findAvailableExterns >>= loadModulesSync - LoadSync modules -> - pure $ TextResult "Done" - -- loadModulesSync modules - Type search filters currentModule -> - findDeclarations (F.Filter (Right $ F.Exact search) : filters) currentModule Nothing - Complete filters matcher currentModule complOptions -> do - - findDeclarations (filters <> foldMap (\case - Flex q -> [F.Filter (Right $ F.Prefix q)] - Distance q _ -> [F.Filter (Right $ F.Prefix q)]) matcher) currentModule (Just complOptions) - -- findCompletions' filters matcher currentModule complOptions - List LoadedModules -> do - logWarnN - "Listing the loaded modules command is DEPRECATED, use the completion command and filter it to modules instead" - ModuleList . join <$> runQuery "select module_name from modules" - List AvailableModules -> - ModuleList . join <$> runQuery "select module_name from modules" - List (Imports fp) -> - ImportList <$> parseImportsFromFile fp - CaseSplit l b e wca t -> - caseSplit l b e wca t - AddClause l wca -> - MultilineTextResult <$> CS.addClause l wca - FindUsages moduleName ident namespace -> do - r :: [Only Lazy.ByteString] <- runQuery $ unlines - [ "select distinct a.span" - , "from dependencies d join asts a on d.module_name = a.module_name" - , "where (d.dependency = '" <> runModuleName moduleName <> "' or d.module_name = '" <> runModuleName moduleName <> "') and a.name = '" <> ident <> "'" - ] - - pure $ UsagesResult (mapMaybe (\(Only span) -> Aeson.decode span) r) - - - -- Map.lookup moduleName <$> getAllModules Nothing >>= \case - -- Nothing -> throwError (GeneralError "Module not found") - -- Just decls -> do - -- case find (\d -> namespaceForDeclaration (discardAnn d) == namespace - -- && identifierFromIdeDeclaration (discardAnn d) == ident) decls of - -- Nothing -> throwError (GeneralError "Declaration not found") - -- Just declaration -> do - -- let sourceModule = fromMaybe moduleName (declaration & _idaAnnotation & _annExportedFrom) - -- UsagesResult . foldMap toList <$> findUsages (discardAnn declaration) sourceModule - Import fp outfp _ (AddImplicitImport mn) -> do - rs <- addImplicitImport fp mn - answerRequest outfp rs - Import fp outfp _ (AddQualifiedImport mn qual) -> do - rs <- addQualifiedImport fp mn qual - answerRequest outfp rs - Import fp outfp filters (AddImportForIdentifier ident qual) -> do - rs <- addImportForIdentifier fp ident qual filters - case rs of - Right rs' -> answerRequest outfp rs' - Left question -> - pure (CompletionResult (map (completionFromMatch . simpleExport . map withEmptyAnn) question)) - Rebuild file actualFile targets -> - rebuildFileAsync file actualFile targets - RebuildSync file actualFile targets -> - rebuildFileSync file actualFile targets - Cwd -> - TextResult . T.pack <$> liftIO getCurrentDirectory - Reset -> - resetIdeState $> TextResult "State has been reset." - Quit -> - liftIO exitSuccess - -findCompletions - :: Ide m - => [F.Filter] - -> Matcher IdeDeclarationAnn - -> Maybe P.ModuleName - -> CompletionOptions - -> m Success -findCompletions filters matcher currentModule complOptions = do - modules <- getAllModules currentModule - let insertPrim = Map.union idePrimDeclarations - pure (CompletionResult (getCompletions filters matcher complOptions (insertPrim modules))) - -findDeclarations - :: Ide m - => [F.Filter] - -> Maybe P.ModuleName - -> Maybe CompletionOptions - -> m Success -findDeclarations filters currentModule completionOptions = do - rows :: [(Text, Lazy.ByteString)] <- runQuery $ - "select module_name, declaration " <> - "from ide_declarations id " <> - ( - mapMaybe (\case - F.Filter (Left modules) -> - Just $ "(exists (select 1 from exports e where id.module_name = e.defined_in and id.name = e.name and id.declaration_type = e.declaration_type and e.module_name in (" <> - T.intercalate "," (toList modules <&> runModuleName <&> \m -> "'" <> escapeSQL m <> "'") <> - "))" <> - " or " <> "id.module_name in (" <> T.intercalate "," (toList modules <&> runModuleName <&> \m -> "'" <> escapeSQL m <> "'") <> "))" - F.Filter (Right (F.Prefix "")) -> Nothing - F.Filter (Right (F.Prefix f)) -> Just $ "id.name glob '" <> escapeSQL f <> "*'" - F.Filter (Right (F.Exact f)) -> Just $ "id.name glob '" <> escapeSQL f <> "'" - F.Filter (Right (F.Namespace namespaces)) -> - Just $ "id.namespace in (" <> T.intercalate "," (toList namespaces <&> \n -> "'" <> toText n <> "'") <> ")" - F.Filter (Right (F.DeclType dt)) -> - Just $ "id.namespace in (" <> T.intercalate "," (toList dt <&> \t -> "'" <> declarationTypeToText t <> "'") <> ")" - F.Filter (Right (F.Dependencies qualifier _ imports@(_:_))) -> - Just $ "(exists (select 1 from exports e where id.module_name = e.defined_in and id.name = e.name and id.declaration_type = e.declaration_type and e.module_name in " - <> moduleNames <> ") or id.module_name in" <> moduleNames <> ")" - where - moduleNames = " (" <> - T.intercalate "," (filter (\(IDEImports.Import _ _ qualified) -> case qualifier of - Nothing -> True - Just qual -> Just qual == qualified - ) imports <&> \(IDEImports.Import m _ _)-> "'" <> escapeSQL (runModuleName m) <> "'") <> ") " - F.Filter _ -> Nothing - ) - filters - & \f -> if null f then " " else " where " <> T.intercalate " and " f - ) <> - foldMap (\maxResults -> " limit " <> show maxResults ) (coMaxResults =<< completionOptions) - - -- Fallback to volatile state if SQLite returns no results - if null rows - then do - modules <- getAllModules currentModule - let insertPrim = Map.union idePrimDeclarations - -- Extract the search term from the filters - let searchTerm = case filters of - (F.Filter (Right (F.Exact term)) : _) -> term - (F.Filter (Right (F.Prefix term)) : _) -> term - _ -> "" - let results = getExactCompletions searchTerm filters (insertPrim modules) - pure (CompletionResult (take (fromMaybe 100 (coMaxResults =<< completionOptions)) results)) - else do - let matches = rows <&> \(m, decl) -> (Match (ModuleName m, deserialise decl), []) - pure $ CompletionResult $ completionFromMatch <$> matches - -sqliteFile :: Ide m => m FilePath -sqliteFile = outputDirectory <&> ( "cache.db") - -outputDirectory :: Ide m => m FilePath -outputDirectory = do - outputPath <- confOutputPath . ideConfiguration <$> ask - cwd <- liftIO getCurrentDirectory - pure (cwd outputPath) - -caseSplit :: (Ide m, MonadError IdeError m) => - Text -> Int -> Int -> CS.WildcardAnnotations -> Text -> m Success -caseSplit l b e csa t = do - patterns <- CS.makePattern l b e csa <$> CS.caseSplit t - pure (MultilineTextResult patterns) - --- | Finds all the externs inside the output folder and returns the --- corresponding module names -findAvailableExterns :: (Ide m, MonadError IdeError m) => m [P.ModuleName] -findAvailableExterns = do - oDir <- outputDirectory - unlessM (liftIO (doesDirectoryExist oDir)) - (throwError (GeneralError $ "Couldn't locate your output directory at: " <> T.pack (normalise oDir))) - liftIO $ do - directories <- getDirectoryContents oDir - moduleNames <- filterM (containsExterns oDir) directories - pure (P.moduleNameFromString . toS <$> moduleNames) - where - -- Takes the output directory and a filepath like "Data.Array" and - -- looks up, whether that folder contains an externs file - containsExterns :: FilePath -> FilePath -> IO Bool - containsExterns oDir d - | d `elem` [".", ".."] = pure False - | otherwise = do - let file = oDir d P.externsFileName - doesFileExist file - --- | Finds all matches for the globs specified at the commandline -findAllSourceFiles :: Ide m => m [FilePath] -findAllSourceFiles = do - IdeConfiguration{..} <- ideConfiguration <$> ask - liftIO $ toInputGlobs $ PSCGlobs - { pscInputGlobs = confGlobs - , pscInputGlobsFromFile = confGlobsFromFile - , pscExcludeGlobs = confGlobsExclude - , pscWarnFileTypeNotFound = const $ pure () - } - - --- | Looks up the ExternsFiles for the given Modulenames and loads them into the --- server state. Then proceeds to parse all the specified sourcefiles and --- inserts their ASTs into the state. Finally kicks off an async worker, which --- populates the VolatileState. -loadModulesAsync - :: (Ide m, MonadError IdeError m, MonadLogger m) - => [P.ModuleName] - -> m Success -loadModulesAsync moduleNames = do - tr <- loadModules moduleNames - _ <- populateVolatileState - pure tr - -loadModulesSync - :: (Ide m, MonadError IdeError m, MonadLogger m) - => [P.ModuleName] - -> m Success -loadModulesSync moduleNames = do - tr <- loadModules moduleNames - populateVolatileStateSync - pure tr - -loadModules - :: (Ide m, MonadError IdeError m, MonadLogger m) - => [P.ModuleName] - -> m Success -loadModules moduleNames = do - -- We resolve all the modulenames to externs files and load these into memory. - oDir <- outputDirectory - let efPaths = - map (\mn -> oDir toS (P.runModuleName mn) P.externsFileName) moduleNames - efiles <- traverse readExternFile efPaths - traverse_ insertExterns efiles - - -- We parse all source files, log eventual parse failures and insert the - -- successful parses into the state. - (failures, allModules) <- - partitionEithers <$> (parseModulesFromFiles =<< findAllSourceFiles) - unless (null failures) $ - logWarnN ("Failed to parse: " <> show failures) - traverse_ insertModule allModules - - pure (TextResult ("Loaded " <> show (length efiles) <> " modules and " - <> show (length allModules) <> " source files.")) diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/CaseSplit.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/CaseSplit.hs deleted file mode 100644 index 8c66f554..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/CaseSplit.hs +++ /dev/null @@ -1,155 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.CaseSplit --- Description : Casesplitting and adding function clauses --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Casesplitting and adding function clauses ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.CaseSplit - ( WildcardAnnotations() - , explicitAnnotations - , noAnnotations - , makePattern - , addClause - , caseSplit - ) where - -import Protolude hiding (Constructor) - -import Data.List.NonEmpty qualified as NE -import Data.Map qualified as M -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.CST qualified as CST - -import Language.PureScript.Externs (ExternsDeclaration(..), ExternsFile(..)) -import Language.PureScript.Ide.Error (IdeError(..)) -import Language.PureScript.Ide.State (cachedRebuild, getExternFiles) -import Language.PureScript.Ide.Types (Ide) - -type Constructor = (P.ProperName 'P.ConstructorName, [P.SourceType]) - -newtype WildcardAnnotations = WildcardAnnotations Bool - -explicitAnnotations :: WildcardAnnotations -explicitAnnotations = WildcardAnnotations True - -noAnnotations :: WildcardAnnotations -noAnnotations = WildcardAnnotations False - -type DataType = ([(Text, Maybe P.SourceType, P.Role)], [(P.ProperName 'P.ConstructorName, [P.SourceType])]) - -caseSplit - :: (Ide m, MonadError IdeError m) - => Text - -> m [Constructor] -caseSplit q = do - type' <- parseType' q - (tc, args) <- splitTypeConstructor type' - (typeVars, ctors) <- findTypeDeclaration tc - let applyTypeVars = P.everywhereOnTypes (P.replaceAllTypeVars (zip (map (\(name, _, _) -> name) typeVars) args)) - let appliedCtors = map (second (map applyTypeVars)) ctors - pure appliedCtors - -findTypeDeclaration - :: (Ide m, MonadError IdeError m) - => P.ProperName 'P.TypeName - -> m DataType -findTypeDeclaration q = do - efs <- getExternFiles - efs' <- maybe efs (flip (uncurry M.insert) efs) <$> cachedRebuild - let m = getFirst $ foldMap (findTypeDeclaration' q) efs' - case m of - Just mn -> pure mn - Nothing -> throwError (GeneralError "Not Found") - -findTypeDeclaration' - :: P.ProperName 'P.TypeName - -> ExternsFile - -> First DataType -findTypeDeclaration' t ExternsFile{..} = - First $ head $ mapMaybe (\case - EDType tn _ (P.DataType _ typeVars ctors) - | tn == t -> Just (typeVars, ctors) - _ -> Nothing) efDeclarations - -splitTypeConstructor :: (MonadError IdeError m) => - P.Type a -> m (P.ProperName 'P.TypeName, [P.Type a]) -splitTypeConstructor = go [] - where - go acc (P.TypeApp _ ty arg) = go (arg : acc) ty - go acc (P.TypeConstructor _ tc) = pure (P.disqualify tc, acc) - go _ _ = throwError (GeneralError "Failed to read TypeConstructor") - -prettyCtor :: WildcardAnnotations -> Constructor -> Text -prettyCtor _ (ctorName, []) = P.runProperName ctorName -prettyCtor wsa (ctorName, ctorArgs) = - "(" <> P.runProperName ctorName <> " " - <> T.unwords (map (prettyPrintWildcard wsa) ctorArgs) <> ")" - -prettyPrintWildcard :: WildcardAnnotations -> P.Type a -> Text -prettyPrintWildcard (WildcardAnnotations True) = prettyWildcard -prettyPrintWildcard (WildcardAnnotations False) = const "_" - -prettyWildcard :: P.Type a -> Text -prettyWildcard t = "( _ :: " <> T.strip (T.pack (P.prettyPrintTypeAtom maxBound t)) <> ")" - --- | Constructs Patterns to insert into a sourcefile -makePattern :: Text -- ^ Current line - -> Int -- ^ Begin of the split - -> Int -- ^ End of the split - -> WildcardAnnotations -- ^ Whether to explicitly type the splits - -> [Constructor] -- ^ Constructors to split - -> [Text] -makePattern t x y wsa = makePattern' (T.take x t) (T.drop y t) - where - makePattern' lhs rhs = map (\ctor -> lhs <> prettyCtor wsa ctor <> rhs) - -addClause :: (MonadError IdeError m) => Text -> WildcardAnnotations -> m [Text] -addClause s wca = do - (fName, fType) <- parseTypeDeclaration' s - let args = splitFunctionType fType - template = P.runIdent fName <> " " <> - T.unwords (map (prettyPrintWildcard wca) args) <> - " = ?" <> (T.strip . P.runIdent $ fName) - pure [s, template] - -parseType' :: (MonadError IdeError m) => - Text -> m P.SourceType -parseType' s = - case CST.runTokenParser CST.parseType $ CST.lex s of - Right type' -> pure $ CST.convertType "" $ snd type' - Left err -> - throwError (GeneralError ("Parsing the splittype failed with:" - <> show err)) - -parseTypeDeclaration' :: (MonadError IdeError m) => Text -> m (P.Ident, P.SourceType) -parseTypeDeclaration' s = - let x = fmap (CST.convertDeclaration "" . snd) - $ CST.runTokenParser CST.parseDecl - $ CST.lex s - in - case x of - Right [P.TypeDeclaration td] -> pure (P.unwrapTypeDeclaration td) - Right _ -> throwError (GeneralError "Found a non-type-declaration") - Left errs -> - throwError (GeneralError ("Parsing the type signature failed with: " - <> toS (CST.prettyPrintErrorMessage $ NE.head errs))) - -splitFunctionType :: P.Type a -> [P.Type a] -splitFunctionType t = fromMaybe [] arguments - where - arguments = initMay splitted - splitted = splitType' t - splitType' (P.ForAll _ _ _ _ t' _) = splitType' t' - splitType' (P.ConstrainedType _ _ t') = splitType' t' - splitType' (P.TypeApp _ (P.TypeApp _ t' lhs) rhs) - | P.eqType t' P.tyFunction = lhs : splitType' rhs - splitType' t' = [t'] diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Command.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Command.hs deleted file mode 100644 index b69394e7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Command.hs +++ /dev/null @@ -1,189 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Command --- Description : Datatypes for the commands psc-ide accepts --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Datatypes for the commands psc-ide accepts ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Command where - -import Protolude - -import Control.Monad.Fail (fail) -import Data.Aeson (FromJSON(..), withObject, (.!=), (.:), (.:?)) -import Data.Map qualified as Map -import Data.Set qualified as Set -import Language.PureScript qualified as P -import Language.PureScript.Ide.CaseSplit (WildcardAnnotations, explicitAnnotations, noAnnotations) -import Language.PureScript.Ide.Completion (CompletionOptions, defaultCompletionOptions) -import Language.PureScript.Ide.Filter (Filter) -import Language.PureScript.Ide.Matcher (Matcher, Matcher') -import Language.PureScript.Ide.Types (IdeDeclarationAnn, IdeNamespace) - -data Command - = Load [P.ModuleName] - | LoadSync [P.ModuleName] -- used in tests - | Type - { typeSearch :: Text - , typeFilters :: [Filter] - , typeCurrentModule :: Maybe P.ModuleName - } - | Complete - { completeFilters :: [Filter] - , completeMatcher :: Maybe Matcher' - , completeCurrentModule :: Maybe P.ModuleName - , completeOptions :: CompletionOptions - } - | CaseSplit - { caseSplitLine :: Text - , caseSplitBegin :: Int - , caseSplitEnd :: Int - , caseSplitAnnotations :: WildcardAnnotations - , caseSplitType :: Text - } - | AddClause - { addClauseLine :: Text - , addClauseAnnotations :: WildcardAnnotations - } - | FindUsages - { usagesModule :: P.ModuleName - , usagesIdentifier :: Text - , usagesNamespace :: IdeNamespace - } - -- Import InputFile OutputFile - | Import FilePath (Maybe FilePath) [Filter] ImportCommand - | List { listType :: ListType } - | Rebuild FilePath (Maybe FilePath) (Set P.CodegenTarget) - | RebuildSync FilePath (Maybe FilePath) (Set P.CodegenTarget) - | Cwd - | Reset - | Quit - -commandName :: Command -> Text -commandName c = case c of - Load{} -> "Load" - LoadSync{} -> "LoadSync" - Type{} -> "Type" - Complete{} -> "Complete" - CaseSplit{} -> "CaseSplit" - AddClause{} -> "AddClause" - FindUsages{} -> "FindUsages" - Import{} -> "Import" - List{} -> "List" - Rebuild{} -> "Rebuild" - RebuildSync{} -> "RebuildSync" - Cwd{} -> "Cwd" - Reset{} -> "Reset" - Quit{} -> "Quit" - -data ImportCommand - = AddImplicitImport P.ModuleName - | AddQualifiedImport P.ModuleName P.ModuleName - | AddImportForIdentifier Text (Maybe P.ModuleName) - deriving (Show, Eq) - -instance FromJSON ImportCommand where - parseJSON = withObject "ImportCommand" $ \o -> do - (command :: Text) <- o .: "importCommand" - case command of - "addImplicitImport" -> - AddImplicitImport <$> (P.moduleNameFromString <$> o .: "module") - "addQualifiedImport" -> - AddQualifiedImport - <$> (P.moduleNameFromString <$> o .: "module") - <*> (P.moduleNameFromString <$> o .: "qualifier") - "addImport" -> - AddImportForIdentifier - <$> (o .: "identifier") - <*> (fmap P.moduleNameFromString <$> o .:? "qualifier") - - s -> fail ("Unknown import command: " <> show s) - -data ListType = LoadedModules | Imports FilePath | AvailableModules - -instance FromJSON ListType where - parseJSON = withObject "ListType" $ \o -> do - (listType' :: Text) <- o .: "type" - case listType' of - "import" -> Imports <$> o .: "file" - "loadedModules" -> pure LoadedModules - "availableModules" -> pure AvailableModules - s -> fail ("Unknown list type: " <> show s) - -instance FromJSON Command where - parseJSON = withObject "command" $ \o -> do - (command :: Text) <- o .: "command" - case command of - "list" -> List <$> o .:? "params" .!= LoadedModules - "cwd" -> pure Cwd - "quit" -> pure Quit - "reset" -> pure Reset - "load" -> do - params' <- o .:? "params" - case params' of - Nothing -> pure (Load []) - Just params -> - Load <$> (map P.moduleNameFromString <$> params .:? "modules" .!= []) - "type" -> do - params <- o .: "params" - Type - <$> params .: "search" - <*> params .:? "filters" .!= [] - <*> (fmap P.moduleNameFromString <$> params .:? "currentModule") - "complete" -> do - params <- o .: "params" - Complete - <$> params .:? "filters" .!= [] - <*> params .:? "matcher" .!= Nothing - <*> (fmap P.moduleNameFromString <$> params .:? "currentModule") - <*> params .:? "options" .!= defaultCompletionOptions - "caseSplit" -> do - params <- o .: "params" - CaseSplit - <$> params .: "line" - <*> params .: "begin" - <*> params .: "end" - <*> (mkAnnotations <$> params .: "annotations") - <*> params .: "type" - "addClause" -> do - params <- o .: "params" - AddClause - <$> params .: "line" - <*> (mkAnnotations <$> params .: "annotations") - "usages" -> do - params <- o .: "params" - FindUsages - <$> map P.moduleNameFromString (params .: "module") - <*> params .: "identifier" - <*> params .: "namespace" - "import" -> do - params <- o .: "params" - Import - <$> params .: "file" - <*> params .:? "outfile" - <*> params .:? "filters" .!= [] - <*> params .: "importCommand" - "rebuild" -> do - params <- o .: "params" - Rebuild - <$> params .: "file" - <*> params .:? "actualFile" - <*> (parseCodegenTargets =<< params .:? "codegen" .!= [ "js" ]) - c -> fail ("Unknown command: " <> show c) - where - parseCodegenTargets ts = - case traverse (\t -> Map.lookup t P.codegenTargets) ts of - Nothing -> - fail ("Failed to parse codegen targets: " <> show ts) - Just ts' -> - pure (Set.fromList ts') - - mkAnnotations True = explicitAnnotations - mkAnnotations False = noAnnotations diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Completion.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Completion.hs deleted file mode 100644 index 87fe81de..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Completion.hs +++ /dev/null @@ -1,141 +0,0 @@ -module Language.PureScript.Ide.Completion - ( getCompletions - , getExactMatches - , getExactCompletions - , simpleExport - , completionFromMatch - , CompletionOptions(..) - , defaultCompletionOptions - , applyCompletionOptions - ) where - -import Protolude hiding ((<&>), moduleName) - -import Control.Lens ((.~), (<&>), (^.)) -import Data.Aeson (FromJSON(..), withObject, (.!=), (.:?)) -import Data.Map qualified as Map -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.Ide.Error (prettyPrintTypeSingleLine) -import Language.PureScript.Ide.Filter (Filter, applyFilters, exactFilter) -import Language.PureScript.Ide.Matcher (Matcher, runMatcher) -import Language.PureScript.Ide.Types -import Language.PureScript.Ide.Util (identT, identifierFromIdeDeclaration, namespaceForDeclaration, properNameT, typeOperatorAliasT, valueOperatorAliasT) - --- | Applies the CompletionFilters and the Matcher to the given Modules --- and sorts the found Completions according to the Matching Score -getCompletions - :: [Filter] - -> Matcher IdeDeclarationAnn - -> CompletionOptions - -> ModuleMap [IdeDeclarationAnn] - -> [Completion] -getCompletions filters matcher options modules = - modules - & applyFilters filters - & matchesFromModules - & runMatcher matcher - & applyCompletionOptions options - <&> completionFromMatch - -getExactMatches :: Text -> [Filter] -> ModuleMap [IdeDeclarationAnn] -> [Match IdeDeclarationAnn] -getExactMatches search filters modules = - modules - & applyFilters (exactFilter search : filters) - & matchesFromModules - -getExactCompletions :: Text -> [Filter] -> ModuleMap [IdeDeclarationAnn] -> [Completion] -getExactCompletions search filters modules = - modules - & getExactMatches search filters - <&> simpleExport - <&> completionFromMatch - -matchesFromModules :: ModuleMap [IdeDeclarationAnn] -> [Match IdeDeclarationAnn] -matchesFromModules = Map.foldMapWithKey completionFromModule - where - completionFromModule moduleName = - map $ \x -> Match (moduleName, x) - -data CompletionOptions = CompletionOptions - { coMaxResults :: Maybe Int - , coGroupReexports :: Bool - } - -instance FromJSON CompletionOptions where - parseJSON = withObject "CompletionOptions" $ \o -> do - maxResults <- o .:? "maxResults" - groupReexports <- o .:? "groupReexports" .!= False - pure (CompletionOptions { coMaxResults = maxResults - , coGroupReexports = groupReexports - }) - -defaultCompletionOptions :: CompletionOptions -defaultCompletionOptions = CompletionOptions { coMaxResults = Nothing, coGroupReexports = False } - -applyCompletionOptions :: CompletionOptions -> [Match IdeDeclarationAnn] -> [(Match IdeDeclarationAnn, [P.ModuleName])] -applyCompletionOptions co decls = decls - & (if coGroupReexports co - then groupCompletionReexports - else map simpleExport) - & maybe identity take (coMaxResults co) - -simpleExport :: Match a -> (Match a, [P.ModuleName]) -simpleExport match@(Match (moduleName, _)) = (match, [moduleName]) - -groupCompletionReexports :: [Match IdeDeclarationAnn] -> [(Match IdeDeclarationAnn, [P.ModuleName])] -groupCompletionReexports initial = - Map.elems (foldr go Map.empty initial) - where - go (Match (moduleName, d@(IdeDeclarationAnn ann decl))) = - let - origin = fromMaybe moduleName (ann ^. annExportedFrom) - in - Map.alter - (insertDeclaration moduleName origin d) - (Namespaced (namespaceForDeclaration decl) - (P.runModuleName origin <> "." <> identifierFromIdeDeclaration decl)) - insertDeclaration moduleName origin d old = case old of - Nothing -> Just ( Match (origin, d & idaAnnotation . annExportedFrom .~ Nothing) - , [moduleName] - ) - Just x -> Just (second (moduleName :) x) - -data Namespaced a = Namespaced IdeNamespace a - deriving (Show, Eq, Ord) - -completionFromMatch :: (Match IdeDeclarationAnn, [P.ModuleName]) -> Completion -completionFromMatch (Match (m, IdeDeclarationAnn ann decl), mns) = - Completion {..} - where - (complIdentifier, complExpandedType) = case decl of - IdeDeclValue v -> (v ^. ideValueIdent . identT, v ^. ideValueType & prettyPrintTypeSingleLine) - IdeDeclType t -> (t ^. ideTypeName . properNameT, t ^. ideTypeKind & prettyPrintTypeSingleLine) - IdeDeclTypeSynonym s -> (s ^. ideSynonymName . properNameT, s ^. ideSynonymType & prettyPrintTypeSingleLine) - IdeDeclDataConstructor d -> (d ^. ideDtorName . properNameT, d ^. ideDtorType & prettyPrintTypeSingleLine) - IdeDeclTypeClass d -> (d ^. ideTCName . properNameT, d ^. ideTCKind & prettyPrintTypeSingleLine) - IdeDeclValueOperator (IdeValueOperator op ref precedence associativity typeP) -> - (P.runOpName op, maybe (showFixity precedence associativity (valueOperatorAliasT ref) op) prettyPrintTypeSingleLine typeP) - IdeDeclTypeOperator (IdeTypeOperator op ref precedence associativity kind) -> - (P.runOpName op, maybe (showFixity precedence associativity (typeOperatorAliasT ref) op) prettyPrintTypeSingleLine kind) - IdeDeclModule mn -> (P.runModuleName mn, "module") - - complExportedFrom = mns - - complModule = P.runModuleName m - - complType = maybe complExpandedType prettyPrintTypeSingleLine (_annTypeAnnotation ann) - - complLocation = _annLocation ann - - complDocumentation = _annDocumentation ann - - complDeclarationType = Just (declarationType decl) - - showFixity p a r o = - let asso = case a of - P.Infix -> "infix" - P.Infixl -> "infixl" - P.Infixr -> "infixr" - in T.unwords [asso, show p, r, "as", P.runOpName o] - diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Error.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Error.hs deleted file mode 100644 index bcd95a77..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Error.hs +++ /dev/null @@ -1,100 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Error --- Description : Error types for psc-ide --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Error types for psc-ide ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Error - ( IdeError(..) - , prettyPrintTypeSingleLine - ) where - -import Data.Aeson (KeyValue(..), ToJSON(..), Value, object) -import Data.Aeson.Types qualified as Aeson -import Data.Aeson.KeyMap qualified as KM -import Data.Text qualified as T -import Protolude -import Language.PureScript.Ide.Types (ModuleIdent, Completion (..)) -import Language.PureScript.Errors qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Pretty qualified as P -import Language.PureScript.Types qualified as P -import Language.PureScript.Errors.JSON (toJSONError) - -data IdeError - = GeneralError Text - | NotFound Text - | ModuleNotFound ModuleIdent - | ModuleFileNotFound ModuleIdent - | RebuildError [(FilePath, Text)] P.MultipleErrors - deriving (Show) - -instance ToJSON IdeError where - toJSON (RebuildError files errs) = object - [ "resultType" .= ("error" :: Text) - , "result" .= encodeRebuildErrors files errs - ] - toJSON err = object - [ "resultType" .= ("error" :: Text) - , "result" .= textError err - ] - -encodeRebuildErrors :: [(FilePath, Text)] -> P.MultipleErrors -> Value -encodeRebuildErrors files = toJSON . map encodeRebuildError . P.runMultipleErrors - where - encodeRebuildError err = case err of - (P.ErrorMessage _ - ((P.HoleInferredType name _ _ - (Just P.TSAfter{tsAfterIdentifiers=idents, tsAfterRecordFields=fields})))) -> - insertTSCompletions name idents (fromMaybe [] fields) (toJSON (toJSONError False P.Error files err)) - _ -> - (toJSON . toJSONError False P.Error files) err - - insertTSCompletions name idents fields (Aeson.Object value) = - Aeson.Object - (KM.insert "pursIde" - (object [ "name" .= name - , "completions" .= ordNub (map identCompletion idents ++ map fieldCompletion fields) - ]) value) - insertTSCompletions _ _ _ v = v - - identCompletion (P.Qualified mn i, ty) = - Completion - { complModule = maybe "" P.runModuleName $ P.toMaybeModuleName mn - , complIdentifier = i - , complType = prettyPrintTypeSingleLine ty - , complExpandedType = prettyPrintTypeSingleLine ty - , complLocation = Nothing - , complDocumentation = Nothing - , complExportedFrom = toList $ P.toMaybeModuleName mn - , complDeclarationType = Nothing - } - fieldCompletion (label, ty) = - Completion - { complModule = "" - , complIdentifier = "_." <> P.prettyPrintLabel label - , complType = prettyPrintTypeSingleLine ty - , complExpandedType = prettyPrintTypeSingleLine ty - , complLocation = Nothing - , complDocumentation = Nothing - , complExportedFrom = [] - , complDeclarationType = Nothing - } - -textError :: IdeError -> Text -textError (GeneralError msg) = msg -textError (NotFound ident) = "Symbol '" <> ident <> "' not found." -textError (ModuleNotFound ident) = "Module '" <> ident <> "' not found." -textError (ModuleFileNotFound ident) = "Extern file for module " <> ident <> " could not be found" -textError (RebuildError _ err) = show err - -prettyPrintTypeSingleLine :: P.Type a -> Text -prettyPrintTypeSingleLine = T.unwords . map T.strip . T.lines . T.pack . P.prettyPrintTypeWithUnicode maxBound diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Externs.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Externs.hs deleted file mode 100644 index 67b973b2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Externs.hs +++ /dev/null @@ -1,145 +0,0 @@ -{-# language PackageImports, BlockArguments #-} - -module Language.PureScript.Ide.Externs - ( readExternFile - , convertExterns - ) where - -import Protolude hiding (to, from, (&)) - -import Codec.CBOR.Term as Term -import Control.Lens (preview, view, (&), (^.)) -import "monad-logger" Control.Monad.Logger (MonadLogger, logErrorN) -import Data.Text qualified as Text -import Language.PureScript.Make.Monad qualified as Make -import Language.PureScript.Ide.Error (IdeError (..)) -import Language.PureScript.Ide.Types (IdeDataConstructor(..), IdeDeclaration(..), IdeDeclarationAnn(..), IdeType(..), IdeTypeClass(..), IdeTypeOperator(..), IdeTypeSynonym(..), IdeValue(..), IdeValueOperator(..), _IdeDeclType, anyOf, emptyAnn, ideTypeKind, ideTypeName) -import Language.PureScript.Ide.Util (properNameT) -import Language.PureScript.Externs qualified as P -import Paths_purescript qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.AST.Declarations qualified as P -import Language.PureScript.Types qualified as P - -readExternFile - :: (MonadIO m, MonadError IdeError m, MonadLogger m) - => FilePath - -> m P.ExternsFile -readExternFile fp = do - externsFile <- liftIO (Make.readCborFileIO fp) - case externsFile of - Just externs | version == P.efVersion externs -> - pure externs - _ -> - liftIO (Make.readCborFileIO fp) >>= \case - Just (Term.TList (_tag : Term.TString efVersion : _rest)) -> do - let errMsg = - "Version mismatch for the externs at: " - <> toS fp - <> " Expected: " <> version - <> " Found: " <> efVersion - logErrorN errMsg - throwError (GeneralError errMsg) - _ -> - throwError (GeneralError ("Parsing the extern at: " <> toS fp <> " failed")) - where - version = toS P.currentVersion - -convertExterns :: P.ExternsFile -> ([IdeDeclarationAnn], [(P.ModuleName, P.DeclarationRef)]) -convertExterns ef = - (decls, exportDecls) - where - decls = moduleDecl : map - (IdeDeclarationAnn emptyAnn) - (resolvedDeclarations <> operatorDecls <> tyOperatorDecls) - exportDecls = mapMaybe convertExport (P.efExports ef) - operatorDecls = convertOperator <$> P.efFixities ef - tyOperatorDecls = convertTypeOperator <$> P.efTypeFixities ef - moduleDecl = IdeDeclarationAnn emptyAnn (IdeDeclModule (P.efModuleName ef)) - (toResolve, declarations) = - second catMaybes (partitionEithers (map convertDecl (P.efDeclarations ef))) - resolvedDeclarations = resolveSynonymsAndClasses toResolve declarations - -resolveSynonymsAndClasses - :: [ToResolve] - -> [IdeDeclaration] - -> [IdeDeclaration] -resolveSynonymsAndClasses trs decls = foldr go decls trs - where - go tr acc = case tr of - TypeClassToResolve tcn -> - case findType (P.coerceProperName tcn) acc of - Nothing -> - acc - Just tyDecl -> IdeDeclTypeClass - (IdeTypeClass tcn (tyDecl ^. ideTypeKind) []) - : filter (not . anyOf (_IdeDeclType . ideTypeName) (== P.coerceProperName tcn)) acc - SynonymToResolve tn ty -> - case findType tn acc of - Nothing -> - acc - Just tyDecl -> - IdeDeclTypeSynonym (IdeTypeSynonym tn ty (tyDecl ^. ideTypeKind)) - : filter (not . anyOf (_IdeDeclType . ideTypeName) (== tn)) acc - -findType :: P.ProperName 'P.TypeName -> [IdeDeclaration] -> Maybe IdeType -findType tn decls = - decls - & mapMaybe (preview _IdeDeclType) - & find ((==) tn . view ideTypeName) - --- The Externs format splits information about synonyms across EDType --- and EDTypeSynonym declarations. For type classes it split them --- across an EDType and an EDClass . We collect these and resolve them --- at the end of the conversion process. -data ToResolve - = TypeClassToResolve (P.ProperName 'P.ClassName) - | SynonymToResolve (P.ProperName 'P.TypeName) P.SourceType - -convertExport :: P.DeclarationRef -> Maybe (P.ModuleName, P.DeclarationRef) -convertExport (P.ReExportRef _ src r) = Just (P.exportSourceDefinedIn src, r) -convertExport _ = Nothing - -convertDecl :: P.ExternsDeclaration -> Either ToResolve (Maybe IdeDeclaration) -convertDecl ed = case ed of - -- We need to filter all types and synonyms that contain a '$' - -- because those are typechecker internal definitions that shouldn't - -- be user facing - P.EDType{..} -> Right do - guard (isNothing (Text.find (== '$') (edTypeName ^. properNameT))) - Just (IdeDeclType (IdeType edTypeName edTypeKind [])) - P.EDTypeSynonym{..} -> - if isNothing (Text.find (== '$') (edTypeSynonymName ^. properNameT)) - then Left (SynonymToResolve edTypeSynonymName edTypeSynonymType) - else Right Nothing - P.EDDataConstructor{..} -> Right do - guard (isNothing (Text.find (== '$') (edDataCtorName ^. properNameT))) - Just - (IdeDeclDataConstructor - (IdeDataConstructor edDataCtorName edDataCtorTypeCtor edDataCtorType)) - P.EDValue{..} -> - Right (Just (IdeDeclValue (IdeValue edValueName edValueType))) - P.EDClass{..} -> - Left (TypeClassToResolve edClassName) - P.EDInstance{} -> - Right Nothing - -convertOperator :: P.ExternsFixity -> IdeDeclaration -convertOperator P.ExternsFixity{..} = - IdeDeclValueOperator - (IdeValueOperator - efOperator - efAlias - efPrecedence - efAssociativity - Nothing) - -convertTypeOperator :: P.ExternsTypeFixity -> IdeDeclaration -convertTypeOperator P.ExternsTypeFixity{..} = - IdeDeclTypeOperator - (IdeTypeOperator - efTypeOperator - efTypeAlias - efTypePrecedence - efTypeAssociativity - Nothing) diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Filter.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Filter.hs deleted file mode 100644 index 413683bd..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Filter.hs +++ /dev/null @@ -1,169 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Filter --- Description : Filters for psc-ide commands --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Filters for psc-ide commands ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Filter - ( Filter(..) - , DeclarationFilter(..) - , moduleFilter - , namespaceFilter - , exactFilter - , prefixFilter - , declarationTypeFilter - , dependencyFilter - , applyFilters - ) where - -import Protolude hiding (isPrefixOf, Prefix) - -import Control.Monad.Fail (fail) -import Data.Aeson (FromJSON(..), withObject, (.:), (.:?)) -import Data.Text (isPrefixOf) -import Data.Set qualified as Set -import Data.Map qualified as Map -import Language.PureScript.Ide.Filter.Declaration (DeclarationType) -import Language.PureScript.Ide.Types (IdeDeclarationAnn, IdeNamespace, ModuleMap, declarationType) -import Language.PureScript.Ide.Imports (Import, sliceImportSection) -import Language.PureScript.Ide.Util (discardAnn, identifierFromIdeDeclaration, namespaceForDeclaration) - -import Language.PureScript qualified as P -import Data.Text qualified as T - -import Language.PureScript.Ide.Filter.Imports (matchImport) - -newtype Filter = Filter (Either (Set P.ModuleName) DeclarationFilter) - deriving Show - -unFilter :: Filter -> Either (Set P.ModuleName) DeclarationFilter -unFilter (Filter f) = f - -data DeclarationFilter - = Prefix Text - | Exact Text - | Namespace (Set IdeNamespace) - | DeclType (Set DeclarationType) - | Dependencies { qualifier :: Maybe P.ModuleName, currentModuleName :: P.ModuleName, dependencyImports :: [Import] } - deriving Show - --- | Only keeps Declarations in the given modules -moduleFilter :: Set P.ModuleName -> Filter -moduleFilter = Filter . Left - --- | Only keeps Identifiers in the given Namespaces -namespaceFilter :: Set IdeNamespace -> Filter -namespaceFilter nss = Filter (Right (Namespace nss)) - --- | Only keeps Identifiers that are equal to the search string -exactFilter :: Text -> Filter -exactFilter t = Filter (Right (Exact t)) - --- | Only keeps Identifiers that start with the given prefix -prefixFilter :: Text -> Filter -prefixFilter t = Filter (Right (Prefix t)) - --- | Only keeps Identifiers in the given type declarations -declarationTypeFilter :: Set DeclarationType -> Filter -declarationTypeFilter dts = Filter (Right (DeclType dts)) - -dependencyFilter :: Maybe P.ModuleName -> P.ModuleName -> [Import] -> Filter -dependencyFilter q m f = Filter (Right (Dependencies q m f)) - -optimizeFilters :: [Filter] -> (Maybe (Set P.ModuleName), [DeclarationFilter]) -optimizeFilters = first smashModuleFilters . partitionEithers . map unFilter - where - smashModuleFilters [] = - Nothing - smashModuleFilters (x:xs) = - Just (foldr Set.intersection x xs) - -applyFilters :: [Filter] -> ModuleMap [IdeDeclarationAnn] -> ModuleMap [IdeDeclarationAnn] -applyFilters fs modules = case optimizeFilters fs of - (Nothing, declarationFilters) -> - applyDeclarationFilters declarationFilters modules - (Just moduleFilter', declarationFilters) -> - applyDeclarationFilters declarationFilters (Map.restrictKeys modules moduleFilter') - -applyDeclarationFilters - :: [DeclarationFilter] - -> ModuleMap [IdeDeclarationAnn] - -> ModuleMap [IdeDeclarationAnn] -applyDeclarationFilters fs = - Map.filter (not . null) - . Map.mapWithKey (\modl decls -> foldr (.) identity (map (applyDeclarationFilter modl) fs) decls) - -applyDeclarationFilter - :: P.ModuleName - -> DeclarationFilter - -> [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -applyDeclarationFilter modl f = case f of - Prefix prefix -> prefixFilter' prefix - Exact t -> exactFilter' t - Namespace namespaces -> namespaceFilter' namespaces - DeclType dts -> declarationTypeFilter' dts - Dependencies qual currentModuleName imps -> dependencyFilter' modl qual currentModuleName imps - -namespaceFilter' :: Set IdeNamespace -> [IdeDeclarationAnn] -> [IdeDeclarationAnn] -namespaceFilter' namespaces = - filter (\decl -> namespaceForDeclaration (discardAnn decl) `elem` namespaces) - -exactFilter' :: Text -> [IdeDeclarationAnn] -> [IdeDeclarationAnn] -exactFilter' search = - filter (\decl -> identifierFromIdeDeclaration (discardAnn decl) == search) - -prefixFilter' :: Text -> [IdeDeclarationAnn] -> [IdeDeclarationAnn] -prefixFilter' prefix = - filter (\decl -> prefix `isPrefixOf` identifierFromIdeDeclaration (discardAnn decl)) - -declarationTypeFilter' :: Set DeclarationType -> [IdeDeclarationAnn] -> [IdeDeclarationAnn] -declarationTypeFilter' declTypes = - filter (\decl -> declarationType (discardAnn decl) `Set.member` declTypes) - -dependencyFilter' :: P.ModuleName -> Maybe P.ModuleName -> P.ModuleName -> [Import] -> [IdeDeclarationAnn] -> [IdeDeclarationAnn] -dependencyFilter' modl qual currentModuleName imports = - if modl == currentModuleName && isNothing qual then - identity - else - filter (\decl -> any (matchImport qual modl decl) imports) - -instance FromJSON Filter where - parseJSON = withObject "filter" $ \o -> do - (filter' :: Text) <- o .: "filter" - case filter' of - "modules" -> do - params <- o .: "params" - modules <- map P.moduleNameFromString <$> params .: "modules" - pure (moduleFilter (Set.fromList modules)) - "exact" -> do - params <- o .: "params" - search <- params .: "search" - pure (exactFilter search) - "prefix" -> do - params <- o .: "params" - search <- params .: "search" - pure (prefixFilter search) - "namespace" -> do - params <- o .: "params" - namespaces <- params .: "namespaces" - pure (namespaceFilter (Set.fromList namespaces)) - "declarations" -> do - declarations <- o .: "params" - pure (declarationTypeFilter (Set.fromList declarations)) - "dependencies" -> do - params <- o .: "params" - moduleText <- params .: "moduleText" - qualifier <- fmap P.moduleNameFromString <$> params .:? "qualifier" - case sliceImportSection (T.lines moduleText) of - Left err -> fail ("Couldn't parse module imports: " <> T.unpack err) - Right (currentModuleName, _, imports, _ ) -> pure (dependencyFilter qualifier currentModuleName imports) - s -> fail ("Unknown filter: " <> show s) diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Filter/Declaration.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Filter/Declaration.hs deleted file mode 100644 index 9f17ebe7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Filter/Declaration.hs +++ /dev/null @@ -1,58 +0,0 @@ -module Language.PureScript.Ide.Filter.Declaration - ( DeclarationType(..) - , declarationTypeToText - ) where - -import Protolude hiding (isPrefixOf) - -import Control.Monad.Fail (fail) -import Data.Aeson (FromJSON(..), ToJSON(..), withText) -import Database.SQLite.Simple.ToField (ToField(..)) -import Database.SQLite.Simple (SQLData(..)) - -data DeclarationType - = Value - | Type - | Synonym - | DataConstructor - | TypeClass - | ValueOperator - | TypeOperator - | Module - deriving (Show, Eq, Ord) - -instance FromJSON DeclarationType where - parseJSON = withText "Declaration type tag" $ \case - "value" -> pure Value - "type" -> pure Type - "synonym" -> pure Synonym - "dataconstructor" -> pure DataConstructor - "typeclass" -> pure TypeClass - "valueoperator" -> pure ValueOperator - "typeoperator" -> pure TypeOperator - "module" -> pure Module - s -> fail ("Unknown declaration type: " <> show s) - -instance ToJSON DeclarationType where - toJSON = toJSON . \case - Value -> "value" :: Text - Type -> "type" - Synonym -> "synonym" - DataConstructor -> "dataconstructor" - TypeClass -> "typeclass" - ValueOperator -> "valueoperator" - TypeOperator -> "typeoperator" - Module -> "module" - -declarationTypeToText :: DeclarationType -> Text -declarationTypeToText Value = "value" -declarationTypeToText Type = "type" -declarationTypeToText Synonym = "synonym" -declarationTypeToText DataConstructor = "dataconstructor" -declarationTypeToText TypeClass = "typeclass" -declarationTypeToText ValueOperator = "valueoperator" -declarationTypeToText TypeOperator = "typeoperator" -declarationTypeToText Module = "module" - -instance ToField DeclarationType where - toField d = SQLText $ declarationTypeToText d diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Filter/Imports.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Filter/Imports.hs deleted file mode 100644 index bd1d7006..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Filter/Imports.hs +++ /dev/null @@ -1,31 +0,0 @@ -module Language.PureScript.Ide.Filter.Imports where - - -import Protolude hiding (isPrefixOf) - -import Language.PureScript.Ide.Types (IdeDataConstructor(..), IdeDeclaration(..), IdeDeclarationAnn(..), IdeType(..), IdeTypeClass(..), IdeTypeOperator(..), IdeTypeSynonym(..), IdeValue(..), IdeValueOperator(..)) -import Language.PureScript.Ide.Imports (Import(..)) - -import Language.PureScript qualified as P - -matchImport :: Maybe P.ModuleName -> P.ModuleName -> IdeDeclarationAnn -> Import -> Bool -matchImport matchQualifier declMod (IdeDeclarationAnn _ decl) (Import importMod declTy qualifier) | declMod == importMod && matchQualifier == qualifier = - case declTy of - P.Implicit -> True - P.Explicit refs -> any (matchRef decl) refs - P.Hiding refs -> not $ any (matchRef decl) refs - where - matchRef (IdeDeclValue (IdeValue ident _)) (P.ValueRef _ ident') = ident == ident' - matchRef (IdeDeclType (IdeType tname _kind _dctors)) (P.TypeRef _ tname' _dctors') = tname == tname' - matchRef (IdeDeclTypeSynonym (IdeTypeSynonym tname _type _kind)) (P.TypeRef _ tname' _dctors) = tname == tname' -- Can this occur? - - matchRef (IdeDeclDataConstructor (IdeDataConstructor dcname tname _type)) (P.TypeRef _ tname' dctors) = - tname == tname' - && maybe True (dcname `elem`) dctors -- (..) or explicitly lists constructor - - matchRef (IdeDeclTypeClass (IdeTypeClass tcname _kind _instances)) (P.TypeClassRef _ tcname') = tcname == tcname' - matchRef (IdeDeclValueOperator (IdeValueOperator{ _ideValueOpName })) (P.ValueOpRef _ opname) = _ideValueOpName == opname - matchRef (IdeDeclTypeOperator (IdeTypeOperator{ _ideTypeOpName })) (P.TypeOpRef _ opname) = _ideTypeOpName == opname - matchRef _ _ = False - -matchImport _ _ _ _ = False diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Imports.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Imports.hs deleted file mode 100644 index b96f090a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Imports.hs +++ /dev/null @@ -1,154 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Imports --- Description : Provides functionality to manage imports --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Provides functionality to manage imports ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Imports - ( parseImportsFromFile - , parseImportsFromFile' - -- for tests - , parseImport - , prettyPrintImportSection - , sliceImportSection - , prettyPrintImport' - , Import(Import) - ) - where - -import Protolude hiding (moduleName) - -import Control.Lens ((^.), (%~), ix) -import Data.List (partition) -import Data.List.NonEmpty qualified as NE -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.CST qualified as CST -import Language.PureScript.Ide.Error (IdeError(..)) -import Language.PureScript.Ide.Util (ideReadFile) - -data Import = Import P.ModuleName P.ImportDeclarationType (Maybe P.ModuleName) - deriving (Eq, Show) - --- | Reads a file and returns the parsed module name as well as the parsed --- imports, while ignoring eventual parse errors that aren't relevant to the --- import section -parseImportsFromFile - :: (MonadIO m, MonadError IdeError m) - => FilePath - -> m (P.ModuleName, [(P.ModuleName, P.ImportDeclarationType, Maybe P.ModuleName)]) -parseImportsFromFile file = do - (mn, _, imports, _) <- parseImportsFromFile' file - pure (mn, unwrapImport <$> imports) - where - unwrapImport (Import a b c) = (a, b, c) - --- | Reads a file and returns the (lines before the imports, the imports, the --- lines after the imports) -parseImportsFromFile' - :: (MonadIO m, MonadError IdeError m) - => FilePath - -> m (P.ModuleName, [Text], [Import], [Text]) -parseImportsFromFile' fp = do - (_, file) <- ideReadFile fp - case sliceImportSection (T.lines file) of - Right res -> pure res - Left err -> throwError (GeneralError err) - --- | @ImportParse@ holds the data we extract out of a partial parse of the --- sourcefile -data ImportParse = ImportParse - { ipModuleName :: P.ModuleName - -- ^ the module name we parse - , ipStart :: P.SourcePos - -- ^ the beginning of the import section. If `import Prelude` was the first - -- import, this would point at `i` - , ipEnd :: P.SourcePos - -- ^ the end of the import section - , ipImports :: [Import] - -- ^ the extracted import declarations - } - -parseModuleHeader :: Text -> Either (NE.NonEmpty CST.ParserError) ImportParse -parseModuleHeader src = do - CST.PartialResult md _ <- CST.parseModule $ CST.lenient $ CST.lexModule src - let - mn = CST.nameValue $ CST.modNamespace md - decls = flip fmap (CST.modImports md) $ \decl -> do - let ((ss, _), mn', it, qual) = CST.convertImportDecl "" decl - (ss, Import mn' it qual) - case (head decls, lastMay decls) of - (Just hd, Just ls) -> do - let - ipStart = P.spanStart $ fst hd - ipEnd = P.spanEnd $ fst ls - pure $ ImportParse mn ipStart ipEnd $ snd <$> decls - _ -> do - let pos = CST.sourcePos . CST.srcEnd . CST.tokRange . CST.tokAnn $ CST.modWhere md - pure $ ImportParse mn pos pos [] - -sliceImportSection :: [Text] -> Either Text (P.ModuleName, [Text], [Import], [Text]) -sliceImportSection fileLines = first (toS . CST.prettyPrintError . NE.head) $ do - ImportParse{..} <- parseModuleHeader file - pure - ( ipModuleName - , sliceFile (P.SourcePos 1 1) (prevPos ipStart) - , ipImports - -- Not sure why I need to drop 1 here, but it makes the tests pass - , drop 1 (sliceFile (nextPos ipEnd) (P.SourcePos (length fileLines) (lineLength (length fileLines)))) - ) - where - prevPos (P.SourcePos l c) - | l == 1 && c == 1 = P.SourcePos l c - | c == 1 = P.SourcePos (l - 1) (lineLength (l - 1)) - | otherwise = P.SourcePos l (c - 1) - nextPos (P.SourcePos l c) - | c == lineLength l = P.SourcePos (l + 1) 1 - | otherwise = P.SourcePos l (c + 1) - file = T.unlines fileLines - lineLength l = T.length (fileLines ^. ix (l - 1)) - sliceFile (P.SourcePos l1 c1) (P.SourcePos l2 c2) = - fileLines - & drop (l1 - 1) - & take (l2 - l1 + 1) - & ix 0 %~ T.drop (c1 - 1) - & ix (l2 - l1) %~ T.take c2 - -prettyPrintImport' :: Import -> Text -prettyPrintImport' (Import mn idt qual) = - "import " <> P.prettyPrintImport mn idt qual - -prettyPrintImportSection :: [Import] -> [Text] -prettyPrintImportSection imports = - let - (implicitImports, explicitImports) = partition isImplicitImport imports - in - sort (map prettyPrintImport' implicitImports) - -- Only add the extra spacing if both implicit as well as - -- explicit/qualified imports exist - <> (guard (not (null explicitImports || null implicitImports)) $> "") - <> sort (map prettyPrintImport' explicitImports) - where - isImplicitImport :: Import -> Bool - isImplicitImport i = case i of - Import _ P.Implicit Nothing -> True - Import _ (P.Hiding _) Nothing -> True - _ -> False - --- | Test and ghci helper -parseImport :: Text -> Maybe Import -parseImport t = - case fmap (CST.convertImportDecl "" . snd) - $ CST.runTokenParser CST.parseImportDeclP - $ CST.lex t of - Right (_, mn, idt, mmn) -> - Just (Import mn idt mmn) - _ -> Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Imports/Actions.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Imports/Actions.hs deleted file mode 100644 index 43ca12c2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Imports/Actions.hs +++ /dev/null @@ -1,285 +0,0 @@ -module Language.PureScript.Ide.Imports.Actions - ( addImplicitImport - , addQualifiedImport - , addImportForIdentifier - , answerRequest - - -- for tests - , addImplicitImport' - , addQualifiedImport' - , addExplicitImport' - ) -where - -import Protolude hiding (moduleName) - -import Control.Lens ((^.), has) -import Data.List (nubBy) -import Data.Map qualified as Map -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.Ide.Completion (getExactMatches) -import Language.PureScript.Ide.Error (IdeError(..)) -import Language.PureScript.Ide.Filter (Filter) -import Language.PureScript.Ide.Imports (Import(..), parseImportsFromFile', prettyPrintImportSection) -import Language.PureScript.Ide.State (getAllModules, runQuery, escapeSQL) -import Language.PureScript.Ide.Prim (idePrimDeclarations) -import Language.PureScript.Ide.Types (Ide, IdeDeclaration(..), IdeType(..), Match(..), Success(..), _IdeDeclModule, ideDtorName, ideDtorTypeName, ideTCName, ideTypeName, ideTypeOpName, ideValueOpName, toText) -import Language.PureScript.Ide.Util (discardAnn, identifierFromIdeDeclaration) -import System.IO.UTF8 (writeUTF8FileT) -import Language.PureScript.Ide.Filter qualified as F -import Language.PureScript.Names (runModuleName) -import Language.PureScript.Ide.Filter.Declaration (declarationTypeToText) -import Codec.Serialise (deserialise) -import Data.List qualified as List -import Data.ByteString.Lazy qualified as Lazy -import Language.PureScript (ModuleName(..)) - --- | Adds an implicit import like @import Prelude@ to a Sourcefile. -addImplicitImport - :: (MonadIO m, MonadError IdeError m) - => FilePath -- ^ The source file read from - -> P.ModuleName -- ^ The module to import - -> m [Text] -addImplicitImport fp mn = do - (_, pre, imports, post) <- parseImportsFromFile' fp - let newImportSection = addImplicitImport' imports mn - pure $ joinSections (pre, newImportSection, post) - -addImplicitImport' :: [Import] -> P.ModuleName -> [Text] -addImplicitImport' imports mn = - prettyPrintImportSection (Import mn P.Implicit Nothing : imports) - --- | Adds a qualified import like @import Data.Map as Map@ to a source file. -addQualifiedImport - :: (MonadIO m, MonadError IdeError m) - => FilePath - -- ^ The sourcefile read from - -> P.ModuleName - -- ^ The module to import - -> P.ModuleName - -- ^ The qualifier under which to import - -> m [Text] -addQualifiedImport fp mn qualifier = do - (_, pre, imports, post) <- parseImportsFromFile' fp - let newImportSection = addQualifiedImport' imports mn qualifier - pure $ joinSections (pre, newImportSection, post) - -addQualifiedImport' :: [Import] -> P.ModuleName -> P.ModuleName -> [Text] -addQualifiedImport' imports mn qualifier = - prettyPrintImportSection (Import mn P.Implicit (Just qualifier) : imports) - --- | Adds an explicit import like @import Prelude (unit)@ to a Sourcefile. If an --- explicit import already exists for the given module, it adds the identifier --- to that imports list. --- --- So @addExplicitImport "/File.purs" "bind" "Prelude"@ with an already existing --- @import Prelude (bind)@ in the file File.purs returns @["import Prelude --- (bind, unit)"]@ -addExplicitImport :: (MonadIO m, MonadError IdeError m) => - FilePath -> IdeDeclaration -> P.ModuleName -> Maybe P.ModuleName -> m [Text] -addExplicitImport fp decl moduleName qualifier = do - (mn, pre, imports, post) <- parseImportsFromFile' fp - let newImportSection = - -- TODO: Open an issue when this PR is merged, we should optimise this - -- so that this case does not write to disc - if mn == moduleName - then imports - else addExplicitImport' decl moduleName qualifier imports - pure $ joinSections (pre, prettyPrintImportSection newImportSection, post) - -addExplicitImport' :: IdeDeclaration -> P.ModuleName -> Maybe P.ModuleName -> [Import] -> [Import] -addExplicitImport' decl moduleName qualifier imports = - let - isImplicitlyImported = - any (\case - Import mn P.Implicit qualifier' -> mn == moduleName && qualifier == qualifier' - _ -> False) imports - isNotExplicitlyImportedFromPrim = - moduleName == C.M_Prim && - not (any (\case - Import C.M_Prim (P.Explicit _) Nothing -> True - _ -> False) imports) - -- We can't import Modules from other modules - isModule = has _IdeDeclModule decl - - matches (Import mn (P.Explicit _) qualifier') = mn == moduleName && qualifier == qualifier' - matches _ = False - freshImport = Import moduleName (P.Explicit [refFromDeclaration decl]) qualifier - in - if isImplicitlyImported || isNotExplicitlyImportedFromPrim || isModule - then imports - else updateAtFirstOrPrepend matches (insertDeclIntoImport decl) freshImport imports - where - refFromDeclaration (IdeDeclTypeClass tc) = - P.TypeClassRef ideSpan (tc ^. ideTCName) - refFromDeclaration (IdeDeclDataConstructor dtor) = - P.TypeRef ideSpan (dtor ^. ideDtorTypeName) Nothing - refFromDeclaration (IdeDeclType t) = - P.TypeRef ideSpan (t ^. ideTypeName) (Just []) - refFromDeclaration (IdeDeclValueOperator op) = - P.ValueOpRef ideSpan (op ^. ideValueOpName) - refFromDeclaration (IdeDeclTypeOperator op) = - P.TypeOpRef ideSpan (op ^. ideTypeOpName) - refFromDeclaration d = - P.ValueRef ideSpan (P.Ident (identifierFromIdeDeclaration d)) - - -- Adds a declaration to an import: - -- TypeDeclaration "Maybe" + Data.Maybe (maybe) -> Data.Maybe(Maybe, maybe) - insertDeclIntoImport :: IdeDeclaration -> Import -> Import - insertDeclIntoImport decl' (Import mn (P.Explicit refs) qual) = - Import mn (P.Explicit (sort (insertDeclIntoRefs decl' refs))) qual - insertDeclIntoImport _ is = is - - insertDeclIntoRefs :: IdeDeclaration -> [P.DeclarationRef] -> [P.DeclarationRef] - insertDeclIntoRefs d@(IdeDeclDataConstructor dtor) refs = - updateAtFirstOrPrepend - (matchType (dtor ^. ideDtorTypeName)) - (insertDtor (dtor ^. ideDtorName)) - (refFromDeclaration d) - refs - insertDeclIntoRefs (IdeDeclType t) refs - | any matches refs = refs - where - matches (P.TypeRef _ typeName _) = _ideTypeName t == typeName - matches _ = False - insertDeclIntoRefs dr refs = nubBy ((==) `on` P.prettyPrintRef) (refFromDeclaration dr : refs) - - insertDtor _ (P.TypeRef ss tn' _) = P.TypeRef ss tn' Nothing - insertDtor _ refs = refs - - matchType :: P.ProperName 'P.TypeName -> P.DeclarationRef -> Bool - matchType tn (P.TypeRef _ n _) = tn == n - matchType _ _ = False - - --- | Looks up the given identifier in the currently loaded modules. --- --- * Throws an error if the identifier cannot be found. --- --- * If exactly one match is found, adds an explicit import to the importsection --- --- * If more than one possible imports are found, reports the possibilities as a --- list of completions. -addImportForIdentifier - :: (Ide m, MonadError IdeError m) - => FilePath -- ^ The Sourcefile to read from - -> Text -- ^ The identifier to import - -> Maybe P.ModuleName -- ^ The optional qualifier under which to import - -> [Filter] -- ^ Filters to apply before searching for the identifier - -> m (Either [Match IdeDeclaration] [Text]) -addImportForIdentifier fp ident qual filters' = do - let filters = F.exactFilter ident : filters' - - - rows :: [(Text, Lazy.ByteString)] <- runQuery $ - "select module_name, declaration " <> - "from ide_declarations where " <> - T.intercalate " and " ( - mapMaybe (\case - F.Filter (Left modules) -> - Just $ "module_name in (" <> T.intercalate "," (toList modules <&> runModuleName <&> \m -> "'" <> m <> "'") <> ")" - F.Filter (Right (F.Prefix f)) -> Just $ "name glob '" <> escapeSQL f <> "*'" - F.Filter (Right (F.Exact f)) -> Just $ "name glob '" <> escapeSQL f <> "'" - F.Filter (Right (F.Namespace namespaces)) -> - Just $ "namespace in (" <> T.intercalate "," (toList namespaces <&> \n -> "'" <> toText n <> "'") <> ")" - F.Filter (Right (F.DeclType dt)) -> - Just $ "namespace in (" <> T.intercalate "," (toList dt <&> \t -> "'" <> declarationTypeToText t <> "'") <> ")" - F.Filter _ -> Nothing) - filters) - - modules <- getAllModules Nothing - - -- Fallback to volatile state if SQLite returns no results (e.g., for Prim modules) - let declarations :: [Match IdeDeclaration] = - if null rows - then - let addPrim = Map.union idePrimDeclarations - in fmap discardAnn - <$> getExactMatches ident filters (addPrim modules) - else - rows <&> \(m, bs) -> Match (ModuleName m, discardAnn $ deserialise bs) - - let - matches = declarations - & filter (\(Match (_, d)) -> not (has _IdeDeclModule d)) - - case matches of - [] -> - throwError (NotFound "Couldn't find the given identifier. \ - \Have you loaded the corresponding module?") - - -- Only one match was found for the given identifier, so we can insert it - -- right away - [Match (m, decl)] -> - Right <$> addExplicitImport fp decl m qual - - -- This case comes up for newtypes and dataconstructors. Because values and - -- types don't share a namespace we can get multiple matches from the same - -- module. This also happens for parameterized types, as these generate both - -- a type as well as a type synonym. - - ms@[Match (m1, d1), Match (m2, d2)] -> - if m1 /= m2 - -- If the modules don't line up we just ask the user to specify the - -- module - then pure (Left ms) - else case decideRedundantCase d1 d2 <|> decideRedundantCase d2 d1 of - -- If dataconstructor and type line up we just import the - -- dataconstructor as that will give us an unnecessary import warning at - -- worst - Just decl -> - Right <$> addExplicitImport fp decl m1 qual - -- Here we need the user to specify whether they wanted a - -- dataconstructor or a type - Nothing -> - throwError (GeneralError "Undecidable between type and dataconstructor") - - -- Multiple matches were found so we need to ask the user to clarify which - -- module they meant - xs -> - pure (Left xs) - where - decideRedundantCase d@(IdeDeclDataConstructor dtor) (IdeDeclType t) = - if dtor ^. ideDtorTypeName == t ^. ideTypeName then Just d else Nothing - decideRedundantCase IdeDeclType{} ts@IdeDeclTypeSynonym{} = - Just ts - decideRedundantCase _ _ = Nothing - --- | Writes a list of lines to @Just filepath@ and responds with a @TextResult@, --- or returns the lines as a @MultilineTextResult@ if @Nothing@ was given as the --- first argument. -answerRequest :: (MonadIO m) => Maybe FilePath -> [Text] -> m Success -answerRequest outfp rs = - case outfp of - Nothing -> pure (MultilineTextResult rs) - Just outfp' -> do - liftIO (writeUTF8FileT outfp' (T.unlines rs)) - pure (TextResult ("Written to " <> T.pack outfp')) - - --- | If none of the elements of the list satisfy the given predicate 'predicate', then prepend the default value 'def' --- to the given list. Otherwise, update the first element of the list that satisfies 'predicate' with the updating --- function 'update'. -updateAtFirstOrPrepend :: (a -> Bool) -> (a -> a) -> a -> [a] -> [a] -updateAtFirstOrPrepend predicate update def xs = - case break predicate xs of - (before, []) -> def : before - (before, x : after) -> before ++ [update x] ++ after - - -ideSpan :: P.SourceSpan -ideSpan = P.internalModuleSourceSpan "" - -joinSections :: ([Text], [Text], [Text]) -> [Text] -joinSections (pre, decls, post) = pre `joinLine` (decls `joinLine` post) - where - isBlank = T.all (== ' ') - joinLine as bs - | Just ln1 <- lastMay as - , Just ln2 <- head bs - , not (isBlank ln1) && not (isBlank ln2) = - as ++ [""] ++ bs - | otherwise = - as ++ bs diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Logging.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Logging.hs deleted file mode 100644 index 925881b2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Logging.hs +++ /dev/null @@ -1,41 +0,0 @@ -{-# LANGUAGE PackageImports #-} - -module Language.PureScript.Ide.Logging - ( runLogger - , logPerf - , displayTimeSpec - , labelTimespec - ) where - -import Protolude - -import "monad-logger" Control.Monad.Logger (LogLevel(..), LoggingT, MonadLogger, filterLogger, logOtherN, runStdoutLoggingT) -import Data.Text qualified as T -import Language.PureScript.Ide.Types (IdeLogLevel(..)) -import System.Clock (Clock(..), TimeSpec, diffTimeSpec, getTime, toNanoSecs) -import Text.Printf (printf) - -runLogger :: MonadIO m => IdeLogLevel -> LoggingT m a -> m a -runLogger logLevel' = - runStdoutLoggingT . filterLogger (\_ logLevel -> - case logLevel' of - LogAll -> True - LogDefault -> not (logLevel == LevelOther "perf" || logLevel == LevelDebug) - LogNone -> False - LogDebug -> logLevel /= LevelOther "perf" - LogPerf -> logLevel == LevelOther "perf") - -labelTimespec :: Text -> TimeSpec -> Text -labelTimespec label duration = label <> ": " <> displayTimeSpec duration - -logPerf :: (MonadIO m, MonadLogger m) => (TimeSpec -> Text) -> m t -> m t -logPerf format f = do - start <- liftIO (getTime Monotonic) - result <- f - end <- liftIO (getTime Monotonic) - logOtherN (LevelOther "perf") (format (diffTimeSpec start end)) - pure result - -displayTimeSpec :: TimeSpec -> Text -displayTimeSpec ts = - T.pack (printf "%0.2f" (fromIntegral (toNanoSecs ts) / 1000000 :: Double)) <> "ms" diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Matcher.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Matcher.hs deleted file mode 100644 index 0d33fe15..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Matcher.hs +++ /dev/null @@ -1,142 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Matcher --- Description : Matchers for psc-ide commands --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Matchers for psc-ide commands ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Matcher - ( Matcher - , Matcher'(..) - , runMatcher - -- for tests - , flexMatcher - ) where - -import Protolude - -import Control.Monad.Fail (fail) -import Data.Aeson (FromJSON(..), withObject, (.:), (.:?)) -import Data.Text qualified as T -import Data.Text.Encoding qualified as TE -import Language.PureScript.Ide.Types (IdeDeclarationAnn, Match) -import Language.PureScript.Ide.Util (discardAnn, identifierFromIdeDeclaration, unwrapMatch) -import Text.EditDistance (defaultEditCosts, levenshteinDistance) -import Text.Regex.TDFA ((=~)) - - -type ScoredMatch a = (Match a, Double) - -newtype Matcher a = Matcher (Endo [Match a]) deriving (Semigroup, Monoid) - -data Matcher' - = Distance { search:: Text, maximumDistance :: Int } - | Flex { search:: Text } - deriving (Show) - -instance FromJSON Matcher' where - parseJSON = withObject "matcher" $ \o -> do - (matcher :: Maybe Text) <- o .:? "matcher" - case matcher of - Just "flex" -> do - params <- o .: "params" - Flex <$> params .: "search" - Just "distance" -> do - params <- o .: "params" - Distance - <$> params .: "search" - <*> params .: "maximumDistance" - Just s -> fail ("Unknown matcher: " <> show s) - Nothing -> fail "Unknown matcher" - -instance FromJSON (Matcher IdeDeclarationAnn) where - parseJSON = withObject "matcher" $ \o -> do - (matcher :: Maybe Text) <- o .:? "matcher" - case matcher of - Just "flex" -> do - params <- o .: "params" - flexMatcher <$> params .: "search" - Just "distance" -> do - params <- o .: "params" - distanceMatcher - <$> params .: "search" - <*> params .: "maximumDistance" - Just s -> fail ("Unknown matcher: " <> show s) - Nothing -> return mempty - --- | Matches any occurrence of the search string with intersections --- --- The scoring measures how far the matches span the string where --- closer is better. --- Examples: --- flMa matches flexMatcher. Score: 14.28 --- sons matches sortCompletions. Score: 6.25 -flexMatcher :: Text -> Matcher IdeDeclarationAnn -flexMatcher p = mkMatcher (flexMatch p) - -distanceMatcher :: Text -> Int -> Matcher IdeDeclarationAnn -distanceMatcher q maxDist = mkMatcher (distanceMatcher' q maxDist) - -distanceMatcher' :: Text -> Int -> [Match IdeDeclarationAnn] -> [ScoredMatch IdeDeclarationAnn] -distanceMatcher' q maxDist = mapMaybe go - where - go m = let d = dist (T.unpack y) - y = identifierFromIdeDeclaration (discardAnn (unwrapMatch m)) - in if d <= maxDist - then Just (m, 1 / fromIntegral d) - else Nothing - dist = levenshteinDistance defaultEditCosts (T.unpack q) - -mkMatcher :: ([Match a] -> [ScoredMatch a]) -> Matcher a -mkMatcher matcher = Matcher . Endo $ fmap fst . sortCompletions . matcher - -runMatcher :: Matcher a -> [Match a] -> [Match a] -runMatcher (Matcher m)= appEndo m - -sortCompletions :: [ScoredMatch a] -> [ScoredMatch a] -sortCompletions = sortOn (Down . snd) - -flexMatch :: Text -> [Match IdeDeclarationAnn] -> [ScoredMatch IdeDeclarationAnn] -flexMatch = mapMaybe . flexRate - -flexRate :: Text -> Match IdeDeclarationAnn -> Maybe (ScoredMatch IdeDeclarationAnn) -flexRate p c = do - score <- flexScore p (identifierFromIdeDeclaration (discardAnn (unwrapMatch c))) - return (c, score) - --- FlexMatching ala Sublime. --- Borrowed from: http://cdewaka.com/2013/06/fuzzy-pattern-matching-in-haskell/ --- --- By string =~ pattern we'll get the start of the match and the length of --- the matches a (start, length) tuple if there's a match. --- If match fails then it would be (-1,0) -flexScore :: Text -> Text -> Maybe Double -flexScore pat str = - case T.uncons pat of - Nothing -> Nothing - Just (first', p) -> - case TE.encodeUtf8 str =~ TE.encodeUtf8 pat' :: (Int, Int) of - (-1,0) -> Nothing - (start,len) -> Just $ calcScore start (start + len) - where - escapedPattern :: [Text] - escapedPattern = map escape (T.unpack p) - - -- escape prepends a backslash to "regexy" characters to prevent the - -- matcher from crashing when trying to build the regex - escape :: Char -> Text - escape c = if c `elem` T.unpack "[\\^$.|?*+(){}" - then T.pack ['\\', c] - else T.singleton c - -- This just interleaves the search pattern with .* - -- abcd[*] -> a.*b.*c.*d.*[*] - pat' = escape first' <> foldMap (<> ".*") escapedPattern - calcScore start end = - 100.0 / fromIntegral ((1 + start) * (end - start + 1)) diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Prim.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Prim.hs deleted file mode 100644 index 398c0137..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Prim.hs +++ /dev/null @@ -1,69 +0,0 @@ -module Language.PureScript.Ide.Prim (idePrimDeclarations) where - -import Protolude - -import Data.Text qualified as T -import Data.Map qualified as Map -import Language.PureScript qualified as P -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.Environment qualified as PEnv -import Language.PureScript.Ide.Types (IdeDeclaration(..), IdeDeclarationAnn(..), IdeType(..), IdeTypeClass(..), ModuleMap, emptyAnn) - -idePrimDeclarations :: ModuleMap [IdeDeclarationAnn] -idePrimDeclarations = Map.fromList - [ ( C.M_Prim - , mconcat [primTypes, primClasses] - ) - , ( C.M_Prim_Boolean - , mconcat [primBooleanTypes] - ) - , ( C.M_Prim_Ordering - , mconcat [primOrderingTypes] - ) - , ( C.M_Prim_Row - , mconcat [primRowTypes, primRowClasses] - ) - , ( C.M_Prim_RowList - , mconcat [primRowListTypes, primRowListClasses] - ) - , ( C.M_Prim_Symbol - , mconcat [primSymbolTypes, primSymbolClasses] - ) - , ( C.M_Prim_Int - , mconcat [primIntTypes, primIntClasses] - ) - , ( C.M_Prim_TypeError - , mconcat [primTypeErrorTypes, primTypeErrorClasses] - ) - ] - where - annType tys = flip mapMaybe (Map.toList tys) $ \(tn, (kind, _)) -> do - let name = P.disqualify tn - -- We need to remove the ClassName$Dict synonyms, because we - -- don't want them to show up in completions - guard (isNothing (T.find (== '$') (P.runProperName name))) - Just (IdeDeclarationAnn emptyAnn (IdeDeclType (IdeType name kind []))) - annClass cls = foreach (Map.toList cls) $ \(cn, _) -> - -- Dummy kind and instances here, but we primarily care about the name completion - IdeDeclarationAnn emptyAnn (IdeDeclTypeClass (IdeTypeClass (P.disqualify cn) P.kindType []) ) - -- The Environment for typechecking holds both a type class as well as a - -- type declaration for every class, but we filter the types out when we - -- load the Externs, so we do the same here - removeClasses types classes = - Map.difference types (Map.mapKeys (map P.coerceProperName) classes) - - primTypes = annType (removeClasses PEnv.primTypes PEnv.primClasses) - primBooleanTypes = annType PEnv.primBooleanTypes - primOrderingTypes = annType PEnv.primOrderingTypes - primRowTypes = annType (removeClasses PEnv.primRowTypes PEnv.primRowClasses) - primRowListTypes = annType (removeClasses PEnv.primRowListTypes PEnv.primRowListClasses) - primSymbolTypes = annType (removeClasses PEnv.primSymbolTypes PEnv.primSymbolClasses) - primIntTypes = annType (removeClasses PEnv.primIntTypes PEnv.primIntClasses) - primTypeErrorTypes = annType (removeClasses PEnv.primTypeErrorTypes PEnv.primTypeErrorClasses) - - primClasses = annClass PEnv.primClasses - primRowClasses = annClass PEnv.primRowClasses - primRowListClasses = annClass PEnv.primRowListClasses - primSymbolClasses = annClass PEnv.primSymbolClasses - primIntClasses = annClass PEnv.primIntClasses - primTypeErrorClasses = annClass PEnv.primTypeErrorClasses diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Rebuild.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Rebuild.hs deleted file mode 100644 index 97d1f6c8..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Rebuild.hs +++ /dev/null @@ -1,282 +0,0 @@ -{-# language PackageImports, TemplateHaskell, BlockArguments #-} - -module Language.PureScript.Ide.Rebuild - ( rebuildFileSync - , rebuildFileAsync - , rebuildFile - ) where - -import Protolude hiding (moduleName) - -import "monad-logger" Control.Monad.Logger (LoggingT, MonadLogger, logDebug) -import Data.List qualified as List -import Data.Map.Lazy qualified as M -import Data.Maybe (fromJust, catMaybes) -import Data.Set qualified as S -import Data.Time qualified as Time -import Data.Text qualified as Text -import Language.PureScript qualified as P -import Language.PureScript.Make (ffiCodegen') -import Language.PureScript.Make.Cache (CacheInfo(..), normaliseForCache) -import Language.PureScript.CST qualified as CST - -import Language.PureScript.Ide.Error (IdeError(..)) -import Language.PureScript.Ide.Logging (labelTimespec, logPerf, runLogger) -import Language.PureScript.Ide.State (cacheRebuild, getExternFiles, insertExterns, insertModule, populateVolatileState, updateCacheTimestamp, runQuery) -import Language.PureScript.Ide.Types (Ide, IdeConfiguration(..), IdeEnvironment(..), ModuleMap, Success(..)) -import Language.PureScript.Ide.Util (ideReadFile) -import System.Directory (getCurrentDirectory) -import Database.SQLite.Simple qualified as SQLite -import System.FilePath ((), makeRelative) -import Data.Aeson (decode) -import Language.PureScript.Externs (ExternsFile(ExternsFile)) -import Data.ByteString qualified as T -import Data.ByteString.Lazy qualified as TE -import Language.PureScript.Names (runModuleName) -import Data.Text (intercalate) -import Unsafe.Coerce (unsafeCoerce) -import Database.SQLite.Simple (Query(fromQuery), ToRow, SQLData (SQLText)) -import Data.String (String) -import Codec.Serialise (deserialise) - --- | Given a filepath performs the following steps: --- --- * Reads and parses a PureScript module from the filepath. --- --- * Builds a dependency graph for the parsed module from the already loaded --- ExternsFiles. --- --- * Attempts to find an FFI definition file for the module by looking --- for a file with the same filepath except for a .js extension. --- --- * Passes all the created artifacts to @rebuildModule@. --- --- * If the rebuilding succeeds, returns a @RebuildSuccess@ with the generated --- warnings, and if rebuilding fails, returns a @RebuildError@ with the --- generated errors. -rebuildFile - :: (Ide m, MonadLogger m, MonadError IdeError m) - => FilePath - -- ^ The file to rebuild - -> Maybe FilePath - -- ^ The file to use as the location for parsing and errors - -> Set P.CodegenTarget - -- ^ The targets to codegen - -> (ReaderT IdeEnvironment (LoggingT IO) () -> m ()) - -- ^ A runner for the second build with open exports - -> m Success -rebuildFile file actualFile codegenTargets runOpenBuild = do - currentDir <- liftIO getCurrentDirectory - (fp, input) <- - case List.stripPrefix "data:" file of - Just source -> pure ("", Text.pack source) - _ -> ideReadFile file - let fp' = fromMaybe fp actualFile - (pwarnings, m) <- case sequence $ CST.parseFromFile fp' input of - Left parseError -> - throwError $ RebuildError [(fp', input)] $ CST.toMultipleErrors fp' parseError - Right m -> pure m - let moduleName = P.getModuleName m - outputDirectory <- confOutputPath . ideConfiguration <$> ask - -- Externs files must be sorted ahead of time, so that they get applied - -- in the right order (bottom up) to the 'Environment'. - -- externs' <- logPerf (labelTimespec "Sorting externs") (sortExterns m =<< getExternFiles) - !externs <- logPerf (labelTimespec "Sorting externs") (sortExterns' outputDirectory m) - -- For rebuilding, we want to 'RebuildAlways', but for inferring foreign - -- modules using their file paths, we need to specify the path in the 'Map'. - let filePathMap = M.singleton moduleName (Left P.RebuildAlways) - let pureRebuild = fp == "" - let modulePath = if pureRebuild then fp' else file - foreigns <- P.inferForeignModules (M.singleton moduleName (Right modulePath)) - let makeEnv = P.buildMakeActions outputDirectory filePathMap foreigns False - & (if pureRebuild then enableForeignCheck foreigns codegenTargets . shushCodegen else identity) - & shushProgress - -- Rebuild the single module using the cached externs - (result, warnings) <- logPerf (labelTimespec "Rebuilding Module") $ - liftIO $ P.runMake (P.defaultOptions { P.optionsCodegenTargets = codegenTargets }) do - newExterns <- P.rebuildModule makeEnv externs m - unless pureRebuild - $ updateCacheDb codegenTargets outputDirectory file actualFile moduleName - pure newExterns - case result of - Left errors -> - throwError (RebuildError [(fp', input)] errors) - Right newExterns -> do - -- insertModule (fromMaybe file actualFile, m) - -- insertExterns newExterns - -- void populateVolatileState - _ <- updateCacheTimestamp - -- runOpenBuild (rebuildModuleOpen makeEnv externs m) - pure (RebuildSuccess (CST.toMultipleWarnings fp pwarnings <> warnings)) - --- | When adjusting the cache db file after a rebuild we always pick a --- non-sensical timestamp ("1858-11-17T00:00:00Z"), and rely on the --- content hash to tell whether the module needs rebuilding. This is --- because IDE rebuilds may be triggered on temporary files to not --- force editors to save the actual source file to get at diagnostics -dayZero :: Time.UTCTime -dayZero = Time.UTCTime (Time.ModifiedJulianDay 0) 0 - -updateCacheDb - :: MonadIO m - => MonadError P.MultipleErrors m - => Set P.CodegenTarget - -> FilePath - -- ^ The output directory - -> FilePath - -- ^ The file to read the content hash from - -> Maybe FilePath - -- ^ The file name to update in the cache - -> P.ModuleName - -- ^ The module name to update in the cache - -> m () -updateCacheDb codegenTargets outputDirectory file actualFile moduleName = do - cwd <- liftIO getCurrentDirectory - contentHash <- P.hashFile file - let moduleCacheInfo = (normaliseForCache cwd (fromMaybe file actualFile), (dayZero, contentHash)) - - foreignCacheInfo <- - if S.member P.JS codegenTargets then do - foreigns' <- P.inferForeignModules (M.singleton moduleName (Right (fromMaybe file actualFile))) - for (M.lookup moduleName foreigns') \foreignPath -> do - foreignHash <- P.hashFile foreignPath - pure (normaliseForCache cwd foreignPath, (dayZero, foreignHash)) - else - pure Nothing - - let cacheInfo = M.fromList (moduleCacheInfo : maybeToList foreignCacheInfo) - cacheDb <- P.readCacheDb' outputDirectory - P.writeCacheDb' outputDirectory (M.insert moduleName (CacheInfo cacheInfo) cacheDb) - -rebuildFileAsync - :: forall m. (Ide m, MonadLogger m, MonadError IdeError m) - => FilePath -> Maybe FilePath -> Set P.CodegenTarget -> m Success -rebuildFileAsync fp fp' ts = rebuildFile fp fp' ts asyncRun - where - asyncRun :: ReaderT IdeEnvironment (LoggingT IO) () -> m () - asyncRun action = do - env <- ask - let ll = confLogLevel (ideConfiguration env) - void (liftIO (async (runLogger ll (runReaderT action env)))) - -rebuildFileSync - :: forall m. (Ide m, MonadLogger m, MonadError IdeError m) - => FilePath -> Maybe FilePath -> Set P.CodegenTarget -> m Success -rebuildFileSync fp fp' ts = rebuildFile fp fp' ts syncRun - where - syncRun :: ReaderT IdeEnvironment (LoggingT IO) () -> m () - syncRun action = do - env <- ask - let ll = confLogLevel (ideConfiguration env) - void (liftIO (runLogger ll (runReaderT action env))) - --- | Rebuilds a module but opens up its export list first and stores the result --- inside the rebuild cache -rebuildModuleOpen - :: (Ide m, MonadLogger m) - => P.MakeActions P.Make - -> [P.ExternsFile] - -> P.Module - -> m () -rebuildModuleOpen makeEnv externs m = void $ runExceptT do - (openResult, _) <- liftIO $ P.runMake P.defaultOptions $ - P.rebuildModule (shushProgress (shushCodegen makeEnv)) externs (openModuleExports m) - case openResult of - Left _ -> - throwError (GeneralError "Failed when rebuilding with open exports") - Right result -> do - $(logDebug) - ("Setting Rebuild cache: " <> P.runModuleName (P.efModuleName result)) - cacheRebuild result - --- | Shuts the compiler up about progress messages -shushProgress :: Monad m => P.MakeActions m -> P.MakeActions m -shushProgress ma = - ma { P.progress = \_ -> pure () } - --- | Stops any kind of codegen -shushCodegen :: Monad m => P.MakeActions m -> P.MakeActions m -shushCodegen ma = - ma { P.codegen = \_ _ _ _ -> pure () - , P.ffiCodegen = \_ -> pure () - } - --- | Enables foreign module check without actual codegen. -enableForeignCheck - :: M.Map P.ModuleName FilePath - -> S.Set P.CodegenTarget - -> P.MakeActions P.Make - -> P.MakeActions P.Make -enableForeignCheck foreigns codegenTargets ma = - ma { P.ffiCodegen = ffiCodegen' foreigns codegenTargets Nothing - } - --- | Returns a topologically sorted list of dependent ExternsFiles for the given --- module. Throws an error if there is a cyclic dependency within the --- ExternsFiles -sortExterns - :: (Ide m, MonadError IdeError m) - => P.Module - -> ModuleMap P.ExternsFile - -> m [P.ExternsFile] -sortExterns m ex = do - sorted' <- runExceptT - . P.sortModules P.Transitive P.moduleSignature - . (:) m - . map mkShallowModule - . M.elems - . M.delete (P.getModuleName m) $ ex - case sorted' of - Left err -> - throwError (RebuildError [] err) - Right (sorted, graph) -> do - let deps = fromJust (List.lookup (P.getModuleName m) graph) - pure $ mapMaybe getExtern (deps `inOrderOf` map P.getModuleName sorted) - where - mkShallowModule P.ExternsFile{..} = - P.Module (P.internalModuleSourceSpan "") [] efModuleName (map mkImport efImports) Nothing - mkImport (P.ExternsImport mn it iq) = - P.ImportDeclaration (P.internalModuleSourceSpan "", []) mn it iq - getExtern mn = M.lookup mn ex - -- Sort a list so its elements appear in the same order as in another list. - inOrderOf :: (Ord a) => [a] -> [a] -> [a] - inOrderOf xs ys = let s = S.fromList xs in filter (`S.member` s) ys - -sortExterns' - :: (Ide m) - => FilePath - -> P.Module - -> m [P.ExternsFile] -sortExterns' _ m = do - let P.Module _ _ _ declarations _ = m - let moduleDependencies = declarations >>= \case - P.ImportDeclaration _ importName _ _ -> [importName] - _ -> [] - - externs <- runQuery $ unlines [ - "with recursive", - "graph(dependency, level) as (", - " select module_name , 1 as level", - " from modules where module_name in (" <> Data.Text.intercalate ", " (moduleDependencies <&> \v -> "'" <> runModuleName v <> "'") <> ")", - " union ", - " select d.dependency as dep, graph.level + 1 as level", - " from graph join dependencies d on graph.dependency = d.module_name", - "),", - "topo as (", - " select dependency, max(level) as level", - " from graph group by dependency", - ") ", - "select extern", - "from topo join modules on topo.dependency = modules.module_name order by level desc;" - ] - - pure $ (externs >>= identity) <&> deserialise - - -- !r <- SQLite.withConnection (outputDir "cache.db") \conn -> - -- SQLite.query conn query (SQLite.Only $ "[" <> Data.Text.intercalate ", " (dependencies <&> \v -> "\"" <> runModuleName v <> "\"") <> "]") - -- <&> \r -> (r >>= identity) <&> deserialise - -- pure r - --- | Removes a modules export list. -openModuleExports :: P.Module -> P.Module -openModuleExports (P.Module ss cs mn decls _) = P.Module ss cs mn decls Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Reexports.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Reexports.hs deleted file mode 100644 index 3da2a0a8..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Reexports.hs +++ /dev/null @@ -1,128 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Reexports --- Description : Resolves reexports for psc-ide --- Copyright : Christoph Hegemann 2016 --- Brian Sermons 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Resolves reexports for psc-ide ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Reexports - ( resolveReexports - , prettyPrintReexportResult - , reexportHasFailures - , ReexportResult(..) - -- for tests - , resolveReexports' - ) where - -import Protolude hiding (moduleName) - -import Control.Lens (set) -import Data.Map qualified as Map -import Language.PureScript qualified as P -import Language.PureScript.Ide.Types -import Language.PureScript.Ide.Util (discardAnn) - --- | Contains the module with resolved reexports, and possible failures -data ReexportResult a - = ReexportResult - { reResolved :: a - , reFailed :: [(P.ModuleName, P.DeclarationRef)] - } deriving (Show, Eq, Functor) - - --- | Uses the passed formatter to format the resolved module, and adds possible --- failures -prettyPrintReexportResult - :: (a -> Text) - -- ^ Formatter for the resolved result - -> ReexportResult a - -- ^ The Result to be pretty printed - -> Text -prettyPrintReexportResult f ReexportResult{..} - | null reFailed = - "Successfully resolved reexports for " <> f reResolved - | otherwise = - "Failed to resolve reexports for " - <> f reResolved - <> foldMap (\(mn, ref) -> P.runModuleName mn <> show ref) reFailed - --- | Whether any Refs couldn't be resolved -reexportHasFailures :: ReexportResult a -> Bool -reexportHasFailures = not . null . reFailed - --- | Resolves Reexports for the given Modules, by looking up the reexported --- values from the passed in DeclarationRefs -resolveReexports - :: ModuleMap [(P.ModuleName, P.DeclarationRef)] - -- ^ the references to resolve - -> ModuleMap [IdeDeclarationAnn] - -- ^ Modules to search for the reexported declarations - -> ModuleMap (ReexportResult [IdeDeclarationAnn]) -resolveReexports reexportRefs modules = - Map.mapWithKey (\moduleName decls -> - maybe (ReexportResult decls []) - (map (decls <>) . resolveReexports' modules) - (Map.lookup moduleName reexportRefs)) modules - -resolveReexports' - :: ModuleMap [IdeDeclarationAnn] - -> [(P.ModuleName, P.DeclarationRef)] - -> ReexportResult [IdeDeclarationAnn] -resolveReexports' modules refs = - ReexportResult (concat resolvedRefs) failedRefs - where - (failedRefs, resolvedRefs) = partitionEithers (resolveRef' <$> refs) - resolveRef' x@(mn, r) = case Map.lookup mn modules of - Nothing -> Left x - Just decls' -> - let - setExportedFrom = set (idaAnnotation . annExportedFrom) . Just - in - bimap (mn,) (map (setExportedFrom mn)) (resolveRef decls' r) - -resolveRef - :: [IdeDeclarationAnn] - -> P.DeclarationRef - -> Either P.DeclarationRef [IdeDeclarationAnn] -resolveRef decls ref = case ref of - P.TypeRef _ tn mdtors -> - case findRef (anyOf (_IdeDeclType . ideTypeName) (== tn)) - <|> findRef (anyOf (_IdeDeclTypeSynonym . ideSynonymName) (== tn)) of - Nothing -> - Left ref - Just d -> Right $ d : case mdtors of - Nothing -> - -- If the dataconstructor field inside the TypeRef is Nothing, that - -- means that all data constructors are exported, so we need to look - -- those up ourselves - findDtors tn - Just dtors -> mapMaybe lookupDtor dtors - P.ValueRef _ i -> - findWrapped (anyOf (_IdeDeclValue . ideValueIdent) (== i)) - P.ValueOpRef _ name -> - findWrapped (anyOf (_IdeDeclValueOperator . ideValueOpName) (== name)) - P.TypeOpRef _ name -> - findWrapped (anyOf (_IdeDeclTypeOperator . ideTypeOpName) (== name)) - P.TypeClassRef _ name -> - findWrapped (anyOf (_IdeDeclTypeClass . ideTCName) (== name)) - _ -> - Left ref - where - findWrapped = maybe (Left ref) (Right . pure) . findRef - findRef f = find (f . discardAnn) decls - - lookupDtor name = - findRef (anyOf (_IdeDeclDataConstructor . ideDtorName) (== name)) - - findDtors tn = filter (anyOf - (idaDeclaration - . _IdeDeclDataConstructor - . ideDtorTypeName) (== tn)) decls diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/SourceFile.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/SourceFile.hs deleted file mode 100644 index e874d17e..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/SourceFile.hs +++ /dev/null @@ -1,105 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.SourceFile --- Description : Getting declarations from PureScript sourcefiles --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Getting declarations from PureScript sourcefiles ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.SourceFile - ( parseModulesFromFiles - , extractAstInformation - -- for tests - , extractSpans - , extractTypeAnnotations - ) where - -import Protolude - -import Control.Parallel.Strategies (withStrategy, parList, rseq) -import Data.Map qualified as Map -import Language.PureScript.CST qualified as CST -import Language.PureScript.Ide.Error (IdeError) -import Language.PureScript.Ide.Types (DefinitionSites, IdeNamespace(..), IdeNamespaced(..), TypeAnnotations) -import Language.PureScript.Ide.Util (ideReadFile) -import Language.PureScript.AST.Declarations qualified as P -import Language.PureScript.AST.SourcePos qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.Types qualified as P - -parseModule :: FilePath -> Text -> Either FilePath (FilePath, P.Module) -parseModule path file = - case snd $ CST.parseFromFile path file of - Left _ -> Left path - Right m -> Right (path, m) - -parseModulesFromFiles - :: (MonadIO m, MonadError IdeError m) - => [FilePath] - -> m [Either FilePath (FilePath, P.Module)] -parseModulesFromFiles paths = do - files <- traverse ideReadFile paths - pure (inParallel (map (uncurry parseModule) files)) - where - inParallel :: [Either e (k, a)] -> [Either e (k, a)] - inParallel = withStrategy (parList rseq) - --- | Extracts AST information from a parsed module -extractAstInformation - :: P.Module - -> (DefinitionSites P.SourceSpan, TypeAnnotations) -extractAstInformation (P.Module moduleSpan _ mn decls _) = - let definitions = - Map.insert - (IdeNamespaced IdeNSModule (P.runModuleName mn)) moduleSpan - (Map.fromList (concatMap extractSpans decls)) - typeAnnotations = Map.fromList (extractTypeAnnotations decls) - in (definitions, typeAnnotations) - --- | Extracts type annotations for functions from a given Module -extractTypeAnnotations :: [P.Declaration] -> [(P.Ident, P.SourceType)] -extractTypeAnnotations = mapMaybe (map P.unwrapTypeDeclaration . P.getTypeDeclaration) - --- | Given a surrounding Sourcespan and a Declaration from the PS AST, extracts --- definition sites inside that Declaration. -extractSpans - :: P.Declaration - -- ^ The declaration to extract spans from - -> [(IdeNamespaced, P.SourceSpan)] - -- ^ Declarations and their source locations -extractSpans d = case d of - P.ValueDecl (ss, _) i _ _ _ -> - [(IdeNamespaced IdeNSValue (P.runIdent i), ss)] - P.TypeSynonymDeclaration (ss, _) name _ _ -> - [(IdeNamespaced IdeNSType (P.runProperName name), ss)] - P.TypeClassDeclaration (ss, _) name _ _ _ members -> - (IdeNamespaced IdeNSType (P.runProperName name), ss) : concatMap extractSpans' members - P.DataDeclaration (ss, _) _ name _ ctors -> - (IdeNamespaced IdeNSType (P.runProperName name), ss) : map dtorSpan ctors - P.FixityDeclaration (ss, _) (Left (P.ValueFixity _ _ opName)) -> - [(IdeNamespaced IdeNSValue (P.runOpName opName), ss)] - P.FixityDeclaration (ss, _) (Right (P.TypeFixity _ _ opName)) -> - [(IdeNamespaced IdeNSType (P.runOpName opName), ss)] - P.ExternDeclaration (ss, _) ident _ -> - [(IdeNamespaced IdeNSValue (P.runIdent ident), ss)] - P.ExternDataDeclaration (ss, _) name _ -> - [(IdeNamespaced IdeNSType (P.runProperName name), ss)] - _ -> [] - where - dtorSpan :: P.DataConstructorDeclaration -> (IdeNamespaced, P.SourceSpan) - dtorSpan P.DataConstructorDeclaration{ P.dataCtorName = name, P.dataCtorAnn = (ss, _) } = - (IdeNamespaced IdeNSValue (P.runProperName name), ss) - -- We need this special case to be able to also get the position info for - -- typeclass member functions. Typedeclarations would clash with value - -- declarations for non-typeclass members, which is why we can't handle them - -- in extractSpans. - extractSpans' dP = case dP of - P.TypeDeclaration (P.TypeDeclarationData (ss', _) ident _) -> - [(IdeNamespaced IdeNSValue (P.runIdent ident), ss')] - _ -> [] diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/State.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/State.hs deleted file mode 100644 index 73e94519..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/State.hs +++ /dev/null @@ -1,482 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.State --- Description : Functions to access psc-ide's state --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Functions to access psc-ide's state ------------------------------------------------------------------------------ - -{-# LANGUAGE PackageImports #-} -{-# LANGUAGE TypeApplications #-} - -module Language.PureScript.Ide.State - ( getLoadedModulenames - , getExternFiles - , getFileState - , toIdeDeclarationAnn - , resetIdeState - , cacheRebuild - , cachedRebuild - , insertExterns - , insertModule - , insertExternsSTM - , getAllModules - , populateVolatileState - , populateVolatileStateSync - , populateVolatileStateSTM - , getOutputDirectory - , runQuery - , getSqliteFilePath - , updateCacheTimestamp - -- for tests - , resolveOperatorsForModule - , resolveInstances - , resolveDataConstructorsForModule - , escapeSQL - ) where - -import Protolude hiding (moduleName, unzip) - -import Control.Concurrent.STM (TVar, modifyTVar, readTVar, readTVarIO, writeTVar) -import Control.Lens (Ixed(..), preview, view, (%~), (.~), (^.)) -import "monad-logger" Control.Monad.Logger (MonadLogger, logWarnN) -import Data.IORef (readIORef, writeIORef) -import Data.Map.Lazy qualified as Map -import Data.Time.Clock (UTCTime) -import Data.Zip (unzip) -import Language.PureScript qualified as P -import Language.PureScript.Docs.Convert.Single (convertComments) -import Language.PureScript.Externs (ExternsDeclaration(..), ExternsFile(..)) -import Language.PureScript.Make.Actions (cacheDbFile) -import Language.PureScript.Ide.Externs (convertExterns) -import Language.PureScript.Ide.Reexports (ReexportResult(..), prettyPrintReexportResult, reexportHasFailures, resolveReexports) -import Language.PureScript.Ide.SourceFile (extractAstInformation) -import Language.PureScript.Ide.Types -import Language.PureScript.Ide.Util (discardAnn, opNameT, properNameT, runLogger) -import System.Directory (getModificationTime) -import Database.SQLite.Simple qualified as SQLite -import Data.Text qualified as T - --- | Resets all State inside psc-ide -resetIdeState :: Ide m => m () -resetIdeState = do - ideVar <- ideStateVar <$> ask - liftIO (atomically (writeTVar ideVar emptyIdeState)) - -getOutputDirectory :: Ide m => m FilePath -getOutputDirectory = do - confOutputPath . ideConfiguration <$> ask - -getSqliteFilePath :: Ide m => m FilePath -getSqliteFilePath = do - sqliteFilePath . ideConfiguration <$> ask - -runQuery :: SQLite.FromRow r => Ide m => Text -> m [r] -runQuery q = do - -- Debug.traceM $ show q - IdeEnvironment{..} <- ask - liftIO $ query q - -escapeSQL :: Text -> Text -escapeSQL = T.replace "'" "''" - -getCacheTimestamp :: Ide m => m (Maybe UTCTime) -getCacheTimestamp = do - x <- ideCacheDbTimestamp <$> ask - liftIO (readIORef x) - -readCacheTimestamp :: Ide m => m (Maybe UTCTime) -readCacheTimestamp = do - cacheDb <- cacheDbFile <$> getOutputDirectory - liftIO (hush <$> try @SomeException (getModificationTime cacheDb)) - -updateCacheTimestamp :: Ide m => m (Maybe (Maybe UTCTime, Maybe UTCTime)) -updateCacheTimestamp = do - old <- getCacheTimestamp - new <- readCacheTimestamp - if old == new - then pure Nothing - else do - ts <- ideCacheDbTimestamp <$> ask - liftIO (writeIORef ts new) - pure (Just (old, new)) - --- | Gets the loaded Modulenames -getLoadedModulenames :: Ide m => m [P.ModuleName] -getLoadedModulenames = Map.keys <$> getExternFiles - --- | Gets all loaded ExternFiles -getExternFiles :: Ide m => m (ModuleMap ExternsFile) -getExternFiles = fsExterns <$> getFileState - --- | Insert a Module into Stage1 of the State -insertModule :: Ide m => (FilePath, P.Module) -> m () -insertModule module' = do - stateVar <- ideStateVar <$> ask - liftIO . atomically $ insertModuleSTM stateVar module' - --- | STM version of insertModule -insertModuleSTM :: TVar IdeState -> (FilePath, P.Module) -> STM () -insertModuleSTM ref (fp, module') = - modifyTVar ref $ \x -> - x { ideFileState = (ideFileState x) { - fsModules = Map.insert - (P.getModuleName module') - (module', fp) - (fsModules (ideFileState x))}} - --- | Retrieves the FileState from the State. This includes loaded Externfiles --- and parsed Modules -getFileState :: Ide m => m IdeFileState -getFileState = do - st <- ideStateVar <$> ask - ideFileState <$> liftIO (readTVarIO st) - --- | STM version of getFileState -getFileStateSTM :: TVar IdeState -> STM IdeFileState -getFileStateSTM ref = ideFileState <$> readTVar ref - --- | Retrieves VolatileState from the State. --- This includes the denormalized Declarations and cached rebuilds -getVolatileState :: Ide m => m IdeVolatileState -getVolatileState = do - st <- ideStateVar <$> ask - liftIO (atomically (getVolatileStateSTM st)) - --- | STM version of getVolatileState -getVolatileStateSTM :: TVar IdeState -> STM IdeVolatileState -getVolatileStateSTM st = ideVolatileState <$> readTVar st - --- | Sets the VolatileState inside Ide's state -setVolatileStateSTM :: TVar IdeState -> IdeVolatileState -> STM () -setVolatileStateSTM ref vs = do - modifyTVar ref $ \x -> - x {ideVolatileState = vs} - pure () - --- | Checks if the given ModuleName matches the last rebuild cache and if it --- does returns all loaded definitions + the definitions inside the rebuild --- cache -getAllModules :: Ide m => Maybe P.ModuleName -> m (ModuleMap [IdeDeclarationAnn]) -getAllModules mmoduleName = do - declarations <- vsDeclarations <$> getVolatileState - rebuild <- cachedRebuild - case mmoduleName of - Nothing -> pure declarations - Just moduleName -> - case rebuild of - Just (cachedModulename, ef) - | cachedModulename == moduleName -> do - AstData asts <- vsAstData <$> getVolatileState - let - ast = - fromMaybe (Map.empty, Map.empty) (Map.lookup moduleName asts) - cachedModule = - resolveLocationsForModule ast (fst (convertExterns ef)) - tmp = - Map.insert moduleName cachedModule declarations - resolved = - Map.adjust (resolveOperatorsForModule tmp) moduleName tmp - - pure resolved - _ -> pure declarations - --- | Adds an ExternsFile into psc-ide's FileState. This does not populate the --- VolatileState, which needs to be done after all the necessary Externs and --- SourceFiles have been loaded. -insertExterns :: Ide m => ExternsFile -> m () -insertExterns ef = do - st <- ideStateVar <$> ask - liftIO (atomically (insertExternsSTM st ef)) - --- | STM version of insertExterns -insertExternsSTM :: TVar IdeState -> ExternsFile -> STM () -insertExternsSTM ref ef = - modifyTVar ref $ \x -> - x { ideFileState = (ideFileState x) { - fsExterns = Map.insert (efModuleName ef) ef (fsExterns (ideFileState x))}} - --- | Sets rebuild cache to the given ExternsFile -cacheRebuild :: Ide m => ExternsFile -> m () -cacheRebuild ef = do - st <- ideStateVar <$> ask - liftIO . atomically . modifyTVar st $ \x -> - x { ideVolatileState = (ideVolatileState x) { - vsCachedRebuild = Just (efModuleName ef, ef)}} - --- | Retrieves the rebuild cache -cachedRebuild :: Ide m => m (Maybe (P.ModuleName, ExternsFile)) -cachedRebuild = vsCachedRebuild <$> getVolatileState - --- | Resolves reexports and populates VolatileState with data to be used in queries. -populateVolatileStateSync :: (Ide m, MonadLogger m) => m () -populateVolatileStateSync = do - st <- ideStateVar <$> ask - results <- liftIO (atomically (populateVolatileStateSTM st)) - void $ Map.traverseWithKey - (\mn -> logWarnN . prettyPrintReexportResult (const (P.runModuleName mn))) - (Map.filter reexportHasFailures results) - -populateVolatileState :: Ide m => m (Async ()) -populateVolatileState = do - env <- ask - let ll = confLogLevel (ideConfiguration env) - -- populateVolatileState return Unit for now, so it's fine to discard this - -- result. We might want to block on this in a benchmarking situation. - liftIO (async (runLogger ll (runReaderT populateVolatileStateSync env))) - --- | STM version of populateVolatileState -populateVolatileStateSTM - :: TVar IdeState - -> STM (ModuleMap (ReexportResult [IdeDeclarationAnn])) -populateVolatileStateSTM ref = do - IdeFileState{fsExterns = externs, fsModules = modules} <- getFileStateSTM ref - -- We're not using the cached rebuild for anything other than preserving it - -- through the repopulation - rebuildCache <- vsCachedRebuild <$> getVolatileStateSTM ref - let asts = map (extractAstInformation . fst) modules - let (moduleDeclarations, reexportRefs) = unzip (Map.map convertExterns externs) - results = - moduleDeclarations - & map resolveDataConstructorsForModule - & resolveLocations asts - & resolveDocumentation (map fst modules) - & resolveInstances externs - & resolveOperators - & resolveReexports reexportRefs - setVolatileStateSTM ref (IdeVolatileState (AstData asts) (map reResolved results) rebuildCache) - pure results - -toIdeDeclarationAnn :: P.Module -> ExternsFile -> [IdeDeclarationAnn] -toIdeDeclarationAnn m e = results - where - asts = extractAstInformation m - (moduleDeclarations, _) = convertExterns e - results = - moduleDeclarations - & resolveDataConstructorsForModule - & resolveLocationsForModule asts - & resolveDocumentationForModule m - -- & resolveInstances externs - -- & resolveOperators - -- & resolveReexports reexportRefs - -resolveLocations - :: ModuleMap (DefinitionSites P.SourceSpan, TypeAnnotations) - -> ModuleMap [IdeDeclarationAnn] - -> ModuleMap [IdeDeclarationAnn] -resolveLocations asts = - Map.mapWithKey (\mn decls -> - maybe decls (flip resolveLocationsForModule decls) (Map.lookup mn asts)) - -resolveLocationsForModule - :: (DefinitionSites P.SourceSpan, TypeAnnotations) - -> [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -resolveLocationsForModule (defs, types) = - map convertDeclaration - where - convertDeclaration :: IdeDeclarationAnn -> IdeDeclarationAnn - convertDeclaration (IdeDeclarationAnn ann d) = convertDeclaration' - annotateFunction - annotateValue - annotateDataConstructor - annotateType - annotateType -- type classes live in the type namespace - annotateModule - d - where - annotateFunction x = IdeDeclarationAnn (ann { _annLocation = Map.lookup (IdeNamespaced IdeNSValue (P.runIdent x)) defs - , _annTypeAnnotation = Map.lookup x types - }) - annotateValue x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSValue x) defs}) - annotateDataConstructor x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSValue x) defs}) - annotateType x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSType x) defs}) - annotateModule x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSModule x) defs}) - -convertDeclaration' - :: (P.Ident -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> IdeDeclaration - -> IdeDeclarationAnn -convertDeclaration' annotateFunction annotateValue annotateDataConstructor annotateType annotateClass annotateModule d = - case d of - IdeDeclValue v -> - annotateFunction (v ^. ideValueIdent) d - IdeDeclType t -> - annotateType (t ^. ideTypeName . properNameT) d - IdeDeclTypeSynonym s -> - annotateType (s ^. ideSynonymName . properNameT) d - IdeDeclDataConstructor dtor -> - annotateDataConstructor (dtor ^. ideDtorName . properNameT) d - IdeDeclTypeClass tc -> - annotateClass (tc ^. ideTCName . properNameT) d - IdeDeclValueOperator operator -> - annotateValue (operator ^. ideValueOpName . opNameT) d - IdeDeclTypeOperator operator -> - annotateType (operator ^. ideTypeOpName . opNameT) d - IdeDeclModule mn -> - annotateModule (P.runModuleName mn) d - -resolveDocumentation - :: ModuleMap P.Module - -> ModuleMap [IdeDeclarationAnn] - -> ModuleMap [IdeDeclarationAnn] -resolveDocumentation modules = - Map.mapWithKey (\mn decls -> - maybe decls (flip resolveDocumentationForModule decls) (Map.lookup mn modules)) - -resolveDocumentationForModule - :: P.Module - -> [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -resolveDocumentationForModule (P.Module _ moduleComments moduleName sdecls _) = - map convertDecl - where - extractDeclComments :: P.Declaration -> [(P.Name, [P.Comment])] - extractDeclComments = \case - P.DataDeclaration (_, cs) _ ctorName _ ctors -> - (P.TyName ctorName, cs) : map dtorComments ctors - P.TypeClassDeclaration (_, cs) tyClassName _ _ _ members -> - (P.TyClassName tyClassName, cs) : concatMap extractDeclComments members - decl -> - maybe [] (\name' -> [(name', snd (P.declSourceAnn decl))]) (name decl) - - comments :: Map P.Name [P.Comment] - comments = Map.insert (P.ModName moduleName) moduleComments $ - Map.fromListWith (flip (<>)) $ concatMap extractDeclComments sdecls - - dtorComments :: P.DataConstructorDeclaration -> (P.Name, [P.Comment]) - dtorComments dcd = (P.DctorName (P.dataCtorName dcd), snd (P.dataCtorAnn dcd)) - - name :: P.Declaration -> Maybe P.Name - name (P.TypeDeclaration d) = Just $ P.IdentName $ P.tydeclIdent d - name decl = P.declName decl - - convertDecl :: IdeDeclarationAnn -> IdeDeclarationAnn - convertDecl (IdeDeclarationAnn ann d) = - convertDeclaration' - (annotateValue . P.IdentName) - (annotateValue . P.IdentName . P.Ident) - (annotateValue . P.DctorName . P.ProperName) - (annotateValue . P.TyName . P.ProperName) - (annotateValue . P.TyClassName . P.ProperName) - (annotateValue . P.ModName . P.moduleNameFromString) - d - where - docs :: P.Name -> Text - docs ident = fromMaybe "" $ convertComments =<< Map.lookup ident comments - - annotateValue ident = IdeDeclarationAnn (ann { _annDocumentation = Just $ docs ident }) - -resolveInstances - :: ModuleMap P.ExternsFile - -> ModuleMap [IdeDeclarationAnn] - -> ModuleMap [IdeDeclarationAnn] -resolveInstances externs declarations = - Map.foldr (flip (foldr go)) declarations - . Map.mapWithKey (\mn ef -> mapMaybe (extractInstances mn) (efDeclarations ef)) - $ externs - where - extractInstances mn P.EDInstance{..} = - case edInstanceClassName of - P.Qualified (P.ByModuleName classModule) className -> - Just (IdeInstance mn - edInstanceName - edInstanceTypes - edInstanceConstraints, classModule, className) - _ -> Nothing - extractInstances _ _ = Nothing - - go - :: (IdeInstance, P.ModuleName, P.ProperName 'P.ClassName) - -> ModuleMap [IdeDeclarationAnn] - -> ModuleMap [IdeDeclarationAnn] - go (ideInstance, classModule, className) acc' = - let - matchTC = - anyOf (idaDeclaration . _IdeDeclTypeClass . ideTCName) (== className) - updateDeclaration = - mapIf matchTC (idaDeclaration - . _IdeDeclTypeClass - . ideTCInstances - %~ (ideInstance :)) - in - acc' & ix classModule %~ updateDeclaration - -resolveOperators - :: ModuleMap [IdeDeclarationAnn] - -> ModuleMap [IdeDeclarationAnn] -resolveOperators modules = - map (resolveOperatorsForModule modules) modules - --- | Looks up the types and kinds for operators and assigns them to their --- declarations -resolveOperatorsForModule - :: ModuleMap [IdeDeclarationAnn] - -> [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -resolveOperatorsForModule modules = map (idaDeclaration %~ resolveOperator) - where - getDeclarations :: P.ModuleName -> [IdeDeclaration] - getDeclarations moduleName = - Map.lookup moduleName modules - & foldMap (map discardAnn) - - resolveOperator (IdeDeclValueOperator op) - | (P.Qualified (P.ByModuleName mn) (Left ident)) <- op ^. ideValueOpAlias = - let t = getDeclarations mn - & mapMaybe (preview _IdeDeclValue) - & filter (anyOf ideValueIdent (== ident)) - & map (view ideValueType) - & listToMaybe - in IdeDeclValueOperator (op & ideValueOpType .~ t) - | (P.Qualified (P.ByModuleName mn) (Right dtor)) <- op ^. ideValueOpAlias = - let t = getDeclarations mn - & mapMaybe (preview _IdeDeclDataConstructor) - & filter (anyOf ideDtorName (== dtor)) - & map (view ideDtorType) - & listToMaybe - in IdeDeclValueOperator (op & ideValueOpType .~ t) - resolveOperator (IdeDeclTypeOperator op) - | P.Qualified (P.ByModuleName mn) properName <- op ^. ideTypeOpAlias = - let k = getDeclarations mn - & mapMaybe (preview _IdeDeclType) - & filter (anyOf ideTypeName (== properName)) - & map (view ideTypeKind) - & listToMaybe - in IdeDeclTypeOperator (op & ideTypeOpKind .~ k) - resolveOperator x = x - - -mapIf :: Functor f => (b -> Bool) -> (b -> b) -> f b -> f b -mapIf p f = map (\x -> if p x then f x else x) - -resolveDataConstructorsForModule - :: [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -resolveDataConstructorsForModule decls = - map (idaDeclaration %~ resolveDataConstructors) decls - where - resolveDataConstructors :: IdeDeclaration -> IdeDeclaration - resolveDataConstructors decl = case decl of - IdeDeclType ty -> - IdeDeclType (ty & ideTypeDtors .~ fromMaybe [] (Map.lookup (ty ^. ideTypeName) dtors)) - _ -> - decl - - dtors = - decls - & mapMaybe (preview (idaDeclaration . _IdeDeclDataConstructor)) - & foldr (\(IdeDataConstructor name typeName type') -> - Map.insertWith (<>) typeName [(name, type')]) Map.empty diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/ToIde.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/ToIde.hs deleted file mode 100644 index f9e8aee9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/ToIde.hs +++ /dev/null @@ -1,147 +0,0 @@ -module Language.PureScript.Ide.ToIde where - -import Protolude hiding (moduleName, unzip) - -import Control.Lens ((^.)) -import Data.Map.Lazy qualified as Map -import Language.PureScript.Docs.Convert.Single (convertComments) -import Language.PureScript.Externs (ExternsFile(..)) -import Language.PureScript.Ide.Externs (convertExterns) -import Language.PureScript.Ide.SourceFile (extractAstInformation) -import Language.PureScript.Ide.Types -import Language.PureScript.Ide.Util (opNameT, properNameT) -import Language.PureScript.AST.Declarations (Module (..)) -import Language.PureScript.AST.SourcePos qualified as P -import Language.PureScript.Names qualified as P -import Language.PureScript.AST.Declarations qualified as P -import Language.PureScript.Comments qualified as P - -toIdeDeclarationAnn :: Module -> ExternsFile -> [IdeDeclarationAnn] -toIdeDeclarationAnn m e = results - where - asts = extractAstInformation m - (moduleDeclarations, _) = convertExterns e - results = - moduleDeclarations - -- & resolveDataConstructorsForModule - & resolveLocationsForModule asts - & resolveDocumentationForModule m - -- & resolveInstances externs - -- & resolveOperators - -- & resolveReexports reexportRefs - - -resolveLocationsForModule - :: (DefinitionSites P.SourceSpan, TypeAnnotations) - -> [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -resolveLocationsForModule (defs, types) = - map convertDeclaration - where - convertDeclaration :: IdeDeclarationAnn -> IdeDeclarationAnn - convertDeclaration (IdeDeclarationAnn ann d) = convertDeclaration' - annotateFunction - annotateValue - annotateDataConstructor - annotateType - annotateType -- type classes live in the type namespace - annotateModule - d - where - annotateFunction x = IdeDeclarationAnn (ann { _annLocation = Map.lookup (IdeNamespaced IdeNSValue (P.runIdent x)) defs - , _annTypeAnnotation = Map.lookup x types - }) - annotateValue x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSValue x) defs}) - annotateDataConstructor x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSValue x) defs}) - annotateType x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSType x) defs}) - annotateModule x = IdeDeclarationAnn (ann {_annLocation = Map.lookup (IdeNamespaced IdeNSModule x) defs}) - -convertDeclaration' - :: (P.Ident -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> (Text -> IdeDeclaration -> IdeDeclarationAnn) - -> IdeDeclaration - -> IdeDeclarationAnn -convertDeclaration' annotateFunction annotateValue annotateDataConstructor annotateType annotateClass annotateModule d = - case d of - IdeDeclValue v -> - annotateFunction (v ^. ideValueIdent) d - IdeDeclType t -> - annotateType (t ^. ideTypeName . properNameT) d - IdeDeclTypeSynonym s -> - annotateType (s ^. ideSynonymName . properNameT) d - IdeDeclDataConstructor dtor -> - annotateDataConstructor (dtor ^. ideDtorName . properNameT) d - IdeDeclTypeClass tc -> - annotateClass (tc ^. ideTCName . properNameT) d - IdeDeclValueOperator operator -> - annotateValue (operator ^. ideValueOpName . opNameT) d - IdeDeclTypeOperator operator -> - annotateType (operator ^. ideTypeOpName . opNameT) d - IdeDeclModule mn -> - annotateModule (P.runModuleName mn) d - -resolveDocumentationForModule - :: Module - -> [IdeDeclarationAnn] - -> [IdeDeclarationAnn] -resolveDocumentationForModule (Module _ moduleComments moduleName sdecls _) = - map convertDecl - where - extractDeclComments :: P.Declaration -> [(P.Name, [P.Comment])] - extractDeclComments = \case - P.DataDeclaration (_, cs) _ ctorName _ ctors -> - (P.TyName ctorName, cs) : map dtorComments ctors - P.TypeClassDeclaration (_, cs) tyClassName _ _ _ members -> - (P.TyClassName tyClassName, cs) : concatMap extractDeclComments members - decl -> - maybe [] (\name' -> [(name', snd (P.declSourceAnn decl))]) (name decl) - - comments :: Map.Map P.Name [P.Comment] - comments = Map.insert (P.ModName moduleName) moduleComments $ - Map.fromListWith (flip (<>)) $ concatMap extractDeclComments sdecls - - dtorComments :: P.DataConstructorDeclaration -> (P.Name, [P.Comment]) - dtorComments dcd = (P.DctorName (P.dataCtorName dcd), snd (P.dataCtorAnn dcd)) - - name :: P.Declaration -> Maybe P.Name - name (P.TypeDeclaration d) = Just $ P.IdentName $ P.tydeclIdent d - name decl = P.declName decl - - convertDecl :: IdeDeclarationAnn -> IdeDeclarationAnn - convertDecl (IdeDeclarationAnn ann d) = - convertDeclaration' - (annotateValue . P.IdentName) - (annotateValue . P.IdentName . P.Ident) - (annotateValue . P.DctorName . P.ProperName) - (annotateValue . P.TyName . P.ProperName) - (annotateValue . P.TyClassName . P.ProperName) - (annotateValue . P.ModName . P.moduleNameFromString) - d - where - docs :: P.Name -> Text - docs ident = fromMaybe "" $ convertComments =<< Map.lookup ident comments - - annotateValue ident = IdeDeclarationAnn (ann { _annDocumentation = Just $ docs ident }) - --- resolveDataConstructorsForModule --- :: [IdeDeclarationAnn] --- -> [IdeDeclarationAnn] --- resolveDataConstructorsForModule decls = --- map (idaDeclaration %~ resolveDataConstructors) decls --- where --- resolveDataConstructors :: IdeDeclaration -> IdeDeclaration --- resolveDataConstructors decl = case decl of --- IdeDeclType ty -> --- IdeDeclType (ty & ideTypeDtors .~ fromMaybe [] (Map.lookup (ty ^. ideTypeName) dtors)) --- _ -> --- decl --- --- dtors = --- decls --- & Map.mapMaybe (preview (idaDeclaration . _IdeDeclDataConstructor)) --- & foldr (\(IdeDataConstructor name typeName type') -> --- Map.insertWith (<>) typeName [(name, type')]) Map.empty diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Types.hs deleted file mode 100644 index b6cf3bee..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Types.hs +++ /dev/null @@ -1,347 +0,0 @@ --- | --- Type definitions for psc-ide - -{-# language DeriveAnyClass, NoGeneralizedNewtypeDeriving, TemplateHaskell #-} - -module Language.PureScript.Ide.Types where - -import Protolude hiding (moduleName) - -import Control.Concurrent.STM (TVar) -import Control.Lens (Getting, Traversal', makeLenses) -import Control.Monad.Fail (fail) -import Data.Aeson (ToJSON, FromJSON, (.=)) -import Data.Aeson qualified as Aeson -import Data.IORef (IORef) -import Data.Time.Clock (UTCTime) -import Data.Map.Lazy qualified as M -import Language.PureScript.Ide.Filter.Declaration (DeclarationType(..)) -import Language.PureScript.Names qualified as P -import Language.PureScript.Types qualified as P -import Language.PureScript.AST.Operators qualified as P -import Language.PureScript.AST.SourcePos qualified as P -import Language.PureScript.Externs qualified as P -import Language.PureScript.AST.Declarations qualified as P -import Language.PureScript.Errors qualified as P -import Language.PureScript.Errors.JSON qualified as P -import Database.SQLite.Simple qualified as SQLite -import Codec.Serialise (Serialise) -import Database.SQLite.Simple.ToField (ToField(..)) -import Database.SQLite.Simple (SQLData(SQLText)) - -type ModuleIdent = Text -type ModuleMap a = Map P.ModuleName a - -data IdeDeclaration - = IdeDeclValue IdeValue - | IdeDeclType IdeType - | IdeDeclTypeSynonym IdeTypeSynonym - | IdeDeclDataConstructor IdeDataConstructor - | IdeDeclTypeClass IdeTypeClass - | IdeDeclValueOperator IdeValueOperator - | IdeDeclTypeOperator IdeTypeOperator - | IdeDeclModule P.ModuleName - deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeValue = IdeValue - { _ideValueIdent :: P.Ident - , _ideValueType :: P.SourceType - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeType = IdeType - { _ideTypeName :: P.ProperName 'P.TypeName - , _ideTypeKind :: P.SourceType - , _ideTypeDtors :: [(P.ProperName 'P.ConstructorName, P.SourceType)] - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeTypeSynonym = IdeTypeSynonym - { _ideSynonymName :: P.ProperName 'P.TypeName - , _ideSynonymType :: P.SourceType - , _ideSynonymKind :: P.SourceType - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeDataConstructor = IdeDataConstructor - { _ideDtorName :: P.ProperName 'P.ConstructorName - , _ideDtorTypeName :: P.ProperName 'P.TypeName - , _ideDtorType :: P.SourceType - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeTypeClass = IdeTypeClass - { _ideTCName :: P.ProperName 'P.ClassName - , _ideTCKind :: P.SourceType - , _ideTCInstances :: [IdeInstance] - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeInstance = IdeInstance - { _ideInstanceModule :: P.ModuleName - , _ideInstanceName :: P.Ident - , _ideInstanceTypes :: [P.SourceType] - , _ideInstanceConstraints :: Maybe [P.SourceConstraint] - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeValueOperator = IdeValueOperator - { _ideValueOpName :: P.OpName 'P.ValueOpName - , _ideValueOpAlias :: P.Qualified (Either P.Ident (P.ProperName 'P.ConstructorName)) - , _ideValueOpPrecedence :: P.Precedence - , _ideValueOpAssociativity :: P.Associativity - , _ideValueOpType :: Maybe P.SourceType - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data IdeTypeOperator = IdeTypeOperator - { _ideTypeOpName :: P.OpName 'P.TypeOpName - , _ideTypeOpAlias :: P.Qualified (P.ProperName 'P.TypeName) - , _ideTypeOpPrecedence :: P.Precedence - , _ideTypeOpAssociativity :: P.Associativity - , _ideTypeOpKind :: Maybe P.SourceType - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -_IdeDeclValue :: Traversal' IdeDeclaration IdeValue -_IdeDeclValue f (IdeDeclValue x) = map IdeDeclValue (f x) -_IdeDeclValue _ x = pure x - -_IdeDeclType :: Traversal' IdeDeclaration IdeType -_IdeDeclType f (IdeDeclType x) = map IdeDeclType (f x) -_IdeDeclType _ x = pure x - -_IdeDeclTypeSynonym :: Traversal' IdeDeclaration IdeTypeSynonym -_IdeDeclTypeSynonym f (IdeDeclTypeSynonym x) = map IdeDeclTypeSynonym (f x) -_IdeDeclTypeSynonym _ x = pure x - -_IdeDeclDataConstructor :: Traversal' IdeDeclaration IdeDataConstructor -_IdeDeclDataConstructor f (IdeDeclDataConstructor x) = map IdeDeclDataConstructor (f x) -_IdeDeclDataConstructor _ x = pure x - -_IdeDeclTypeClass :: Traversal' IdeDeclaration IdeTypeClass -_IdeDeclTypeClass f (IdeDeclTypeClass x) = map IdeDeclTypeClass (f x) -_IdeDeclTypeClass _ x = pure x - -_IdeDeclValueOperator :: Traversal' IdeDeclaration IdeValueOperator -_IdeDeclValueOperator f (IdeDeclValueOperator x) = map IdeDeclValueOperator (f x) -_IdeDeclValueOperator _ x = pure x - -_IdeDeclTypeOperator :: Traversal' IdeDeclaration IdeTypeOperator -_IdeDeclTypeOperator f (IdeDeclTypeOperator x) = map IdeDeclTypeOperator (f x) -_IdeDeclTypeOperator _ x = pure x - -_IdeDeclModule :: Traversal' IdeDeclaration P.ModuleName -_IdeDeclModule f (IdeDeclModule x) = map IdeDeclModule (f x) -_IdeDeclModule _ x = pure x - -anyOf :: Getting Any s a -> (a -> Bool) -> s -> Bool -anyOf g p = getAny . getConst . g (Const . Any . p) - -makeLenses ''IdeValue -makeLenses ''IdeType -makeLenses ''IdeTypeSynonym -makeLenses ''IdeDataConstructor -makeLenses ''IdeTypeClass -makeLenses ''IdeValueOperator -makeLenses ''IdeTypeOperator - -data IdeDeclarationAnn = IdeDeclarationAnn - { _idaAnnotation :: Annotation - , _idaDeclaration :: IdeDeclaration - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -data Annotation - = Annotation - { _annLocation :: Maybe P.SourceSpan - , _annExportedFrom :: Maybe P.ModuleName - , _annTypeAnnotation :: Maybe P.SourceType - , _annDocumentation :: Maybe Text - } deriving (Show, Eq, Ord, Generic, NFData, Serialise) - -makeLenses ''Annotation -makeLenses ''IdeDeclarationAnn - -emptyAnn :: Annotation -emptyAnn = Annotation Nothing Nothing Nothing Nothing - -type DefinitionSites a = Map IdeNamespaced a -type TypeAnnotations = Map P.Ident P.SourceType -newtype AstData a = AstData (ModuleMap (DefinitionSites a, TypeAnnotations)) - -- ^ SourceSpans for the definition sites of values and types as well as type - -- annotations found in a module - deriving (Show, Eq, Ord, Functor, Foldable) - -data IdeLogLevel = LogDebug | LogPerf | LogAll | LogDefault | LogNone - deriving (Show, Eq) - -data IdeConfiguration = - IdeConfiguration - { confOutputPath :: FilePath - , sqliteFilePath :: FilePath - , confLogLevel :: IdeLogLevel - , confGlobs :: [FilePath] - , confGlobsFromFile :: Maybe FilePath - , confGlobsExclude :: [FilePath] - } - -data IdeEnvironment = - IdeEnvironment - { ideStateVar :: TVar IdeState - , ideConfiguration :: IdeConfiguration - , ideCacheDbTimestamp :: IORef (Maybe UTCTime) - , query :: forall a. SQLite.FromRow a => Text -> IO [a] - } - -type Ide m = (MonadIO m, MonadReader IdeEnvironment m) - -data IdeState = IdeState - { ideFileState :: IdeFileState - , ideVolatileState :: IdeVolatileState - } deriving (Show) - -emptyIdeState :: IdeState -emptyIdeState = IdeState emptyFileState emptyVolatileState - -emptyFileState :: IdeFileState -emptyFileState = IdeFileState M.empty M.empty - -emptyVolatileState :: IdeVolatileState -emptyVolatileState = IdeVolatileState (AstData M.empty) M.empty Nothing - - --- | @IdeFileState@ holds data that corresponds 1-to-1 to an entity on the --- filesystem. Externs correspond to the ExternsFiles the compiler emits into --- the output folder, and modules are parsed ASTs from source files. This means, --- that we can update single modules or ExternsFiles inside this state whenever --- the corresponding entity changes on the file system. -data IdeFileState = IdeFileState - { fsExterns :: ModuleMap P.ExternsFile - , fsModules :: ModuleMap (P.Module, FilePath) - } deriving (Show) - --- | @IdeVolatileState@ is derived from the @IdeFileState@ and needs to be --- invalidated and refreshed carefully. It holds @AstData@, which is the data we --- extract from the parsed ASTs, as well as the IdeDeclarations, which contain --- lots of denormalized data, so they need to fully rebuilt whenever --- @IdeFileState@ changes. The vsCachedRebuild field can hold a rebuild result --- with open imports which is used to provide completions for module private --- declarations -data IdeVolatileState = IdeVolatileState - { vsAstData :: AstData P.SourceSpan - , vsDeclarations :: ModuleMap [IdeDeclarationAnn] - , vsCachedRebuild :: Maybe (P.ModuleName, P.ExternsFile) - } deriving (Show) - -newtype Match a = Match (P.ModuleName, a) - deriving (Show, Eq, Functor) - --- | A completion as it gets sent to the editors -data Completion = Completion - { complModule :: Text - , complIdentifier :: Text - , complType :: Text - , complExpandedType :: Text - , complLocation :: Maybe P.SourceSpan - , complDocumentation :: Maybe Text - , complExportedFrom :: [P.ModuleName] - , complDeclarationType :: Maybe DeclarationType - } deriving (Show, Eq, Ord) - -instance ToJSON Completion where - toJSON Completion {..} = - Aeson.object - [ "module" .= complModule - , "identifier" .= complIdentifier - , "type" .= complType - , "expandedType" .= complExpandedType - , "definedAt" .= complLocation - , "documentation" .= complDocumentation - , "exportedFrom" .= map P.runModuleName complExportedFrom - , "declarationType" .= complDeclarationType - ] - -identifierFromDeclarationRef :: P.DeclarationRef -> Text -identifierFromDeclarationRef = \case - P.TypeRef _ name _ -> P.runProperName name - P.ValueRef _ ident -> P.runIdent ident - P.TypeClassRef _ name -> P.runProperName name - P.ValueOpRef _ op -> P.showOp op - P.TypeOpRef _ op -> P.showOp op - _ -> "" - -declarationType :: IdeDeclaration -> DeclarationType -declarationType decl = case decl of - IdeDeclValue _ -> Value - IdeDeclType _ -> Type - IdeDeclTypeSynonym _ -> Synonym - IdeDeclDataConstructor _ -> DataConstructor - IdeDeclTypeClass _ -> TypeClass - IdeDeclValueOperator _ -> ValueOperator - IdeDeclTypeOperator _ -> TypeOperator - IdeDeclModule _ -> Module -data Success = - CompletionResult [Completion] - | TextResult Text - | UsagesResult [P.SourceSpan] - | MultilineTextResult [Text] - | ImportList (P.ModuleName, [(P.ModuleName, P.ImportDeclarationType, Maybe P.ModuleName)]) - | ModuleList [ModuleIdent] - | RebuildSuccess P.MultipleErrors - deriving (Show) - -encodeSuccess :: ToJSON a => a -> Aeson.Value -encodeSuccess res = - Aeson.object ["resultType" .= ("success" :: Text), "result" .= res] - -instance ToJSON Success where - toJSON = \case - CompletionResult cs -> encodeSuccess cs - TextResult t -> encodeSuccess t - UsagesResult ssp -> encodeSuccess ssp - MultilineTextResult ts -> encodeSuccess ts - ImportList (moduleName, imports) -> - Aeson.object - [ "resultType" .= ("success" :: Text) - , "result" .= Aeson.object - [ "imports" .= map encodeImport imports - , "moduleName" .= P.runModuleName moduleName - ] - ] - ModuleList modules -> encodeSuccess modules - RebuildSuccess warnings -> encodeSuccess (P.toJSONErrors False P.Warning [] warnings) - -encodeImport :: (P.ModuleName, P.ImportDeclarationType, Maybe P.ModuleName) -> Aeson.Value -encodeImport (P.runModuleName -> mn, importType, map P.runModuleName -> qualifier) = case importType of - P.Implicit -> - Aeson.object $ - [ "module" .= mn - , "importType" .= ("implicit" :: Text) - ] ++ map ("qualifier" .=) (maybeToList qualifier) - P.Explicit refs -> - Aeson.object $ - [ "module" .= mn - , "importType" .= ("explicit" :: Text) - , "identifiers" .= (identifierFromDeclarationRef <$> refs) - ] ++ map ("qualifier" .=) (maybeToList qualifier) - P.Hiding refs -> - Aeson.object $ - [ "module" .= mn - , "importType" .= ("hiding" :: Text) - , "identifiers" .= (identifierFromDeclarationRef <$> refs) - ] ++ map ("qualifier" .=) (maybeToList qualifier) - --- | Denotes the different namespaces a name in PureScript can reside in. -data IdeNamespace = IdeNSValue | IdeNSType | IdeNSModule - deriving (Show, Eq, Ord) - -instance FromJSON IdeNamespace where - parseJSON = Aeson.withText "Namespace" $ \case - "value" -> pure IdeNSValue - "type" -> pure IdeNSType - "module" -> pure IdeNSModule - s -> fail ("Unknown namespace: " <> show s) - -toText :: IdeNamespace -> Text -toText IdeNSValue = "value" -toText IdeNSType = "type" -toText IdeNSModule = "module" - -instance ToField IdeNamespace where - toField n = SQLText $ toText n - --- | A name tagged with a namespace -data IdeNamespaced = IdeNamespaced IdeNamespace Text - deriving (Show, Eq, Ord) diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Usage.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Usage.hs deleted file mode 100644 index 3e773efe..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Usage.hs +++ /dev/null @@ -1,161 +0,0 @@ -module Language.PureScript.Ide.Usage - ( findReexportingModules - , directDependants - , eligibleModules - , applySearch - , findUsages - ) where - -import Protolude hiding (moduleName) - -import Control.Lens (preview) -import Data.Map qualified as Map -import Data.Set qualified as Set -import Language.PureScript qualified as P -import Language.PureScript.Ide.State (getAllModules, getFileState) -import Language.PureScript.Ide.Types -import Language.PureScript.Ide.Util (identifierFromIdeDeclaration, namespaceForDeclaration) - --- | --- How we find usages, given an IdeDeclaration and the module it was defined in: --- --- 1. Find all modules that reexport the given declaration --- 2. Find all modules that import from those modules, and while traversing the --- imports build a specification for how the identifier can be found in the --- module. --- 3. Apply the collected search specifications and collect the results -findUsages - :: Ide m - => IdeDeclaration - -> P.ModuleName - -> m (ModuleMap (NonEmpty P.SourceSpan)) -findUsages declaration moduleName = do - ms <- getAllModules Nothing - asts <- Map.map fst . fsModules <$> getFileState - let elig = eligibleModules (moduleName, declaration) ms asts - pure - $ Map.mapMaybe nonEmpty - $ Map.mapWithKey (\mn searches -> - foldMap (\m -> foldMap (applySearch m) searches) (Map.lookup mn asts)) elig - --- | A declaration can either be imported qualified, or unqualified. All the --- information we need to find usages through a Traversal is thus captured in --- the `Search` type. -type Search = P.Qualified IdeDeclaration - -findReexportingModules - :: (P.ModuleName, IdeDeclaration) - -- ^ The declaration and the module it is defined in for which we are - -- searching usages - -> ModuleMap [IdeDeclarationAnn] - -- ^ Our declaration cache. Needs to have reexports resolved - -> [P.ModuleName] - -- ^ All the modules that reexport the declaration. This does NOT include - -- the defining module -findReexportingModules (moduleName, declaration) decls = - Map.keys (Map.filter (any hasReexport) decls) - where - hasReexport d = - (d & _idaDeclaration & identifierFromIdeDeclaration) == identifierFromIdeDeclaration declaration - && (d & _idaAnnotation & _annExportedFrom) == Just moduleName - && (d & _idaDeclaration & namespaceForDeclaration) == namespaceForDeclaration declaration - -directDependants :: IdeDeclaration -> ModuleMap P.Module -> P.ModuleName -> ModuleMap (NonEmpty Search) -directDependants declaration modules mn = Map.mapMaybe (nonEmpty . go) modules - where - go :: P.Module -> [Search] - go = foldMap isImporting . P.getModuleDeclarations - - isImporting d = case d of - P.ImportDeclaration _ mn' it qual | mn == mn' -> P.Qualified (P.byMaybeModuleName qual) <$> case it of - P.Implicit -> pure declaration - P.Explicit refs - | any (declaration `matchesRef`) refs -> pure declaration - P.Explicit _ -> [] - P.Hiding refs - | not (any (declaration `matchesRef`) refs) -> pure declaration - P.Hiding _ -> [] - _ -> [] - --- | Determines whether an IdeDeclaration is referenced by a DeclarationRef. --- --- TODO(Christoph): We should also extract the spans of matching refs here, --- since they also count as a usage (at least for rename refactorings) -matchesRef :: IdeDeclaration -> P.DeclarationRef -> Bool -matchesRef declaration ref = case declaration of - IdeDeclValue valueDecl -> case ref of - P.ValueRef _ i -> i == _ideValueIdent valueDecl - _ -> False - IdeDeclType typeDecl -> case ref of - P.TypeRef _ tn _ -> tn == _ideTypeName typeDecl - _ -> False - IdeDeclTypeSynonym synonym -> case ref of - P.TypeRef _ tn _ -> tn == _ideSynonymName synonym - _ -> False - IdeDeclDataConstructor dtor -> case ref of - P.TypeRef _ tn dtors - -- We check if the given data constructor constructs the type imported - -- here. - -- This way we match `Just` with an import like `import Data.Maybe (Maybe(..))` - | _ideDtorTypeName dtor == tn -> - maybe True (elem (_ideDtorName dtor)) dtors - _ -> False - IdeDeclTypeClass typeClass -> case ref of - P.TypeClassRef _ name -> name == _ideTCName typeClass - _ -> False - IdeDeclValueOperator valueOperator -> case ref of - P.ValueOpRef _ opName -> opName == _ideValueOpName valueOperator - _ -> False - IdeDeclTypeOperator typeOperator -> case ref of - P.TypeOpRef _ opName -> opName == _ideTypeOpName typeOperator - _ -> False - IdeDeclModule m -> case ref of - P.ModuleRef _ mn -> m == mn - _ -> False - -eligibleModules - :: (P.ModuleName, IdeDeclaration) - -> ModuleMap [IdeDeclarationAnn] - -> ModuleMap P.Module - -> ModuleMap (NonEmpty Search) -eligibleModules query@(moduleName, declaration) decls modules = - let - searchDefiningModule = P.Qualified P.ByNullSourcePos declaration :| [] - in - Map.insert moduleName searchDefiningModule $ - foldMap (directDependants declaration modules) (moduleName :| findReexportingModules query decls) - --- | Finds all usages for a given `Search` throughout a module -applySearch :: P.Module -> Search -> [P.SourceSpan] -applySearch module_ search = - foldMap findUsageInDeclaration decls - where - decls = P.getModuleDeclarations module_ - findUsageInDeclaration = - let - (extr, _, _, _, _) = P.everythingWithScope mempty goExpr goBinder mempty mempty - in - extr mempty - - goExpr scope expr = case expr of - P.Var sp i - | Just ideValue <- preview _IdeDeclValue (P.disqualify search) - , P.isQualified search - || not (P.LocalIdent (_ideValueIdent ideValue) `Set.member` scope) -> - [sp | map P.runIdent i == map identifierFromIdeDeclaration search] - P.Constructor sp name - | Just ideDtor <- traverse (preview _IdeDeclDataConstructor) search -> - [sp | name == map _ideDtorName ideDtor] - P.Op sp opName - | Just ideOp <- traverse (preview _IdeDeclValueOperator) search -> - [sp | opName == map _ideValueOpName ideOp] - _ -> [] - - goBinder _ binder = case binder of - P.ConstructorBinder sp ctorName _ - | Just ideDtor <- traverse (preview _IdeDeclDataConstructor) search -> - [sp | ctorName == map _ideDtorName ideDtor] - P.OpBinder sp opName - | Just op <- traverse (preview _IdeDeclValueOperator) search -> - [sp | opName == map _ideValueOpName op] - _ -> [] diff --git a/claude-help/original-compiler/src/Language/PureScript/Ide/Util.hs b/claude-help/original-compiler/src/Language/PureScript/Ide/Util.hs deleted file mode 100644 index bfbb38bf..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Ide/Util.hs +++ /dev/null @@ -1,124 +0,0 @@ ------------------------------------------------------------------------------ --- --- Module : Language.PureScript.Ide.Util --- Description : Generally useful functions and conversions --- Copyright : Christoph Hegemann 2016 --- License : MIT (http://opensource.org/licenses/MIT) --- --- Maintainer : Christoph Hegemann --- Stability : experimental --- --- | --- Generally useful functions ------------------------------------------------------------------------------ - -module Language.PureScript.Ide.Util - ( identifierFromIdeDeclaration - , unwrapMatch - , namespaceForDeclaration - , encodeT - , decodeT - , discardAnn - , withEmptyAnn - , valueOperatorAliasT - , typeOperatorAliasT - , properNameT - , identT - , opNameT - , ideReadFile - , module Language.PureScript.Ide.Logging - ) where - -import Protolude hiding (decodeUtf8, encodeUtf8, to) - -import Control.Lens (Getting, to, (^.)) -import Data.Aeson (FromJSON, ToJSON, eitherDecode, encode) -import Data.Text qualified as T -import Data.Text.Lazy qualified as TL -import Data.Text.Lazy.Encoding as TLE -import Language.PureScript.Ide.Error (IdeError(..)) -import Language.PureScript.Ide.Logging -import Language.PureScript.Ide.Types (IdeDeclaration(..), IdeDeclarationAnn(..), IdeNamespace(..), Match(..), emptyAnn, ideDtorName, ideSynonymName, ideTCName, ideTypeName, ideTypeOpName, ideValueIdent, ideValueOpName) -import System.IO.UTF8 (readUTF8FileT) -import System.Directory (makeAbsolute) -import Language.PureScript.Names qualified as P - -identifierFromIdeDeclaration :: IdeDeclaration -> Text -identifierFromIdeDeclaration d = case d of - IdeDeclValue v -> v ^. ideValueIdent . identT - IdeDeclType t -> t ^. ideTypeName . properNameT - IdeDeclTypeSynonym s -> s ^. ideSynonymName . properNameT - IdeDeclDataConstructor dtor -> dtor ^. ideDtorName . properNameT - IdeDeclTypeClass tc -> tc ^. ideTCName . properNameT - IdeDeclValueOperator op -> op ^. ideValueOpName & P.runOpName - IdeDeclTypeOperator op -> op ^. ideTypeOpName & P.runOpName - IdeDeclModule name -> P.runModuleName name - -namespaceForDeclaration :: IdeDeclaration -> IdeNamespace -namespaceForDeclaration d = case d of - IdeDeclValue _ -> IdeNSValue - IdeDeclType _ -> IdeNSType - IdeDeclTypeSynonym _ -> IdeNSType - IdeDeclDataConstructor _ -> IdeNSValue - IdeDeclTypeClass _ -> IdeNSType - IdeDeclValueOperator _ -> IdeNSValue - IdeDeclTypeOperator _ -> IdeNSType - IdeDeclModule _ -> IdeNSModule - -discardAnn :: IdeDeclarationAnn -> IdeDeclaration -discardAnn (IdeDeclarationAnn _ d) = d - -withEmptyAnn :: IdeDeclaration -> IdeDeclarationAnn -withEmptyAnn = IdeDeclarationAnn emptyAnn - -unwrapMatch :: Match a -> a -unwrapMatch (Match (_, ed)) = ed - -valueOperatorAliasT - :: P.Qualified (Either P.Ident (P.ProperName 'P.ConstructorName)) -> Text -valueOperatorAliasT = - P.showQualified $ either P.runIdent P.runProperName - -typeOperatorAliasT - :: P.Qualified (P.ProperName 'P.TypeName) -> Text -typeOperatorAliasT = - P.showQualified P.runProperName - -encodeT :: (ToJSON a) => a -> Text -encodeT = TL.toStrict . TLE.decodeUtf8 . encode - -decodeT :: (FromJSON a) => Text -> Either Text a -decodeT = first T.pack . eitherDecode . TLE.encodeUtf8 . TL.fromStrict - -properNameT :: Getting r (P.ProperName a) Text -properNameT = to P.runProperName - -identT :: Getting r P.Ident Text -identT = to P.runIdent - -opNameT :: Getting r (P.OpName a) Text -opNameT = to P.runOpName - -ideReadFile' - :: (MonadIO m, MonadError IdeError m) - => (FilePath -> IO Text) - -> FilePath - -> m (FilePath, Text) -ideReadFile' fileReader fp = do - absPath <- liftIO (try (makeAbsolute fp)) >>= \case - Left (err :: IOException) -> - throwError - (GeneralError - ("Couldn't resolve path for: " <> show fp <> ", Error: " <> show err)) - Right absPath -> pure absPath - contents <- liftIO (try (fileReader absPath)) >>= \case - Left (err :: IOException) -> - throwError - (GeneralError - ("Couldn't find file at: " <> show absPath <> ", Error: " <> show err)) - Right contents -> - pure contents - pure (absPath, contents) - -ideReadFile :: (MonadIO m, MonadError IdeError m) => FilePath -> m (FilePath, Text) -ideReadFile = ideReadFile' readUTF8FileT diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive.hs deleted file mode 100644 index 8248b679..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive.hs +++ /dev/null @@ -1,366 +0,0 @@ -{-# LANGUAGE DoAndIfThenElse #-} - -{-# OPTIONS_GHC -Wwarn #-} - -module Language.PureScript.Interactive - ( handleCommand - , module Interactive - - -- TODO: remove these exports - , make - , runMake - ) where - -import Prelude -import Protolude (ordNub) - -import Data.List (sort, find, foldl') -import Data.Maybe (fromMaybe, mapMaybe) -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Text (Text) -import Data.Text qualified as T - -import Control.Monad.IO.Class (MonadIO, liftIO) -import Control.Monad.State.Class (MonadState(..), gets, modify) -import Control.Monad.Reader.Class (MonadReader, asks) -import Control.Monad.Trans.Except (ExceptT(..), runExceptT) -import Control.Monad.Trans.State.Strict (StateT, runStateT, evalStateT) -import Control.Monad.Writer.Strict (Writer(), runWriter) - -import Language.PureScript qualified as P -import Language.PureScript.CST qualified as CST -import Language.PureScript.Names qualified as N -import Language.PureScript.Constants.Prim qualified as C - -import Language.PureScript.Interactive.Completion as Interactive -import Language.PureScript.Interactive.IO as Interactive -import Language.PureScript.Interactive.Message as Interactive -import Language.PureScript.Interactive.Module as Interactive -import Language.PureScript.Interactive.Parser as Interactive -import Language.PureScript.Interactive.Printer as Interactive -import Language.PureScript.Interactive.Types as Interactive - -import System.Directory (getCurrentDirectory) -import System.FilePath (()) -import System.FilePath.Glob (glob) -import Language.PureScript.TypeChecker.Monad (liftTypeCheckM) - --- | Pretty-print errors -printErrors :: MonadIO m => P.MultipleErrors -> m () -printErrors errs = liftIO $ do - pwd <- getCurrentDirectory - putStrLn $ P.prettyPrintMultipleErrors P.defaultPPEOptions {P.ppeRelativeDirectory = pwd} errs - --- | This is different than the runMake in 'Language.PureScript.Make' in that it specifies the --- options and ignores the warning messages. -runMake :: P.Make a -> IO (Either P.MultipleErrors a) -runMake mk = fst <$> P.runMake P.defaultOptions mk - --- | Rebuild a module, using the cached externs data for dependencies. -rebuild - :: [P.ExternsFile] - -> P.Module - -> P.Make (P.ExternsFile, P.Environment) -rebuild loadedExterns m = do - externs <- P.rebuildModule buildActions loadedExterns m - return (externs, foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment (loadedExterns ++ [externs])) - where - buildActions :: P.MakeActions P.Make - buildActions = - (P.buildMakeActions modulesDir - filePathMap - M.empty - False) { P.progress = const (return ()) } - - filePathMap :: M.Map P.ModuleName (Either P.RebuildPolicy FilePath) - filePathMap = M.singleton (P.getModuleName m) (Left P.RebuildAlways) - --- | Build the collection of modules from scratch. This is usually done on startup. -make - :: [(FilePath, CST.PartialResult P.Module)] - -> P.Make ([P.ExternsFile], P.Environment) -make ms = do - foreignFiles <- P.inferForeignModules filePathMap - externs <- P.make (buildActions foreignFiles) (map snd ms) - return (externs, foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment externs) - where - buildActions :: M.Map P.ModuleName FilePath -> P.MakeActions P.Make - buildActions foreignFiles = - P.buildMakeActions modulesDir - filePathMap - foreignFiles - False - - filePathMap :: M.Map P.ModuleName (Either P.RebuildPolicy FilePath) - filePathMap = M.fromList $ map (\(fp, m) -> (P.getModuleName $ CST.resPartial m, Right fp)) ms - --- | Performs a PSCi command -handleCommand - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => (String -> m ()) -- ^ evaluate JS - -> m () -- ^ reload - -> (String -> m ()) -- ^ print into console - -> Command - -> m () -handleCommand _ _ p ShowHelp = p helpMessage -handleCommand _ r _ ReloadState = handleReloadState r -handleCommand _ r _ ClearState = handleClearState r -handleCommand e _ _ (Expression val) = handleExpression e val -handleCommand _ _ _ (Import im) = handleImport im -handleCommand _ _ _ (Decls l) = handleDecls l -handleCommand _ _ p (TypeOf val) = handleTypeOf p val -handleCommand _ _ p (KindOf typ) = handleKindOf p typ -handleCommand _ _ p (BrowseModule moduleName) = handleBrowse p moduleName -handleCommand _ _ p (ShowInfo QueryLoaded) = handleShowLoadedModules p -handleCommand _ _ p (ShowInfo QueryImport) = handleShowImportedModules p -handleCommand _ _ p (ShowInfo QueryPrint) = handleShowPrint p -handleCommand _ _ p (CompleteStr prefix) = handleComplete p prefix -handleCommand _ _ p (SetInteractivePrint ip) = handleSetInteractivePrint p ip -handleCommand _ _ _ _ = P.internalError "handleCommand: unexpected command" - --- | Reload the application state -handleReloadState - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => m () - -> m () -handleReloadState reload = do - modify $ updateLets (const []) - globs <- asks psciFileGlobs - files <- liftIO $ concat <$> traverse glob globs - e <- runExceptT $ do - modules <- ExceptT . liftIO $ loadAllModules files - (externs, _) <- ExceptT . liftIO . runMake . make $ fmap CST.pureResult <$> modules - return (map snd modules, externs) - case e of - Left errs -> printErrors errs - Right (modules, externs) -> do - modify (updateLoadedExterns (const (zip modules externs))) - reload - --- | Clear the application state -handleClearState - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => m () - -> m () -handleClearState reload = do - modify $ updateImportedModules (const []) - handleReloadState reload - --- | Takes a value expression and evaluates it with the current state. -handleExpression - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => (String -> m ()) - -> P.Expr - -> m () -handleExpression evaluate val = do - st <- get - let m = createTemporaryModule True st val - e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m - case e of - Left errs -> printErrors errs - Right _ -> do - js <- liftIO $ readFile (modulesDir "$PSCI" "index.js") - evaluate js - --- | --- Takes a list of declarations and updates the environment, then run a make. If the declaration fails, --- restore the original environment. --- -handleDecls - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => [P.Declaration] - -> m () -handleDecls ds = do - st <- gets (updateLets (++ ds)) - let m = createTemporaryModule False st (P.Literal P.nullSourceSpan (P.ObjectLiteral [])) - e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m - case e of - Left err -> printErrors err - Right _ -> put st - --- | Show actual loaded modules in psci. -handleShowLoadedModules - :: MonadState PSCiState m - => (String -> m ()) - -> m () -handleShowLoadedModules print' = do - loadedModules <- gets psciLoadedExterns - print' $ readModules loadedModules - where - readModules = unlines . sort . ordNub . map (T.unpack . P.runModuleName . P.getModuleName . fst) - --- | Show the imported modules in psci. -handleShowImportedModules - :: MonadState PSCiState m - => (String -> m ()) - -> m () -handleShowImportedModules print' = do - importedModules <- psciImportedModules <$> get - print' $ showModules importedModules - where - showModules = unlines . sort . map (T.unpack . showModule) - showModule (mn, declType, asQ) = - "import " <> N.runModuleName mn <> showDeclType declType <> - foldMap (\mn' -> " as " <> N.runModuleName mn') asQ - - showDeclType P.Implicit = "" - showDeclType (P.Explicit refs) = refsList refs - showDeclType (P.Hiding refs) = " hiding " <> refsList refs - refsList refs = " (" <> commaList (mapMaybe showRef refs) <> ")" - - showRef :: P.DeclarationRef -> Maybe Text - showRef (P.TypeRef _ pn dctors) = - Just $ N.runProperName pn <> "(" <> maybe ".." (commaList . map N.runProperName) dctors <> ")" - showRef (P.TypeOpRef _ op) = - Just $ "type " <> N.showOp op - showRef (P.ValueRef _ ident) = - Just $ N.runIdent ident - showRef (P.ValueOpRef _ op) = - Just $ N.showOp op - showRef (P.TypeClassRef _ pn) = - Just $ "class " <> N.runProperName pn - showRef (P.TypeInstanceRef _ ident P.UserNamed) = - Just $ N.runIdent ident - showRef (P.TypeInstanceRef _ _ P.CompilerNamed) = - Nothing - showRef (P.ModuleRef _ name) = - Just $ "module " <> N.runModuleName name - showRef (P.ReExportRef _ _ _) = - Nothing - - commaList :: [Text] -> Text - commaList = T.intercalate ", " - -handleShowPrint - :: MonadState PSCiState m - => (String -> m ()) - -> m () -handleShowPrint print' = do - current <- psciInteractivePrint <$> get - if current == initialInteractivePrint - then - print' $ - "The interactive print function is currently set to the default (`" ++ showPrint current ++ "`)" - else - print' $ - "The interactive print function is currently set to `" ++ showPrint current ++ "`\n" ++ - "The default can be restored with `:print " ++ showPrint initialInteractivePrint ++ "`" - - where - showPrint (mn, ident) = T.unpack (N.runModuleName mn <> "." <> N.runIdent ident) - --- | Imports a module, preserving the initial state on failure. -handleImport - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => ImportedModule - -> m () -handleImport im = do - st <- gets (updateImportedModules (im :)) - let m = createTemporaryModuleForImports st - e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m - case e of - Left errs -> printErrors errs - Right _ -> put st - --- | Takes a value and prints its type -handleTypeOf - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => (String -> m ()) - -> P.Expr - -> m () -handleTypeOf print' val = do - st <- get - let m = createTemporaryModule False st val - e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m - case e of - Left errs -> printErrors errs - Right (_, env') -> - case M.lookup (P.mkQualified (P.Ident "it") (P.ModuleName "$PSCI")) (P.names env') of - Just (ty, _, _) -> print' . P.prettyPrintType maxBound $ ty - Nothing -> print' "Could not find type" - --- | Takes a type and prints its kind -handleKindOf - :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m) - => (String -> m ()) - -> P.SourceType - -> m () -handleKindOf print' typ = do - st <- get - let m = createTemporaryModuleForKind st typ - mName = P.ModuleName "$PSCI" - e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m - case e of - Left errs -> printErrors errs - Right (_, env') -> - case M.lookup (P.Qualified (P.ByModuleName mName) $ P.ProperName "IT") (P.typeSynonyms env') of - Just (_, typ') -> do - let chk = (P.emptyCheckState env') { P.checkCurrentModule = Just mName } - k = check (snd <$> liftTypeCheckM (P.kindOf typ')) chk - - check :: P.SupplyT (StateT P.CheckState (ExceptT P.MultipleErrors (Writer P.MultipleErrors))) a -> P.CheckState -> Either P.MultipleErrors (a, P.CheckState) - check sew = fst . runWriter . runExceptT . runStateT (P.evalSupplyT 0 sew) - case k of - Left err -> printErrors err - Right (kind, _) -> print' . P.prettyPrintType 1024 $ kind - Nothing -> print' "Could not find kind" - --- | Browse a module and displays its signature -handleBrowse - :: (MonadReader PSCiConfig m, MonadState PSCiState m) - => (String -> m ()) - -> P.ModuleName - -> m () -handleBrowse print' moduleName = do - st <- get - let env = psciEnvironment st - case findMod moduleName (psciLoadedExterns st) (psciImportedModules st) of - Just qualName -> print' $ printModuleSignatures qualName env - Nothing -> failNotInEnv moduleName - where - findMod needle externs imports = - let qualMod = fromMaybe needle (lookupUnQualifiedModName needle imports) - modules = S.fromList (C.primModules <> (P.getModuleName . fst <$> externs)) - in if qualMod `S.member` modules - then Just qualMod - else Nothing - - failNotInEnv modName = print' $ T.unpack $ "Module '" <> N.runModuleName modName <> "' is not valid." - lookupUnQualifiedModName needle imports = - (\(modName,_,_) -> modName) <$> find (\(_,_,mayQuaName) -> mayQuaName == Just needle) imports - --- | Return output as would be returned by tab completion, for tools integration etc. -handleComplete - :: (MonadState PSCiState m, MonadIO m) - => (String -> m ()) - -> String - -> m () -handleComplete print' prefix = do - st <- get - let act = liftCompletionM (completion' (reverse prefix, "")) - results <- evalStateT act st - print' $ unlines (formatCompletions results) - --- | Attempt to set the interactive print function. Note that the state will --- only be updated if the interactive print function exists and appears to --- work; we test it by attempting to evaluate '0'. -handleSetInteractivePrint - :: (MonadState PSCiState m, MonadIO m) - => (String -> m ()) - -> (P.ModuleName, P.Ident) - -> m () -handleSetInteractivePrint print' new = do - current <- gets psciInteractivePrint - modify (setInteractivePrint new) - st <- get - let expr = P.Literal internalSpan (P.NumericLiteral (Left 0)) - let m = createTemporaryModule True st expr - e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m - case e of - Left errs -> do - modify (setInteractivePrint current) - print' "Unable to set the repl's printing function:" - printErrors errs - Right _ -> - pure () diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Completion.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Completion.hs deleted file mode 100644 index d9e61e9c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Completion.hs +++ /dev/null @@ -1,193 +0,0 @@ -module Language.PureScript.Interactive.Completion - ( CompletionM - , liftCompletionM - , completion - , completion' - , formatCompletions - ) where - -import Prelude -import Protolude (ordNub) - -import Control.Monad.IO.Class (MonadIO(..)) -import Control.Monad.State.Class (MonadState(..)) -import Control.Monad.Trans.Reader (asks, runReaderT, ReaderT) -import Data.List (nub, isPrefixOf, isInfixOf, isSuffixOf, sortBy, stripPrefix) -import Data.Map (keys) -import Data.Maybe (mapMaybe) -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.Interactive.Directive qualified as D -import Language.PureScript.Interactive.Types (Directive(..), PSCiState, psciExports, psciImports, psciLoadedExterns, replQueryStrings) -import System.Console.Haskeline (Completion(..), CompletionFunc, completeWordWithPrev, listFiles, simpleCompletion) - --- Completions may read the state, but not modify it. -type CompletionM = ReaderT PSCiState IO - --- Lift a `CompletionM` action into a state monad. -liftCompletionM - :: (MonadState PSCiState m, MonadIO m) - => CompletionM a - -> m a -liftCompletionM act = do - st <- get - liftIO $ runReaderT act st - --- Haskeline completions - --- | Loads module, function, and file completions. -completion - :: (MonadState PSCiState m, MonadIO m) - => CompletionFunc m -completion = liftCompletionM . completion' - -completion' :: CompletionFunc CompletionM -completion' = completeWordWithPrev Nothing " \t\n\r([" findCompletions - --- | Callback for Haskeline's `completeWordWithPrev`. --- Expects: --- * Line contents to the left of the word, reversed --- * Word to be completed -findCompletions :: String -> String -> CompletionM [Completion] -findCompletions prev word = do - let ctx = completionContext (words (reverse prev)) word - completions <- concat <$> traverse getCompletions ctx - return $ sortBy directivesFirst completions - where - getCompletions :: CompletionContext -> CompletionM [Completion] - getCompletions = fmap (mapMaybe (either (prefixedBy word) Just)) . getCompletion - - getCompletion :: CompletionContext -> CompletionM [Either String Completion] - getCompletion ctx = - case ctx of - CtxFilePath f -> map Right <$> listFiles f - CtxModule -> map Left <$> getModuleNames - CtxIdentifier -> map Left <$> ((++) <$> getIdentNames <*> getDctorNames) - CtxType pre -> map (Left . (pre ++)) <$> getTypeNames - CtxFixed str -> return [Left str] - CtxDirective d -> return (map Left (completeDirectives d)) - - completeDirectives :: String -> [String] - completeDirectives = map (':' :) . D.directiveStringsFor - - prefixedBy :: String -> String -> Maybe Completion - prefixedBy w cand = if w `isPrefixOf` cand - then Just (simpleCompletion cand) - else Nothing - - directivesFirst :: Completion -> Completion -> Ordering - directivesFirst (Completion _ d1 _) (Completion _ d2 _) = go d1 d2 - where - go (':' : xs) (':' : ys) = compare xs ys - go (':' : _) _ = LT - go _ (':' : _) = GT - go xs ys = compare xs ys - --- | --- Convert Haskeline completion result to results as they would be displayed -formatCompletions :: (String, [Completion]) -> [String] -formatCompletions (unusedR, completions) = actuals - where - unused = reverse unusedR - actuals = map ((unused ++) . replacement) completions - -data CompletionContext - = CtxDirective String - | CtxFilePath String - | CtxModule - | CtxIdentifier - | CtxType String - | CtxFixed String - deriving (Show) - --- | --- Decide what kind of completion we need based on input. This function expects --- a list of complete words (to the left of the cursor) as the first argument, --- and the current word as the second argument. -completionContext :: [String] -> String -> [CompletionContext] -completionContext _ w | "::" `isInfixOf` w = [CtxType (w `endingWith` "::")] -completionContext ws _ | lastSatisfies ("::" `isSuffixOf`) ws = [CtxType ""] -completionContext [] _ = [CtxDirective "", CtxIdentifier, CtxFixed "import"] -completionContext ws w | headSatisfies (":" `isPrefixOf`) ws = completeDirective ws w -completionContext ws w | headSatisfies (== "import") ws = completeImport ws w -completionContext _ _ = [CtxIdentifier] - -endingWith :: String -> String -> String -endingWith str stop = aux "" str - where - aux acc s@(x:xs) - | stop `isPrefixOf` s = reverse (stop ++ acc) - | otherwise = aux (x:acc) xs - aux acc [] = reverse (stop ++ acc) - -completeDirective :: [String] -> String -> [CompletionContext] -completeDirective ws w = - case ws of - [] -> [CtxDirective w] - (x:xs) -> case D.directivesFor <$> stripPrefix ":" x of - -- only offer completions if the directive is unambiguous - Just [dir] -> directiveArg xs dir - _ -> [] - -directiveArg :: [String] -> Directive -> [CompletionContext] -directiveArg [] Browse = [CtxModule] -- only complete very next term -directiveArg [] Show = map CtxFixed replQueryStrings -- only complete very next term -directiveArg _ Type = [CtxIdentifier] -directiveArg _ Kind = [CtxType ""] -directiveArg _ _ = [] - -completeImport :: [String] -> String -> [CompletionContext] -completeImport ws w' = - case (ws, w') of - (["import"], _) -> [CtxModule] - _ -> [] - -headSatisfies :: (a -> Bool) -> [a] -> Bool -headSatisfies p str = - case str of - (c:_) -> p c - _ -> False - -lastSatisfies :: (a -> Bool) -> [a] -> Bool -lastSatisfies _ [] = False -lastSatisfies p xs = p (last xs) - -getLoadedModules :: CompletionM [P.Module] -getLoadedModules = asks (map fst . psciLoadedExterns) - -getModuleNames :: CompletionM [String] -getModuleNames = moduleNames <$> getLoadedModules - -getIdentNames :: CompletionM [String] -getIdentNames = do - importedVals <- asks (keys . P.importedValues . psciImports) - exportedVals <- asks (keys . P.exportedValues . psciExports) - - importedValOps <- asks (keys . P.importedValueOps . psciImports) - exportedValOps <- asks (keys . P.exportedValueOps . psciExports) - - return . nub $ map (T.unpack . P.showQualified P.showIdent) importedVals - ++ map (T.unpack . P.showQualified P.runOpName) importedValOps - ++ map (T.unpack . P.showIdent) exportedVals - ++ map (T.unpack . P.runOpName) exportedValOps - -getDctorNames :: CompletionM [String] -getDctorNames = do - imports <- asks (keys . P.importedDataConstructors . psciImports) - return . nub $ map (T.unpack . P.showQualified P.runProperName) imports - -getTypeNames :: CompletionM [String] -getTypeNames = do - importedTypes <- asks (keys . P.importedTypes . psciImports) - exportedTypes <- asks (keys . P.exportedTypes . psciExports) - - importedTypeOps <- asks (keys . P.importedTypeOps . psciImports) - exportedTypeOps <- asks (keys . P.exportedTypeOps . psciExports) - - return . nub $ map (T.unpack . P.showQualified P.runProperName) importedTypes - ++ map (T.unpack . P.showQualified P.runOpName) importedTypeOps - ++ map (T.unpack . P.runProperName) exportedTypes - ++ map (T.unpack . P.runOpName) exportedTypeOps - -moduleNames :: [P.Module] -> [String] -moduleNames = ordNub . map (T.unpack . P.runModuleName . P.getModuleName) diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Directive.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Directive.hs deleted file mode 100644 index a8a0ce13..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Directive.hs +++ /dev/null @@ -1,88 +0,0 @@ --- | --- Directives for PSCI. --- -module Language.PureScript.Interactive.Directive where - -import Prelude - -import Data.Maybe (fromJust) -import Data.List (isPrefixOf) -import Data.Tuple (swap) -import Data.List.NonEmpty (NonEmpty(..)) -import Data.List.NonEmpty qualified as NEL - -import Language.PureScript.Interactive.Types (Directive(..)) - --- | --- A mapping of directives to the different strings that can be used to invoke --- them. --- -directiveStrings :: [(Directive, NonEmpty String)] -directiveStrings = - [ (Help , NEL.fromList ["?", "help"]) - , (Quit , NEL.singleton "quit") - , (Reload , NEL.singleton "reload") - , (Clear , NEL.singleton "clear") - , (Browse , NEL.singleton "browse") - , (Type , NEL.singleton "type") - , (Kind , NEL.singleton "kind") - , (Show , NEL.singleton "show") - , (Paste , NEL.singleton "paste") - , (Complete , NEL.singleton "complete") - , (Print , NEL.singleton "print") - ] - --- | --- Like `directiveStrings`, but the other way around. --- -directiveStrings' :: [(String, Directive)] -directiveStrings' = concatMap go directiveStrings - where - go (dir, strs) = map (, dir) $ NEL.toList strs - --- | --- Returns all possible string representations of a directive. --- -stringsFor :: Directive -> NonEmpty String -stringsFor d = fromJust (lookup d directiveStrings) - --- | --- Returns the default string representation of a directive. --- -stringFor :: Directive -> String -stringFor = NEL.head . stringsFor - --- | --- Returns the list of directives which could be expanded from the string --- argument, together with the string alias that matched. --- -directivesFor' :: String -> [(Directive, String)] -directivesFor' str = go directiveStrings' - where - go = map swap . filter ((str `isPrefixOf`) . fst) - -directivesFor :: String -> [Directive] -directivesFor = map fst . directivesFor' - -directiveStringsFor :: String -> [String] -directiveStringsFor = map snd . directivesFor' - --- | --- The help menu. --- -help :: [(Directive, String, String)] -help = - [ (Help, "", "Show this help menu") - , (Quit, "", "Quit PSCi") - , (Reload, "", "Reload all imported modules while discarding bindings") - , (Clear, "", "Discard all imported modules and declared bindings") - , (Browse, "", "See all functions in ") - , (Type, "", "Show the type of ") - , (Kind, "", "Show the kind of ") - , (Show, "import", "Show all imported modules") - , (Show, "loaded", "Show all loaded modules") - , (Show, "print", "Show the repl's current printing function") - , (Paste, "paste", "Enter multiple lines, terminated by ^D") - , (Complete, "", "Show completions for as if pressing tab") - , (Print, "", "Set the repl's printing function to (which must be fully qualified)") - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/IO.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/IO.hs deleted file mode 100644 index 34c9a287..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/IO.hs +++ /dev/null @@ -1,86 +0,0 @@ -{-# LANGUAGE TypeApplications #-} - -module Language.PureScript.Interactive.IO (findNodeProcess, readNodeProcessWithExitCode, getHistoryFilename) where - -import Prelude - -import Control.Monad (msum, void) -import Control.Monad.Error.Class (throwError) -import Control.Monad.Trans.Class (lift) -import Control.Monad.Trans.Except (ExceptT(..), runExceptT) -import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) -import Data.Functor ((<&>)) -import Data.List (isInfixOf) -import System.Directory (XdgDirectory (..), createDirectoryIfMissing, - getAppUserDataDirectory, getXdgDirectory, - findExecutable, doesFileExist) -import System.Exit (ExitCode(ExitFailure, ExitSuccess)) -import System.FilePath (takeDirectory, ()) -import System.Process (readProcessWithExitCode) -import Text.Parsec ((), many1, parse, sepBy) -import Text.Parsec.Char (char, digit) -import Protolude (note) - -mkdirp :: FilePath -> IO () -mkdirp = createDirectoryIfMissing True . takeDirectory - --- File helpers - -onFirstFileMatching :: Monad m => (b -> m (Maybe a)) -> [b] -> m (Maybe a) -onFirstFileMatching f pathVariants = runMaybeT . msum $ map (MaybeT . f) pathVariants - --- | --- Locates the node executable. --- Checks for either @nodejs@ or @node@. --- -findNodeProcess :: IO (Either String String) -findNodeProcess = onFirstFileMatching findExecutable ["nodejs", "node"] <&> - note "Could not find Node.js. Do you have Node.js installed and available in your PATH?" - -findNodeVersion :: String -> IO (Maybe String) -findNodeVersion node = do - result <- readProcessWithExitCode node ["--version"] "" - return $ case result of - (ExitSuccess, version, _) -> Just version - (ExitFailure _, _, _) -> Nothing - -readNodeProcessWithExitCode :: Maybe FilePath -> [String] -> String -> IO (Either String (ExitCode, String, String)) -readNodeProcessWithExitCode nodePath nodeArgs stdin = runExceptT $ do - process <- maybe (ExceptT findNodeProcess) pure nodePath - (major, _, _) <- lift (findNodeVersion process) >>= \case - Nothing -> throwError "Could not find Node.js version." - Just version -> do - let semver = do - void $ char 'v' - major : minor : patch : _ <- fmap (read @Int) (many1 digit) `sepBy` void (char '.') - pure (major, minor, patch) - case parse (semver "Could not parse Node.js version.") "" version of - Left err -> throwError $ show err - Right (major, minor, patch) - | major < 12 -> throwError $ "Unsupported Node.js version " <> show major <> ". Required Node.js version >=12." - | otherwise -> pure (major, minor, patch) - let nodeArgs' = if major < 13 then "--experimental-modules" : nodeArgs else nodeArgs - lift (readProcessWithExitCode process nodeArgs' stdin) <&> \case - (ExitSuccess, out, err) -> - (ExitSuccess, out, censorExperimentalWarnings err) - (ExitFailure code, out, err) -> - (ExitFailure code, out, err) - -censorExperimentalWarnings :: String -> String -censorExperimentalWarnings = - unlines . filter (not . ("ExperimentalWarning" `isInfixOf`)) . lines - --- | --- Grabs the filename where the history is stored. --- -getHistoryFilename :: IO FilePath -getHistoryFilename = do - appuserdata <- getAppUserDataDirectory "purescript" - olddirbool <- doesFileExist (appuserdata "psci_history") - if olddirbool - then return (appuserdata "psci_history") - else do - datadir <- getXdgDirectory XdgData "purescript" - let filename = datadir "psci_history" - mkdirp filename - return filename diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Message.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Message.hs deleted file mode 100644 index 800b6147..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Message.hs +++ /dev/null @@ -1,59 +0,0 @@ -module Language.PureScript.Interactive.Message where - -import Prelude - -import Data.List (intercalate) -import Data.Version (showVersion) -import Paths_purescript qualified as Paths -import Language.PureScript.Interactive.Directive qualified as D -import Language.PureScript.Interactive.Types (Directive) - --- Messages - --- | The guide URL -guideURL :: String -guideURL = "https://github.com/purescript/documentation/blob/master/guides/PSCi.md" - --- | The help message. -helpMessage :: String -helpMessage = "The following commands are available:\n\n " ++ - intercalate "\n " (map line D.help) ++ - "\n\n" ++ extraHelp - where - line :: (Directive, String, String) -> String - line (dir, arg, desc) = - let cmd = ':' : D.stringFor dir - in unwords [ cmd - , replicate (11 - length cmd) ' ' - , arg - , replicate (11 - length arg) ' ' - , desc - ] - - extraHelp = - "Further information is available on the PureScript documentation repository:\n" ++ - " --> " ++ guideURL - --- | The welcome prologue. -prologueMessage :: String -prologueMessage = unlines - [ "PSCi, version " ++ showVersion Paths.version - , "Type :? for help" - ] - -noInputMessage :: String -noInputMessage = unlines - [ "purs repl: No input files; try running `pulp psci` instead." - , "For help getting started, visit " ++ guideURL - , "Usage: For basic information, try the `--help' option." - ] - -supportModuleMessage :: String -supportModuleMessage = unlines - [ "purs repl: PSCi requires the psci-support package." - , "For help getting started, visit " ++ guideURL - ] - --- | The quit message. -quitMessage :: String -quitMessage = "See ya!" diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Module.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Module.hs deleted file mode 100644 index 61083eee..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Module.hs +++ /dev/null @@ -1,95 +0,0 @@ -module Language.PureScript.Interactive.Module where - -import Prelude - -import Language.PureScript qualified as P -import Language.PureScript.CST qualified as CST -import Language.PureScript.Interactive.Types (ImportedModule, PSCiState, initialInteractivePrint, psciImportedModules, psciInteractivePrint, psciLetBindings) -import System.Directory (getCurrentDirectory) -import System.FilePath (pathSeparator, makeRelative) -import System.IO.UTF8 (readUTF8FilesT) - --- * Support Module - --- | The name of the PSCI support module -supportModuleName :: P.ModuleName -supportModuleName = fst initialInteractivePrint - --- | Checks if the Console module is defined -supportModuleIsDefined :: [P.ModuleName] -> Bool -supportModuleIsDefined = elem supportModuleName - --- * Module Management - --- | Load all modules. -loadAllModules :: [FilePath] -> IO (Either P.MultipleErrors [(FilePath, P.Module)]) -loadAllModules files = do - pwd <- getCurrentDirectory - filesAndContent <- readUTF8FilesT files - return $ fmap (fmap snd) <$> CST.parseFromFiles (makeRelative pwd) filesAndContent - --- | --- Makes a volatile module to execute the current expression. --- -createTemporaryModule :: Bool -> PSCiState -> P.Expr -> P.Module -createTemporaryModule exec st val = - let - imports = psciImportedModules st - lets = psciLetBindings st - moduleName = P.ModuleName "$PSCI" - effModuleName = P.ModuleName "Effect" - effImport = (effModuleName, P.Implicit, Just (P.ModuleName "$Effect")) - supportImport = (fst (psciInteractivePrint st), P.Implicit, Just (P.ModuleName "$Support")) - eval = P.Var internalSpan (P.Qualified (P.ByModuleName (P.ModuleName "$Support")) (snd (psciInteractivePrint st))) - mainValue = P.App eval (P.Var internalSpan (P.Qualified P.ByNullSourcePos (P.Ident "it"))) - itDecl = P.ValueDecl (internalSpan, []) (P.Ident "it") P.Public [] [P.MkUnguarded val] - typeDecl = P.TypeDeclaration - (P.TypeDeclarationData (internalSpan, []) (P.Ident "$main") - (P.srcTypeApp - (P.srcTypeConstructor - (P.Qualified (P.ByModuleName (P.ModuleName "$Effect")) (P.ProperName "Effect"))) - P.srcTypeWildcard)) - mainDecl = P.ValueDecl (internalSpan, []) (P.Ident "$main") P.Public [] [P.MkUnguarded mainValue] - decls = if exec then [itDecl, typeDecl, mainDecl] else [itDecl] - in - P.Module internalSpan - [] moduleName - ((importDecl `map` (effImport : supportImport : imports)) ++ lets ++ decls) - Nothing - - --- | --- Makes a volatile module to hold a non-qualified type synonym for a fully-qualified data type declaration. --- -createTemporaryModuleForKind :: PSCiState -> P.SourceType -> P.Module -createTemporaryModuleForKind st typ = - let - imports = psciImportedModules st - lets = psciLetBindings st - moduleName = P.ModuleName "$PSCI" - itDecl = P.TypeSynonymDeclaration (internalSpan, []) (P.ProperName "IT") [] typ - in - P.Module internalSpan [] moduleName ((importDecl `map` imports) ++ lets ++ [itDecl]) Nothing - --- | --- Makes a volatile module to execute the current imports. --- -createTemporaryModuleForImports :: PSCiState -> P.Module -createTemporaryModuleForImports st = - let - imports = psciImportedModules st - moduleName = P.ModuleName "$PSCI" - in - P.Module internalSpan [] moduleName (importDecl `map` imports) Nothing - -importDecl :: ImportedModule -> P.Declaration -importDecl (mn, declType, asQ) = P.ImportDeclaration (internalSpan, []) mn declType asQ - -indexFile :: FilePath -indexFile = ".psci_modules" ++ pathSeparator : "index.js" - -modulesDir :: FilePath -modulesDir = ".psci_modules" - -internalSpan :: P.SourceSpan -internalSpan = P.internalModuleSourceSpan "" diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Parser.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Parser.hs deleted file mode 100644 index d888683b..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Parser.hs +++ /dev/null @@ -1,147 +0,0 @@ --- | --- Parser for PSCI. --- -module Language.PureScript.Interactive.Parser - ( parseDotFile - , parseCommand - ) where - -import Prelude - -import Control.Monad (join) -import Data.Bifunctor (bimap) -import Data.Char (isSpace) -import Data.List (intercalate) -import Data.List.NonEmpty qualified as NE -import Data.Text qualified as T -import Language.PureScript qualified as P -import Language.PureScript.CST qualified as CST -import Language.PureScript.CST.Monad qualified as CSTM -import Language.PureScript.Interactive.Directive qualified as D -import Language.PureScript.Interactive.Types (Command(..), Directive(..), ReplQuery(..), parseReplQuery, replQueryStrings) - --- | --- Parses a limited set of commands from from .purs-repl --- -parseDotFile :: FilePath -> String -> Either String [Command] -parseDotFile filePath = - bimap (CST.prettyPrintError . NE.head) snd - . CST.runTokenParser (parseMany parser <* CSTM.token CST.TokEof) - . CST.lexTopLevel - . T.pack - where - parser = CSTM.oneOf $ NE.fromList - [ psciImport filePath - , do - tok <- CSTM.munch - CSTM.parseFail tok $ CST.ErrCustom "The .purs-repl file only supports import declarations" - ] - --- | --- Parses PSCI metacommands or expressions input from the user. --- -parseCommand :: String -> Either String [Command] -parseCommand cmdString = - case cmdString of - (':' : cmd) -> pure <$> parseDirective cmd - _ -> parseRest (mergeDecls <$> parseMany psciCommand) cmdString - where - mergeDecls (Decls as : bs) = - case mergeDecls bs of - Decls bs' : cs' -> - Decls (as <> bs') : cs' - cs' -> - Decls as : cs' - mergeDecls (a : bs) = - a : mergeDecls bs - mergeDecls [] = [] - -parseMany :: CST.Parser a -> CST.Parser [a] -parseMany = CSTM.manyDelimited CST.TokLayoutStart CST.TokLayoutEnd CST.TokLayoutSep - -parseOne :: CST.Parser a -> CST.Parser a -parseOne p = CSTM.token CST.TokLayoutStart *> p <* CSTM.token CST.TokLayoutEnd - -parseRest :: CST.Parser a -> String -> Either String a -parseRest p = - bimap (CST.prettyPrintError . NE.head) snd - . CST.runTokenParser (p <* CSTM.token CST.TokEof) - . CST.lexTopLevel - . T.pack - -psciCommand :: CST.Parser Command -psciCommand = - CSTM.oneOf $ NE.fromList - [ psciImport "" - , psciDeclaration - , psciExpression - ] - -trim :: String -> String -trim = trimEnd . trimStart - -trimStart :: String -> String -trimStart = dropWhile isSpace - -trimEnd :: String -> String -trimEnd = reverse . trimStart . reverse - -parseDirective :: String -> Either String Command -parseDirective cmd = - case D.directivesFor' dstr of - [(d, _)] -> commandFor d - [] -> Left "Unrecognized directive. Type :? for help." - ds -> Left ("Ambiguous directive. Possible matches: " ++ - intercalate ", " (map snd ds) ++ ". Type :? for help.") - where - (dstr, arg) = trim <$> break isSpace cmd - - commandFor d = case d of - Help -> return ShowHelp - Quit -> return QuitPSCi - Reload -> return ReloadState - Clear -> return ClearState - Paste -> return PasteLines - Browse -> BrowseModule . CST.nameValue <$> parseRest (parseOne CST.parseModuleNameP) arg - Show -> ShowInfo <$> parseReplQuery' arg - Type -> TypeOf . CST.convertExpr "" <$> parseRest (parseOne CST.parseExprP) arg - Kind -> KindOf . CST.convertType "" <$> parseRest (parseOne CST.parseTypeP) arg - Complete -> return (CompleteStr arg) - Print - | arg == "" -> return $ ShowInfo QueryPrint - | otherwise -> SetInteractivePrint <$> parseRest (parseOne parseFullyQualifiedIdent) arg - --- | --- Parses expressions entered at the PSCI repl. --- -psciExpression :: CST.Parser Command -psciExpression = Expression . CST.convertExpr "" <$> CST.parseExprP - --- | Imports must be handled separately from other declarations, so that --- :show import works, for example. -psciImport :: FilePath -> CST.Parser Command -psciImport filePath = do - (_, mn, declType, asQ) <- CST.convertImportDecl filePath <$> CST.parseImportDeclP - pure $ Import (mn, declType, asQ) - --- | Any declaration that we don't need a 'special case' parser for --- (like import declarations). -psciDeclaration :: CST.Parser Command -psciDeclaration = Decls . CST.convertDeclaration "" <$> CST.parseDeclP - -parseReplQuery' :: String -> Either String ReplQuery -parseReplQuery' str = - case parseReplQuery str of - Nothing -> Left ("Don't know how to show " ++ str ++ ". Try one of: " ++ - intercalate ", " replQueryStrings ++ ".") - Just query -> Right query - -parseFullyQualifiedIdent :: CST.Parser (P.ModuleName, P.Ident) -parseFullyQualifiedIdent = join $ CST.Parser $ \st _ ksucc -> - case CST.runParser st CST.parseQualIdentP of - (st', Right (CST.QualifiedName _ (Just mn) ident)) -> - ksucc st' $ pure (mn, P.Ident $ CST.getIdent ident) - _ -> - ksucc st $ do - tok <- CSTM.munch - CSTM.parseFail tok $ CST.ErrCustom "Expected a fully-qualified name (eg: PSCI.Support.eval)" diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Printer.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Printer.hs deleted file mode 100644 index ed2d1452..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Printer.hs +++ /dev/null @@ -1,132 +0,0 @@ -module Language.PureScript.Interactive.Printer where - -import Prelude - -import Data.List (intersperse) -import Data.Map qualified as M -import Data.Maybe (mapMaybe) -import Data.Text qualified as T -import Data.Text (Text) -import Language.PureScript qualified as P -import Text.PrettyPrint.Boxes qualified as Box - --- TODO (Christoph): Text version of boxes -textT :: Text -> Box.Box -textT = Box.text . T.unpack - --- Printers - --- | --- Pretty print a module's signatures --- -printModuleSignatures :: P.ModuleName -> P.Environment -> String -printModuleSignatures moduleName P.Environment{..} = - -- get relevant components of a module from environment - let moduleNamesIdent = byModuleName names - moduleTypeClasses = byModuleName typeClasses - moduleTypes = byModuleName types - - byModuleName :: M.Map (P.Qualified a) b -> [P.Qualified a] - byModuleName = filter ((== Just moduleName) . P.getQual) . M.keys - - in - -- print each component - (unlines . map trimEnd . lines . Box.render . Box.vsep 1 Box.left) - [ printModule's (mapMaybe (showTypeClass . findTypeClass typeClasses)) moduleTypeClasses -- typeClasses - , printModule's (mapMaybe (showType typeClasses dataConstructors typeSynonyms . findType types)) moduleTypes -- types - , printModule's (map (showNameType . findNameType names)) moduleNamesIdent -- functions - ] - - where printModule's showF = Box.vsep 1 Box.left . showF - - findNameType :: M.Map (P.Qualified P.Ident) (P.SourceType, P.NameKind, P.NameVisibility) - -> P.Qualified P.Ident - -> (P.Ident, Maybe (P.SourceType, P.NameKind, P.NameVisibility)) - findNameType envNames m = (P.disqualify m, M.lookup m envNames) - - showNameType :: (P.Ident, Maybe (P.SourceType, P.NameKind, P.NameVisibility)) -> Box.Box - showNameType (mIdent, Just (mType, _, _)) = textT (P.showIdent mIdent <> " :: ") Box.<> P.typeAsBox maxBound mType - showNameType _ = P.internalError "The impossible happened in printModuleSignatures." - - findTypeClass - :: M.Map (P.Qualified (P.ProperName 'P.ClassName)) P.TypeClassData - -> P.Qualified (P.ProperName 'P.ClassName) - -> (P.Qualified (P.ProperName 'P.ClassName), Maybe P.TypeClassData) - findTypeClass envTypeClasses name = (name, M.lookup name envTypeClasses) - - showTypeClass - :: (P.Qualified (P.ProperName 'P.ClassName), Maybe P.TypeClassData) - -> Maybe Box.Box - showTypeClass (_, Nothing) = Nothing - showTypeClass (P.Qualified _ name, Just P.TypeClassData{..}) = - let constraints = - if null typeClassSuperclasses - then Box.text "" - else Box.text "(" - Box.<> Box.hcat Box.left (intersperse (Box.text ", ") $ map (\(P.Constraint _ (P.Qualified _ pn) _ lt _) -> textT (P.runProperName pn) Box.<+> Box.hcat Box.left (map (P.typeAtomAsBox maxBound) lt)) typeClassSuperclasses) - Box.<> Box.text ") <= " - className = - textT (P.runProperName name) - Box.<> textT (foldMap ((" " <>) . fst) typeClassArguments) - classBody = - Box.vcat Box.top (map (\(i, t, _) -> textT (P.showIdent i <> " ::") Box.<+> P.typeAsBox maxBound t) typeClassMembers) - - in - Just $ - (Box.text "class " - Box.<> constraints - Box.<> className - Box.<+> if null typeClassMembers then Box.text "" else Box.text "where") - Box.// Box.moveRight 2 classBody - - - findType - :: M.Map (P.Qualified (P.ProperName 'P.TypeName)) (P.SourceType, P.TypeKind) - -> P.Qualified (P.ProperName 'P.TypeName) - -> (P.Qualified (P.ProperName 'P.TypeName), Maybe (P.SourceType, P.TypeKind)) - findType envTypes name = (name, M.lookup name envTypes) - - showType - :: M.Map (P.Qualified (P.ProperName 'P.ClassName)) P.TypeClassData - -> M.Map (P.Qualified (P.ProperName 'P.ConstructorName)) (P.DataDeclType, P.ProperName 'P.TypeName, P.SourceType, [P.Ident]) - -> M.Map (P.Qualified (P.ProperName 'P.TypeName)) ([(Text, Maybe P.SourceType)], P.SourceType) - -> (P.Qualified (P.ProperName 'P.TypeName), Maybe (P.SourceType, P.TypeKind)) - -> Maybe Box.Box - showType typeClassesEnv dataConstructorsEnv typeSynonymsEnv (n@(P.Qualified modul name), typ) = - case (typ, M.lookup n typeSynonymsEnv) of - (Just (_, P.TypeSynonym), Just (typevars, dtType)) -> - if M.member (fmap P.coerceProperName n) typeClassesEnv - then - Nothing - else - Just $ - textT ("type " <> P.runProperName name <> foldMap ((" " <>) . fst) typevars) - Box.// Box.moveRight 2 (Box.text "=" Box.<+> P.typeAsBox maxBound dtType) - - (Just (_, P.DataType _ typevars pt), _) -> - let prefix = - case pt of - [(dtProperName,_)] -> - case M.lookup (P.Qualified modul dtProperName) dataConstructorsEnv of - Just (dataDeclType, _, _, _) -> P.showDataDeclType dataDeclType - _ -> "data" - _ -> "data" - - in - Just $ textT (prefix <> " " <> P.runProperName name <> foldMap ((" " <>) . (\(v, _, _) -> v)) typevars) Box.// printCons pt - - _ -> - Nothing - - where printCons pt = - Box.moveRight 2 $ - Box.vcat Box.left $ - mapFirstRest (Box.text "=" Box.<+>) (Box.text "|" Box.<+>) $ - map (\(cons,idents) -> textT (P.runProperName cons) Box.<> Box.hcat Box.left (map prettyPrintType idents)) pt - - prettyPrintType t = Box.text " " Box.<> P.typeAtomAsBox maxBound t - - mapFirstRest _ _ [] = [] - mapFirstRest f g (x:xs) = f x : map g xs - - trimEnd = reverse . dropWhile (== ' ') . reverse diff --git a/claude-help/original-compiler/src/Language/PureScript/Interactive/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Interactive/Types.hs deleted file mode 100644 index 83fedf81..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Interactive/Types.hs +++ /dev/null @@ -1,242 +0,0 @@ --- | --- Type declarations and associated basic functions for PSCI. --- -module Language.PureScript.Interactive.Types - ( PSCiConfig(..) - , psciEnvironment - , PSCiState -- constructor is not exported, to prevent psciImports and psciExports from - -- becoming inconsistent with importedModules, letBindings and loadedExterns - , ImportedModule - , psciExports - , psciImports - , psciLoadedExterns - , psciInteractivePrint - , psciImportedModules - , psciLetBindings - , initialPSCiState - , initialInteractivePrint - , updateImportedModules - , updateLoadedExterns - , updateLets - , setInteractivePrint - , Command(..) - , ReplQuery(..) - , replQueries - , replQueryStrings - , showReplQuery - , parseReplQuery - , Directive(..) - ) where - -import Prelude - -import Language.PureScript qualified as P -import Data.Map qualified as M -import Data.List (foldl') -import Language.PureScript.Sugar.Names.Env (nullImports, primExports) -import Control.Monad (foldM) -import Control.Monad.Trans.Except (runExceptT) -import Control.Monad.Trans.State (execStateT) -import Control.Monad.Writer.Strict (runWriterT) - - --- | The PSCI configuration. --- --- These configuration values do not change during execution. --- -newtype PSCiConfig = PSCiConfig - { psciFileGlobs :: [String] - } deriving Show - --- | The PSCI state. --- --- Holds a list of imported modules, loaded files, and partial let bindings, --- plus the currently configured interactive printing function. --- --- The let bindings are partial, because it makes more sense to apply the --- binding to the final evaluated expression. --- --- The last two fields are derived from the first three via updateImportExports --- each time a module is imported, a let binding is added, or the session is --- cleared or reloaded -data PSCiState = PSCiState - [ImportedModule] - [P.Declaration] - [(P.Module, P.ExternsFile)] - (P.ModuleName, P.Ident) - P.Imports - P.Exports - deriving Show - -psciImportedModules :: PSCiState -> [ImportedModule] -psciImportedModules (PSCiState x _ _ _ _ _) = x - -psciLetBindings :: PSCiState -> [P.Declaration] -psciLetBindings (PSCiState _ x _ _ _ _) = x - -psciLoadedExterns :: PSCiState -> [(P.Module, P.ExternsFile)] -psciLoadedExterns (PSCiState _ _ x _ _ _) = x - -psciInteractivePrint :: PSCiState -> (P.ModuleName, P.Ident) -psciInteractivePrint (PSCiState _ _ _ x _ _) = x - -psciImports :: PSCiState -> P.Imports -psciImports (PSCiState _ _ _ _ x _) = x - -psciExports :: PSCiState -> P.Exports -psciExports (PSCiState _ _ _ _ _ x) = x - -initialPSCiState :: PSCiState -initialPSCiState = PSCiState [] [] [] initialInteractivePrint nullImports primExports - --- | The default interactive print function. -initialInteractivePrint :: (P.ModuleName, P.Ident) -initialInteractivePrint = (P.moduleNameFromString "PSCI.Support", P.Ident "eval") - -psciEnvironment :: PSCiState -> P.Environment -psciEnvironment st = foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment externs - where externs = map snd (psciLoadedExterns st) - --- | All of the data that is contained by an ImportDeclaration in the AST. --- That is: --- --- * A module name, the name of the module which is being imported --- * An ImportDeclarationType which specifies whether there is an explicit --- import list, a hiding list, or neither. --- * If the module is imported qualified, its qualified name in the importing --- module. Otherwise, Nothing. --- -type ImportedModule = (P.ModuleName, P.ImportDeclarationType, Maybe P.ModuleName) - --- * State helpers - --- This function updates the Imports and Exports values in the PSCiState, which are used for --- handling completions. This function must be called whenever the PSCiState is modified to --- ensure that completions remain accurate. -updateImportExports :: PSCiState -> PSCiState -updateImportExports st@(PSCiState modules lets externs iprint _ _) = - case createEnv (map snd externs) >>= flip desugarModule temporaryModule of - Left _ -> st -- TODO: can this fail and what should we do? - Right env -> - case M.lookup temporaryName env of - Just (_, is, es) -> PSCiState modules lets externs iprint is es - _ -> st -- impossible - where - - desugarModule :: P.Env -> P.Module -> Either P.MultipleErrors P.Env - desugarModule e = runExceptT =<< fmap (fst . fst) . runWriterT . flip execStateT (e, mempty) . P.desugarImports - - createEnv :: [P.ExternsFile] -> Either P.MultipleErrors P.Env - createEnv = runExceptT =<< fmap fst . runWriterT . foldM P.externsEnv P.primEnv - - temporaryName :: P.ModuleName - temporaryName = P.ModuleName "$PSCI" - - temporaryModule :: P.Module - temporaryModule = - let - prim = (P.ModuleName "Prim", P.Implicit, Nothing) - decl = (importDecl `map` (prim : modules)) ++ lets - in - P.Module internalSpan [] temporaryName decl Nothing - - importDecl :: ImportedModule -> P.Declaration - importDecl (mn, declType, asQ) = P.ImportDeclaration (internalSpan, []) mn declType asQ - - internalSpan :: P.SourceSpan - internalSpan = P.internalModuleSourceSpan "" - --- | Updates the imported modules in the state record. -updateImportedModules :: ([ImportedModule] -> [ImportedModule]) -> PSCiState -> PSCiState -updateImportedModules f (PSCiState x a b c d e) = - updateImportExports (PSCiState (f x) a b c d e) - --- | Updates the loaded externs files in the state record. -updateLoadedExterns :: ([(P.Module, P.ExternsFile)] -> [(P.Module, P.ExternsFile)]) -> PSCiState -> PSCiState -updateLoadedExterns f (PSCiState a b x c d e) = - updateImportExports (PSCiState a b (f x) c d e) - --- | Updates the let bindings in the state record. -updateLets :: ([P.Declaration] -> [P.Declaration]) -> PSCiState -> PSCiState -updateLets f (PSCiState a x b c d e) = - updateImportExports (PSCiState a (f x) b c d e) - --- | Replaces the interactive printing function in the state record with a new --- one. -setInteractivePrint :: (P.ModuleName, P.Ident) -> PSCiState -> PSCiState -setInteractivePrint iprint (PSCiState a b c _ d e) = - PSCiState a b c iprint d e - --- * Commands - --- | --- Valid Meta-commands for PSCI --- -data Command - -- | A purescript expression - = Expression P.Expr - -- | Show the help (ie, list of directives) - | ShowHelp - -- | Import a module from a loaded file - | Import ImportedModule - -- | Browse a module - | BrowseModule P.ModuleName - -- | Exit PSCI - | QuitPSCi - -- | Reload all the imported modules of the REPL - | ReloadState - -- | Clear the state of the REPL - | ClearState - -- | Add some declarations to the current evaluation context - | Decls [P.Declaration] - -- | Find the type of an expression - | TypeOf P.Expr - -- | Find the kind of an expression - | KindOf P.SourceType - -- | Shows information about the current state of the REPL - | ShowInfo ReplQuery - -- | Paste multiple lines - | PasteLines - -- | Return auto-completion output as if pressing - | CompleteStr String - -- | Set the interactive printing function - | SetInteractivePrint (P.ModuleName, P.Ident) - deriving Show - -data ReplQuery - = QueryLoaded - | QueryImport - | QueryPrint - deriving (Eq, Show) - --- | A list of all ReplQuery values. -replQueries :: [ReplQuery] -replQueries = [QueryLoaded, QueryImport, QueryPrint] - -replQueryStrings :: [String] -replQueryStrings = map showReplQuery replQueries - -showReplQuery :: ReplQuery -> String -showReplQuery QueryLoaded = "loaded" -showReplQuery QueryImport = "import" -showReplQuery QueryPrint = "print" - -parseReplQuery :: String -> Maybe ReplQuery -parseReplQuery "loaded" = Just QueryLoaded -parseReplQuery "import" = Just QueryImport -parseReplQuery "print" = Just QueryPrint -parseReplQuery _ = Nothing - -data Directive - = Help - | Quit - | Reload - | Clear - | Browse - | Type - | Kind - | Show - | Paste - | Complete - | Print - deriving (Eq, Show) diff --git a/claude-help/original-compiler/src/Language/PureScript/Label.hs b/claude-help/original-compiler/src/Language/PureScript/Label.hs deleted file mode 100644 index a5d080a7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Label.hs +++ /dev/null @@ -1,21 +0,0 @@ -module Language.PureScript.Label (Label(..)) where - -import Prelude -import GHC.Generics (Generic) -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Data.Monoid () -import Data.String (IsString(..)) -import Data.Aeson qualified as A - -import Language.PureScript.PSString (PSString) - --- | --- Labels are used as record keys and row entry names. Labels newtype PSString --- because records are indexable by PureScript strings at runtime. --- -newtype Label = Label { runLabel :: PSString } - deriving (Show, Eq, Ord, IsString, Semigroup, Monoid, A.ToJSON, A.FromJSON, Generic) - -instance NFData Label -instance Serialise Label diff --git a/claude-help/original-compiler/src/Language/PureScript/Linter.hs b/claude-help/original-compiler/src/Language/PureScript/Linter.hs deleted file mode 100644 index 9bce1909..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Linter.hs +++ /dev/null @@ -1,299 +0,0 @@ --- | --- This module implements a simple linting pass on the PureScript AST. --- -module Language.PureScript.Linter (lint, module L) where - -import Prelude - -import Control.Monad.Writer.Class (MonadWriter(..), censor) - -import Data.Maybe (mapMaybe) -import Data.Set qualified as S -import Data.Text (Text) -import Data.Text qualified as Text -import Control.Monad ((<=<)) - -import Language.PureScript.AST -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage') -import Language.PureScript.Linter.Exhaustive as L -import Language.PureScript.Linter.Imports as L -import Language.PureScript.Names (Ident(..), Qualified(..), QualifiedBy(..), getIdentName, runIdent) -import Language.PureScript.Types (Constraint(..), SourceType, Type(..), everythingWithContextOnTypes) -import Language.PureScript.Constants.Libs qualified as C - --- | Lint the PureScript AST. --- | --- | Right now, this pass performs a shadowing check and a check for unused bindings. -lint :: forall m. (MonadWriter MultipleErrors m) => Module -> m () -lint modl@(Module _ _ mn ds _) = do - lintUnused modl - censor (addHint (ErrorInModule mn)) $ mapM_ lintDeclaration ds - - where - moduleNames :: S.Set ScopedIdent - moduleNames = S.fromList (map ToplevelIdent (mapMaybe getDeclIdent ds)) - - getDeclIdent :: Declaration -> Maybe Ident - getDeclIdent = getIdentName <=< declName - - lintDeclaration :: Declaration -> m () - lintDeclaration = tell . f - where - (warningsInDecl, _, _, _, _) = everythingWithScope (\_ _ -> mempty) stepE stepB (\_ _ -> mempty) stepDo - - f :: Declaration -> MultipleErrors - f (TypeClassDeclaration _ name args _ _ decs) = addHint (ErrorInTypeClassDeclaration name) (foldMap (f' (S.fromList $ fst <$> args)) decs) - f dec = f' S.empty dec - - f' :: S.Set Text -> Declaration -> MultipleErrors - f' s dec@(ValueDeclaration vd) = - addHint (ErrorInValueDeclaration (valdeclIdent vd)) (warningsInDecl moduleNames dec <> checkTypeVarsInDecl s dec) - f' s (TypeDeclaration td@(TypeDeclarationData (ss, _) _ _)) = - addHint (ErrorInTypeDeclaration (tydeclIdent td)) (checkTypeVars ss s (tydeclType td)) - f' s dec = warningsInDecl moduleNames dec <> checkTypeVarsInDecl s dec - - stepE :: S.Set ScopedIdent -> Expr -> MultipleErrors - stepE s (Abs (VarBinder ss name) _) | name `inScope` s = errorMessage' ss (ShadowedName name) - stepE s (Let _ ds' _) = foldMap go ds' - where - go d | Just i <- getDeclIdent d - , inScope i s = errorMessage' (declSourceSpan d) (ShadowedName i) - | otherwise = mempty - stepE _ _ = mempty - - stepB :: S.Set ScopedIdent -> Binder -> MultipleErrors - stepB s (VarBinder ss name) - | name `inScope` s - = errorMessage' ss (ShadowedName name) - stepB s (NamedBinder ss name _) - | inScope name s - = errorMessage' ss (ShadowedName name) - stepB _ _ = mempty - - stepDo :: S.Set ScopedIdent -> DoNotationElement -> MultipleErrors - stepDo s (DoNotationLet ds') = foldMap go ds' - where - go d - | Just i <- getDeclIdent d, i `inScope` s = errorMessage' (declSourceSpan d) (ShadowedName i) - | otherwise = mempty - stepDo _ _ = mempty - - checkTypeVarsInDecl :: S.Set Text -> Declaration -> MultipleErrors - checkTypeVarsInDecl s d = let (f, _, _, _, _) = accumTypes (checkTypeVars (declSourceSpan d) s) in f d - - checkTypeVars :: SourceSpan -> S.Set Text -> SourceType -> MultipleErrors - checkTypeVars ss set ty = everythingWithContextOnTypes set mempty mappend step ty <> snd (findUnused ty) - where - - step :: S.Set Text -> SourceType -> (S.Set Text, MultipleErrors) - step s (ForAll _ _ tv _ _ _) = bindVar s tv - step s _ = (s, mempty) - - bindVar :: S.Set Text -> Text -> (S.Set Text, MultipleErrors) - bindVar = bind ss ShadowedTypeVar - - findUnused :: SourceType -> (S.Set Text, MultipleErrors) - findUnused = go set where - -- Recursively walk the type and prune used variables from `unused` - go :: S.Set Text -> SourceType -> (S.Set Text, MultipleErrors) - go unused (TypeVar _ v) = (S.delete v unused, mempty) - go unused (ForAll _ _ tv mbK t1 _) = - let (nowUnused, errors) - | Just k <- mbK = go unused k `combine` go (S.insert tv unused) t1 - | otherwise = go (S.insert tv unused) t1 - restoredUnused = if S.member tv unused then S.insert tv nowUnused else nowUnused - combinedErrors = if S.member tv nowUnused then errors <> errorMessage' ss (UnusedTypeVar tv) else errors - in (restoredUnused, combinedErrors) - go unused (TypeApp _ f x) = go unused f `combine` go unused x - go unused (KindApp _ f x) = go unused f `combine` go unused x - go unused (ConstrainedType _ c t1) = foldl combine (unused, mempty) $ map (go unused) (constraintArgs c <> [t1]) - go unused (RCons _ _ t1 rest) = go unused t1 `combine` go unused rest - go unused (KindedType _ t1 _) = go unused t1 - go unused (ParensInType _ t1) = go unused t1 - go unused (BinaryNoParensType _ t1 t2 t3) = go unused t1 `combine` go unused t2 `combine` go unused t3 - go unused TUnknown{} = (unused, mempty) - go unused TypeLevelString{} = (unused, mempty) - go unused TypeLevelInt{} = (unused, mempty) - go unused TypeWildcard{} = (unused, mempty) - go unused TypeConstructor{} = (unused, mempty) - go unused TypeOp{} = (unused, mempty) - go unused Skolem{} = (unused, mempty) - go unused REmpty{} = (unused, mempty) - - combine :: - (S.Set Text, MultipleErrors) -> - (S.Set Text, MultipleErrors) -> - (S.Set Text, MultipleErrors) - combine (a, b) (c, d) = (S.intersection a c, b <> d) - - bind :: (Ord a) => SourceSpan -> (a -> SimpleErrorMessage) -> S.Set a -> a -> (S.Set a, MultipleErrors) - bind ss mkError s name - | name `S.member` s = (s, errorMessage' ss (mkError name)) - | otherwise = (S.insert name s, mempty) - - - -lintUnused :: forall m. (MonadWriter MultipleErrors m) => Module -> m () -lintUnused (Module modSS _ mn modDecls exports) = - censor (addHint (ErrorInModule mn)) $ do - topVars <- traverse lintDeclaration modDecls - let allVars = S.unions topVars - case exports of - Nothing -> - pure () - Just exports' - | any thisModuleRef exports' -> pure () - | otherwise -> do - let exportIds = S.fromList $ mapMaybe getValueRef exports' - expectedUsedDecls = S.fromList (mapMaybe getDeclIdent $ filter isValueDecl modDecls) `S.difference` exportIds - unused = (expectedUsedDecls `S.difference` allVars) `S.difference` rebindable - newErrs = mconcat $ map unusedDeclError $ S.toList unused - tell newErrs - pure () - where - unusedDeclError ident = errorMessage' ss $ UnusedDeclaration ident - where - ss = case filter ((== Just ident) . getDeclIdent) modDecls of - decl:_ -> declSourceSpan decl - _ -> modSS - - thisModuleRef :: DeclarationRef -> Bool - thisModuleRef (ModuleRef _ mn') = mn == mn' - thisModuleRef _ = False - - rebindable :: S.Set Ident - rebindable = S.fromList [ Ident C.S_bind, Ident C.S_discard ] - - getDeclIdent :: Declaration -> Maybe Ident - getDeclIdent = getIdentName <=< declName - - lintDeclaration :: Declaration -> m (S.Set Ident) - lintDeclaration declToLint = do - let (vars, errs) = goDecl declToLint - tell errs - pure vars - where - - goDecl :: Declaration -> (S.Set Ident, MultipleErrors) - goDecl (ValueDeclaration vd) = - let allExprs = concatMap unguard $ valdeclExpression vd - bindNewNames = S.fromList (concatMap binderNamesWithSpans $ valdeclBinders vd) - (vars, errs) = removeAndWarn bindNewNames $ mconcat $ map go allExprs - errs' = addHint (ErrorInValueDeclaration $ valdeclIdent vd) errs - in - (vars, errs') - - goDecl (ValueFixityDeclaration _ _ (Qualified _ (Left v)) _) = (S.singleton v, mempty) - - goDecl (TypeInstanceDeclaration _ _ _ _ _ _ _ _ (ExplicitInstance decls)) = mconcat $ map goDecl decls - goDecl _ = mempty - - go :: Expr -> (S.Set Ident, MultipleErrors) - go (Var _ (Qualified (BySourcePos _) v)) = (S.singleton v, mempty) - go (Var _ _) = (S.empty, mempty) - - go (Let _ ds e) = onDecls ds (go e) - - go (Abs binder v1) = - let newNames = S.fromList (binderNamesWithSpans binder) - in - removeAndWarn newNames $ go v1 - - go (UnaryMinus _ v1) = go v1 - go (BinaryNoParens v0 v1 v2) = go v0 <> go v1 <> go v2 - go (Parens v1) = go v1 - go (Accessor _ v1) = go v1 - - go (ObjectUpdate obj vs) = mconcat (go obj : map (go . snd) vs) - go (ObjectUpdateNested obj vs) = go obj <> goTree vs - where - goTree (PathTree tree) = mconcat $ map (goNode . snd) (runAssocList tree) - goNode (Leaf val) = go val - goNode (Branch val) = goTree val - - go (App v1 v2) = go v1 <> go v2 - go (VisibleTypeApp v _) = go v - go (Unused v) = go v - go (IfThenElse v1 v2 v3) = go v1 <> go v2 <> go v3 - go (Case vs alts) = - let f (CaseAlternative binders gexprs) = - let bindNewNames = S.fromList (concatMap binderNamesWithSpans binders) - allExprs = concatMap unguard gexprs - in - removeAndWarn bindNewNames $ mconcat $ map go allExprs - in - mconcat $ map go vs ++ map f alts - - go (TypedValue _ v1 _) = go v1 - go (Do _ es) = doElts es Nothing - go (Ado _ es v1) = doElts es (Just v1) - - go (Literal _ (ArrayLiteral es)) = mconcat $ map go es - go (Literal _ (ObjectLiteral oo)) = mconcat $ map (go . snd) oo - - go (PositionedValue _ _ v1) = go v1 - - go (Literal _ _) = mempty - go (Op _ _) = mempty - go (Constructor _ _) = mempty - go (TypeClassDictionary _ _ _) = mempty - go (DeferredDictionary _ _) = mempty - go (DerivedInstancePlaceholder _ _) = mempty - go AnonymousArgument = mempty - go (Hole _) = mempty - - - doElts :: [DoNotationElement] -> Maybe Expr -> (S.Set Ident, MultipleErrors) - doElts (DoNotationValue e : rest) v = go e <> doElts rest v - doElts (DoNotationBind binder e : rest) v = - let bindNewNames = S.fromList (binderNamesWithSpans binder) - in go e <> removeAndWarn bindNewNames (doElts rest v) - - doElts (DoNotationLet ds : rest) v = onDecls ds (doElts rest v) - - doElts (PositionedDoNotationElement _ _ e : rest) v = doElts (e : rest) v - doElts [] (Just e) = go e <> (rebindable, mempty) - doElts [] Nothing = (rebindable, mempty) - - -- (non-recursively, recursively) bound idents in decl - declIdents :: Declaration -> (S.Set (SourceSpan, Ident), S.Set (SourceSpan, Ident)) - declIdents (ValueDecl (ss,_) ident _ _ _) = (S.empty, S.singleton (ss, ident)) - declIdents (BoundValueDeclaration _ binders _) = (S.fromList $ binderNamesWithSpans binders, S.empty) - declIdents _ = (S.empty, S.empty) - - onDecls :: [ Declaration ] -> (S.Set Ident, MultipleErrors) -> (S.Set Ident, MultipleErrors) - onDecls ds errs = - let - onDecl d (accErrs, accLetNamesRec) = - let (letNames, recNames) = declIdents d - dErrs = underDecl d - errs' = dErrs <> removeAndWarn letNames accErrs - in - (errs', accLetNamesRec <> recNames) - (errs'', letNamesRec) = foldr onDecl (errs, S.empty) ds - in - removeAndWarn letNamesRec errs'' - - -- let f x = e -- check the x in e (but not the f) - underDecl (ValueDecl _ _ _ binders gexprs) = - let bindNewNames = S.fromList (concatMap binderNamesWithSpans binders) - allExprs = concatMap unguard gexprs - in - removeAndWarn bindNewNames $ foldr1 (<>) $ map go allExprs - -- let {x} = e -- no binding to check inside e - underDecl (BoundValueDeclaration _ _ expr) = go expr - underDecl _ = (mempty, mempty) - - unguard (GuardedExpr guards expr) = map unguard' guards ++ [expr] - unguard' (ConditionGuard ee) = ee - unguard' (PatternGuard _ ee) = ee - - removeAndWarn :: S.Set (SourceSpan, Ident) -> (S.Set Ident, MultipleErrors) -> (S.Set Ident, MultipleErrors) - removeAndWarn newNamesWithSpans (used, errors) = - let newNames = S.map snd newNamesWithSpans - filteredUsed = used `S.difference` newNames - warnUnused = S.filter (not . Text.isPrefixOf "_" . runIdent) (newNames `S.difference` used) - warnUnusedSpans = S.filter (\(_,ident) -> ident `elem` warnUnused) newNamesWithSpans - combinedErrors = if not $ S.null warnUnusedSpans then errors <> mconcat (map (\(ss,ident) -> errorMessage' ss $ UnusedName ident) $ S.toList warnUnusedSpans) else errors - in - (filteredUsed, combinedErrors) diff --git a/claude-help/original-compiler/src/Language/PureScript/Linter/Exhaustive.hs b/claude-help/original-compiler/src/Language/PureScript/Linter/Exhaustive.hs deleted file mode 100644 index eb03da41..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Linter/Exhaustive.hs +++ /dev/null @@ -1,308 +0,0 @@ --- | --- Module for exhaustivity checking over pattern matching definitions --- The algorithm analyses the clauses of a definition one by one from top --- to bottom, where in each step it has the cases already missing (uncovered), --- and it generates the new set of missing cases. --- -module Language.PureScript.Linter.Exhaustive - ( checkExhaustiveExpr - ) where - -import Prelude -import Protolude (ordNub) - -import Control.Arrow (first, second) -import Control.Monad (unless) -import Control.Monad.Writer.Class (MonadWriter(..)) - -import Data.List (foldl', sortOn) -import Data.Maybe (fromMaybe) -import Data.Map qualified as M -import Data.Text qualified as T - -import Language.PureScript.AST.Binders (Binder(..)) -import Language.PureScript.AST.Declarations (CaseAlternative(..), Expr(..), Guard(..), GuardedExpr(..), pattern MkUnguarded, isTrueExpr) -import Language.PureScript.AST.Literals (Literal(..)) -import Language.PureScript.AST.Traversals (everywhereOnValuesM) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType, Environment(..), TypeKind(..)) -import Language.PureScript.Errors (MultipleErrors, pattern NullSourceAnn, SimpleErrorMessage(..), SourceSpan, errorMessage') -import Language.PureScript.Names as P -import Language.PureScript.Pretty.Values (prettyPrintBinderAtom) -import Language.PureScript.Types as P -import Language.PureScript.Constants.Prim qualified as C - --- | There are two modes of failure for the redundancy check: --- --- 1. Exhaustivity was incomplete due to too many cases, so we couldn't determine redundancy. --- 2. We didn't attempt to determine redundancy for a binder, e.g. an integer binder. --- --- We want to warn the user in the first case. -data RedundancyError = Incomplete | Unknown - --- | --- Qualifies a propername from a given qualified propername and a default module name --- -qualifyName - :: ProperName a - -> ModuleName - -> Qualified (ProperName b) - -> Qualified (ProperName a) -qualifyName n defmn qn = Qualified (ByModuleName mn) n - where - (mn, _) = qualify defmn qn - --- | --- Given an environment and a datatype or newtype name, --- this function returns the associated data constructors if it is the case of a datatype --- where: - ProperName is the name of the constructor (for example, "Nothing" in Maybe) --- - [Type] is the list of arguments, if it has (for example, "Just" has [TypeVar "a"]) --- -getConstructors :: Environment -> ModuleName -> Qualified (ProperName 'ConstructorName) -> [(ProperName 'ConstructorName, [SourceType])] -getConstructors env defmn n = extractConstructors lnte - where - - extractConstructors :: Maybe (SourceType, TypeKind) -> [(ProperName 'ConstructorName, [SourceType])] - extractConstructors (Just (_, DataType _ _ pt)) = pt - extractConstructors _ = internalError "Data name not in the scope of the current environment in extractConstructors" - - lnte :: Maybe (SourceType, TypeKind) - lnte = M.lookup qpn (types env) - - qpn :: Qualified (ProperName 'TypeName) - qpn = getConsDataName n - - getConsDataName :: Qualified (ProperName 'ConstructorName) -> Qualified (ProperName 'TypeName) - getConsDataName con = - case getConsInfo con of - Nothing -> internalError $ "Constructor " ++ T.unpack (showQualified runProperName con) ++ " not in the scope of the current environment in getConsDataName." - Just (_, pm, _, _) -> qualifyName pm defmn con - - getConsInfo :: Qualified (ProperName 'ConstructorName) -> Maybe (DataDeclType, ProperName 'TypeName, SourceType, [Ident]) - getConsInfo con = M.lookup con (dataConstructors env) - --- | --- Replicates a wildcard binder --- -initialize :: Int -> [Binder] -initialize l = replicate l NullBinder - --- | --- Applies a function over two lists of tuples that may lack elements --- -genericMerge :: Ord a => - (a -> Maybe b -> Maybe c -> d) -> - [(a, b)] -> - [(a, c)] -> - [d] -genericMerge _ [] [] = [] -genericMerge f bs [] = map (\(s, b) -> f s (Just b) Nothing) bs -genericMerge f [] bs = map (\(s, b) -> f s Nothing (Just b)) bs -genericMerge f bsl@((s, b):bs) bsr@((s', b'):bs') - | s < s' = f s (Just b) Nothing : genericMerge f bs bsr - | s > s' = f s' Nothing (Just b') : genericMerge f bsl bs' - | otherwise = f s (Just b) (Just b') : genericMerge f bs bs' - --- | --- Find the uncovered set between two binders: --- the first binder is the case we are trying to cover, the second one is the matching binder --- -missingCasesSingle :: Environment -> ModuleName -> Binder -> Binder -> ([Binder], Either RedundancyError Bool) -missingCasesSingle _ _ _ NullBinder = ([], return True) -missingCasesSingle _ _ _ (VarBinder _ _) = ([], return True) -missingCasesSingle env mn (VarBinder _ _) b = missingCasesSingle env mn NullBinder b -missingCasesSingle env mn br (NamedBinder _ _ bl) = missingCasesSingle env mn br bl -missingCasesSingle env mn NullBinder cb@(ConstructorBinder ss con _) = - (concatMap (\cp -> fst $ missingCasesSingle env mn cp cb) allPatterns, return True) - where - allPatterns = map (\(p, t) -> ConstructorBinder ss (qualifyName p mn con) (initialize $ length t)) - $ getConstructors env mn con -missingCasesSingle env mn cb@(ConstructorBinder ss con bs) (ConstructorBinder _ con' bs') - | con == con' = let (bs'', pr) = missingCasesMultiple env mn bs bs' in (map (ConstructorBinder ss con) bs'', pr) - | otherwise = ([cb], return False) -missingCasesSingle env mn NullBinder (LiteralBinder ss (ObjectLiteral bs)) = - (map (LiteralBinder ss . ObjectLiteral . zip (map fst bs)) allMisses, pr) - where - (allMisses, pr) = missingCasesMultiple env mn (initialize $ length bs) (map snd bs) -missingCasesSingle env mn (LiteralBinder _ (ObjectLiteral bs)) (LiteralBinder ss (ObjectLiteral bs')) = - (map (LiteralBinder ss . ObjectLiteral . zip sortedNames) allMisses, pr) - where - (allMisses, pr) = uncurry (missingCasesMultiple env mn) (unzip binders) - - sortNames = sortOn fst - - (sbs, sbs') = (sortNames bs, sortNames bs') - - compB :: a -> Maybe a -> Maybe a -> (a, a) - compB e b b' = (fm b, fm b') - where - fm = fromMaybe e - - compBS :: b -> a -> Maybe b -> Maybe b -> (a, (b, b)) - compBS e s b b' = (s, compB e b b') - - (sortedNames, binders) = unzip $ genericMerge (compBS NullBinder) sbs sbs' -missingCasesSingle _ _ NullBinder (LiteralBinder ss (BooleanLiteral b)) = ([LiteralBinder ss . BooleanLiteral $ not b], return True) -missingCasesSingle _ _ (LiteralBinder ss (BooleanLiteral bl)) (LiteralBinder _ (BooleanLiteral br)) - | bl == br = ([], return True) - | otherwise = ([LiteralBinder ss $ BooleanLiteral bl], return False) -missingCasesSingle env mn b (PositionedBinder _ _ cb) = missingCasesSingle env mn b cb -missingCasesSingle env mn b (TypedBinder _ cb) = missingCasesSingle env mn b cb -missingCasesSingle _ _ b _ = ([b], Left Unknown) - --- | --- Returns the uncovered set of binders --- the first argument is the list of uncovered binders at step i --- the second argument is the (i+1)th clause of a pattern matching definition --- --- The idea of the algorithm is as follows: --- it processes each binder of the two lists (say, `x` and `y`) one by one --- at each step two cases arises: --- - there are no missing cases between `x` and `y`: this is very straightforward, it continues with the remaining cases --- but keeps the uncovered binder in its position. --- - there are missing cases, let us call it the set `U`: on the one hand, we mix each new uncovered case in `U` --- with the current tail of uncovered set. On the other hand, it continues with the remaining cases: here we --- can use `x` (but it will generate overlapping cases), or `y`, which will generate non-overlapping cases. --- --- As an example, consider: --- --- data N = Z | S N --- f Z Z = Z --> {[S _, _], [Z, S _]} which are the right non-overlapping cases (GHC uses this). --- --- if we use `x` instead of `y` (in this case, `y` stands for `Z` and `x` for `_`) we obtain: --- f Z Z = Z --> {[S _, _], [_, S _]} you can see that both cases overlaps each other. --- --- Up to now, we've decided to use `x` just because we expect to generate uncovered cases which might be --- redundant or not, but uncovered at least. If we use `y` instead, we'll need to have a redundancy checker --- (which ought to be available soon), or increase the complexity of the algorithm. --- -missingCasesMultiple :: Environment -> ModuleName -> [Binder] -> [Binder] -> ([[Binder]], Either RedundancyError Bool) -missingCasesMultiple env mn = go - where - go (x:xs) (y:ys) = (map (: xs) miss1 ++ map (x :) miss2, liftA2 (&&) pr1 pr2) - where - (miss1, pr1) = missingCasesSingle env mn x y - (miss2, pr2) = go xs ys - go _ _ = ([], pure True) - --- | --- Guard handling --- --- We say a guard is exhaustive iff it has an `otherwise` (or `true`) expression. --- Example: --- f x | x < 0 = 0 --- | otherwise = 1 --- is exhaustive, whereas `f x | x < 0` is not --- --- or in case of a pattern guard if the pattern is exhaustive. --- --- The function below say whether or not a guard has an `otherwise` expression --- It is considered that `otherwise` is defined in Prelude --- -isExhaustiveGuard :: Environment -> ModuleName -> [GuardedExpr] -> Bool -isExhaustiveGuard _ _ [MkUnguarded _] = True -isExhaustiveGuard env moduleName gs = - any (\(GuardedExpr grd _) -> isExhaustive grd) gs - where - isExhaustive :: [Guard] -> Bool - isExhaustive = all checkGuard - - checkGuard :: Guard -> Bool - checkGuard (ConditionGuard cond) = isTrueExpr cond - checkGuard (PatternGuard binder _) = - case missingCasesMultiple env moduleName [NullBinder] [binder] of - ([], _) -> True -- there are no missing pattern for this guard - _ -> False - --- | --- Returns the uncovered set of case alternatives --- -missingCases :: Environment -> ModuleName -> [Binder] -> CaseAlternative -> ([[Binder]], Either RedundancyError Bool) -missingCases env mn uncovered ca = missingCasesMultiple env mn uncovered (caseAlternativeBinders ca) - -missingAlternative :: Environment -> ModuleName -> CaseAlternative -> [Binder] -> ([[Binder]], Either RedundancyError Bool) -missingAlternative env mn ca uncovered - | isExhaustiveGuard env mn (caseAlternativeResult ca) = mcases - | otherwise = ([uncovered], snd mcases) - where - mcases = missingCases env mn uncovered ca - --- | --- Main exhaustivity checking function --- Starting with the set `uncovered = { _ }` (nothing covered, one `_` for each function argument), --- it partitions that set with the new uncovered cases, until it consumes the whole set of clauses. --- Then, returns the uncovered set of case alternatives. --- -checkExhaustive - :: forall m - . MonadWriter MultipleErrors m - => SourceSpan - -> Environment - -> ModuleName - -> Int - -> [CaseAlternative] - -> Expr - -> m Expr -checkExhaustive ss env mn numArgs cas expr = makeResult . first ordNub $ foldl' step ([initialize numArgs], (pure True, [])) cas - where - step :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Either RedundancyError Bool, [[Binder]])) - step (uncovered, (nec, redundant)) ca = - let (missed, pr) = unzip (map (missingAlternative env mn ca) uncovered) - (missed', approx) = splitAt 10000 (ordNub (concat missed)) - cond = or <$> sequenceA pr - in (missed', ( if null approx - then liftA2 (&&) cond nec - else Left Incomplete - , if and cond - then redundant - else caseAlternativeBinders ca : redundant - ) - ) - - makeResult :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> m Expr - makeResult (bss, (rr, bss')) = - do unless (null bss') tellRedundant - case rr of - Left Incomplete -> tellIncomplete - _ -> return () - return $ if null bss - then expr - else addPartialConstraint (second null (splitAt 5 bss)) expr - where - tellRedundant = tell . errorMessage' ss . uncurry OverlappingPattern . second null . splitAt 5 $ bss' - tellIncomplete = tell . errorMessage' ss $ IncompleteExhaustivityCheck - - -- We add a Partial constraint by annotating the expression to have type `Partial => _`. - -- - -- The binder information is provided so that it can be embedded in the constraint, - -- and then included in the error message. - addPartialConstraint :: ([[Binder]], Bool) -> Expr -> Expr - addPartialConstraint (bss, complete) e = - TypedValue True e $ - srcConstrainedType (srcConstraint C.Partial [] [] (Just constraintData)) $ TypeWildcard NullSourceAnn IgnoredWildcard - where - constraintData :: ConstraintData - constraintData = - PartialConstraintData (map (map prettyPrintBinderAtom) bss) complete - --- | --- Exhaustivity checking --- -checkExhaustiveExpr - :: forall m - . MonadWriter MultipleErrors m - => SourceSpan - -> Environment - -> ModuleName - -> Expr - -> m Expr -checkExhaustiveExpr ss env mn = onExpr' - where - (_, onExpr', _) = everywhereOnValuesM pure onExpr pure - - onExpr :: Expr -> m Expr - onExpr e = case e of - Case es cas -> - checkExhaustive ss env mn (length es) cas e - _ -> - pure e diff --git a/claude-help/original-compiler/src/Language/PureScript/Linter/Imports.hs b/claude-help/original-compiler/src/Language/PureScript/Linter/Imports.hs deleted file mode 100644 index 10f0aec7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Linter/Imports.hs +++ /dev/null @@ -1,388 +0,0 @@ -module Language.PureScript.Linter.Imports - ( lintImports - , Name(..) - , UsedImports() - ) where - -import Prelude -import Protolude (ordNub, tailDef, headDef) - -import Control.Monad (join, unless, foldM, (<=<)) -import Control.Monad.Writer.Class (MonadWriter(..)) - -import Data.Function (on) -import Data.Foldable (for_) -import Data.List (find, intersect, groupBy, sort, sortOn, (\\)) -import Data.Maybe (mapMaybe) -import Data.Monoid (Sum(..)) -import Data.Traversable (forM) -import Data.Text qualified as T -import Data.Map qualified as M - -import Language.PureScript.AST.Declarations (Declaration(..), DeclarationRef(..), ExportSource, ImportDeclarationType(..), Module(..), getTypeRef, isExplicit) -import Language.PureScript.AST.SourcePos (SourceSpan) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage') -import Language.PureScript.Names -import Language.PureScript.Sugar.Names.Common (warnDuplicateRefs) -import Language.PureScript.Sugar.Names.Env (Env, Exports(..), ImportRecord(..), Imports(..), envModuleExports, nullImports) -import Language.PureScript.Sugar.Names.Imports (ImportDef, findImports) -import Language.PureScript.Constants.Prim qualified as C - --- | --- Map of module name to list of imported names from that module which have --- been used. --- -type UsedImports = M.Map ModuleName [Qualified Name] - --- | --- Find and warn on: --- --- * Unused import statements (qualified or unqualified) --- --- * Unused references in an explicit import list --- --- * Implicit imports of modules --- --- * Implicit imports into a virtual module (unless the virtual module only has --- members from one module imported) --- --- * Imports using `hiding` (this is another form of implicit importing) --- -lintImports - :: forall m - . MonadWriter MultipleErrors m - => Module - -> Env - -> UsedImports - -> m () -lintImports (Module _ _ _ _ Nothing) _ _ = - internalError "lintImports needs desugared exports" -lintImports (Module _ _ mn mdecls (Just mexports)) env usedImps = do - - -- TODO: this needs some work to be easier to understand - - let scope = maybe nullImports (\(_, imps', _) -> imps') (M.lookup mn env) - usedImps' = foldr (elaborateUsed scope) usedImps exportedModules - numOpenImports = getSum $ foldMap (Sum . countOpenImports) mdecls - allowImplicit = numOpenImports == 1 - imports = M.toAscList (findImports mdecls) - - for_ imports $ \(mni, decls) -> - unless (isPrim mni) . - for_ decls $ \(ss, declType, qualifierName) -> do - let names = ordNub $ M.findWithDefault [] mni usedImps' - lintImportDecl env mni qualifierName names ss declType allowImplicit - - for_ (M.toAscList (byQual imports)) $ \(mnq, entries) -> do - let mnis = ordNub $ map (\(_, _, mni) -> mni) entries - unless (length mnis == 1) $ do - let implicits = filter (\(_, declType, _) -> not $ isExplicit declType) entries - for_ implicits $ \(ss, _, mni) -> do - let names = ordNub $ M.findWithDefault [] mni usedImps' - usedRefs = findUsedRefs ss env mni (Just mnq) names - unless (null usedRefs) . - tell . errorMessage' ss $ ImplicitQualifiedImport mni mnq $ map (simplifyTypeRef $ const True) usedRefs - - for_ imports $ \(mnq, imps) -> do - - warned <- foldM (checkDuplicateImports mnq) [] (selfCartesianSubset imps) - - let unwarned = imps \\ warned - duplicates - = join - . map (tailDef $ internalError "lintImports: duplicates") - . filter ((> 1) . length) - . groupBy ((==) `on` defQual) - . sortOn defQual - $ unwarned - - for_ duplicates $ \(pos, _, _) -> - tell . errorMessage' pos $ DuplicateSelectiveImport mnq - - for_ (imps \\ (warned ++ duplicates)) $ \(pos, typ, _) -> - warnDuplicateRefs pos DuplicateImportRef $ case typ of - Explicit refs -> refs - Hiding refs -> refs - _ -> [] - - -- Check re-exported modules to see if we are re-exporting a qualified module - -- that has unspecified imports. - for_ mexports $ \case - ModuleRef _ mnq -> - case M.lookup mnq (byQual imports) of - -- We only match the single-entry case here as otherwise there will be - -- a different warning about implicit imports potentially colliding - -- anyway - Just [(ss, Implicit, mni)] -> do - let names = ordNub $ M.findWithDefault [] mni usedImps' - usedRefs = findUsedRefs ss env mni (Just mnq) names - tell . errorMessage' ss $ - ImplicitQualifiedImportReExport mni mnq - $ map (simplifyTypeRef $ const True) usedRefs - _ -> pure () - _ -> pure () - - where - - defQual :: ImportDef -> Maybe ModuleName - defQual (_, _, q) = q - - selfCartesianSubset :: [a] -> [(a, a)] - selfCartesianSubset (x : xs) = [(x, y) | y <- xs] ++ selfCartesianSubset xs - selfCartesianSubset [] = [] - - countOpenImports :: Declaration -> Int - countOpenImports (ImportDeclaration _ mn' Implicit Nothing) - | not (isPrim mn' || mn == mn') = 1 - countOpenImports (ImportDeclaration _ mn' (Hiding _) Nothing) - | not (isPrim mn' || mn == mn') = 1 - countOpenImports _ = 0 - - -- Checks whether a module is the Prim module - used to suppress any checks - -- made, as Prim is always implicitly imported. - isPrim :: ModuleName -> Bool - isPrim = (== C.M_Prim) - - -- Creates a map of virtual modules mapped to all the declarations that - -- import to that module, with the corresponding source span, import type, - -- and module being imported - byQual - :: [(ModuleName, [(SourceSpan, ImportDeclarationType, Maybe ModuleName)])] - -> M.Map ModuleName [(SourceSpan, ImportDeclarationType, ModuleName)] - byQual = foldr goImp M.empty - where - goImp (mni, xs) acc = foldr (goDecl mni) acc xs - goDecl mni (ss', declType, Just qmn) acc = - let entry = (ss', declType, mni) - in M.alter (Just . maybe [entry] (entry :)) qmn acc - goDecl _ _ acc = acc - - -- The list of modules that are being re-exported by the current module. Any - -- module that appears in this list is always considered to be used. - exportedModules :: [ModuleName] - exportedModules = ordNub $ mapMaybe extractModule mexports - where - extractModule (ModuleRef _ mne) = Just mne - extractModule _ = Nothing - - -- Elaborates the UsedImports to include values from modules that are being - -- re-exported. This ensures explicit export hints are printed for modules - -- that are implicitly exported and then re-exported. - elaborateUsed :: Imports -> ModuleName -> UsedImports -> UsedImports - elaborateUsed scope mne used = - foldr go used - $ extractByQual mne (importedTypeClasses scope) TyClassName - ++ extractByQual mne (importedTypeOps scope) TyOpName - ++ extractByQual mne (importedTypes scope) TyName - ++ extractByQual mne (importedDataConstructors scope) DctorName - ++ extractByQual mne (importedValues scope) IdentName - ++ extractByQual mne (importedValueOps scope) ValOpName - where - go :: (ModuleName, Qualified Name) -> UsedImports -> UsedImports - go (q, name) = M.alter (Just . maybe [name] (name :)) q - - extractByQual - :: ModuleName - -> M.Map (Qualified a) [ImportRecord a] - -> (a -> Name) - -> [(ModuleName, Qualified Name)] - extractByQual k m toName = mapMaybe go (M.toList m) - where - go (q@(Qualified mnq _), is) - | isUnqualified q = - case find (isQualifiedWith k) (map importName is) of - Just (Qualified _ name) -> Just (k, Qualified mnq (toName name)) - _ -> Nothing - | isQualifiedWith k q = - case importName (headDef (internalError "extractByQual: empty import list") is) of - Qualified (ByModuleName mn') name -> Just (mn', Qualified mnq (toName name)) - _ -> internalError "unqualified name in extractByQual" - go _ = Nothing - - --- Replace explicit type refs with data constructor lists from listing the --- used constructors explicitly `T(X, Y, [...])` to `T(..)` for suggestion --- message. --- Done everywhere when suggesting a completely new explicit imports list, otherwise --- maintain the existing form. -simplifyTypeRef :: (ProperName 'TypeName -> Bool) -> DeclarationRef -> DeclarationRef -simplifyTypeRef shouldOpen (TypeRef ss name (Just dctors)) - | not (null dctors) && shouldOpen name = TypeRef ss name Nothing -simplifyTypeRef _ other = other - -lintImportDecl - :: forall m - . MonadWriter MultipleErrors m - => Env - -> ModuleName - -> Maybe ModuleName - -> [Qualified Name] - -> SourceSpan - -> ImportDeclarationType - -> Bool - -> m Bool -lintImportDecl env mni qualifierName names ss declType allowImplicit = - case declType of - Implicit -> case qualifierName of - Nothing -> - if null allRefs - then unused - else unless' allowImplicit (checkImplicit ImplicitImport) - Just q -> unless' (q `elem` mapMaybe getQual names) unused - Hiding _ -> unless' allowImplicit (checkImplicit HidingImport) - Explicit [] -> unused - Explicit declrefs -> checkExplicit declrefs - - where - - checkImplicit - :: (ModuleName -> [DeclarationRef] -> SimpleErrorMessage) - -> m Bool - checkImplicit warning = - if null allRefs - then unused - else warn (warning mni (map (simplifyTypeRef $ const True) allRefs)) - - checkExplicit - :: [DeclarationRef] - -> m Bool - checkExplicit declrefs = do - let idents = ordNub (mapMaybe runDeclRef declrefs) - dctors = mapMaybe (getDctorName <=< disqualifyFor qualifierName) names - usedNames = mapMaybe (matchName (typeForDCtor mni) <=< disqualifyFor qualifierName) names - diff = idents \\ usedNames - - didWarn <- case (length diff, length idents) of - (0, _) -> return False - (n, m) | n == m -> unused - _ -> warn (UnusedExplicitImport mni diff qualifierName $ map simplifyTypeRef' allRefs) - - didWarn' <- forM (mapMaybe getTypeRef declrefs) $ \(tn, c) -> do - let allCtors = dctorsForType mni tn - -- If we've not already warned a type is unused, check its data constructors - unless' (TyName tn `notElem` usedNames) $ - case (c, dctors `intersect` allCtors) of - (_, []) | c /= Just [] -> warn (UnusedDctorImport mni tn qualifierName $ map simplifyTypeRef' allRefs) - (Just ctors, dctors') -> - let ddiff = ctors \\ dctors' - in unless' (null ddiff) . warn $ UnusedDctorExplicitImport mni tn ddiff qualifierName $ map simplifyTypeRef' allRefs - _ -> return False - - return (didWarn || or didWarn') - - where - simplifyTypeRef' :: DeclarationRef -> DeclarationRef - simplifyTypeRef' = simplifyTypeRef (\name -> any (isMatch name) declrefs) - where - isMatch name (TypeRef _ name' Nothing) = name == name' - isMatch _ _ = False - - unused :: m Bool - unused = warn (UnusedImport mni qualifierName) - - warn :: SimpleErrorMessage -> m Bool - warn err = tell (errorMessage' ss err) >> return True - - -- Unless the boolean is true, run the action. Return false when the action is - -- not run, otherwise return whatever the action does. - -- - -- The return value is intended for cases where we want to track whether some - -- work was done, as there may be further conditions in the action that mean - -- it ends up doing nothing. - unless' :: Bool -> m Bool -> m Bool - unless' False m = m - unless' True _ = return False - - allRefs :: [DeclarationRef] - allRefs = findUsedRefs ss env mni qualifierName names - - dtys - :: ModuleName - -> M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - dtys mn = foldMap (exportedTypes . envModuleExports) $ mn `M.lookup` env - - dctorsForType - :: ModuleName - -> ProperName 'TypeName - -> [ProperName 'ConstructorName] - dctorsForType mn tn = maybe [] fst $ tn `M.lookup` dtys mn - - typeForDCtor - :: ModuleName - -> ProperName 'ConstructorName - -> Maybe (ProperName 'TypeName) - typeForDCtor mn pn = fst <$> find (elem pn . fst . snd) (M.toList (dtys mn)) - -findUsedRefs - :: SourceSpan - -> Env - -> ModuleName - -> Maybe ModuleName - -> [Qualified Name] - -> [DeclarationRef] -findUsedRefs ss env mni qn names = - let - classRefs = TypeClassRef ss <$> mapMaybe (getClassName <=< disqualifyFor qn) names - valueRefs = ValueRef ss <$> mapMaybe (getIdentName <=< disqualifyFor qn) names - valueOpRefs = ValueOpRef ss <$> mapMaybe (getValOpName <=< disqualifyFor qn) names - typeOpRefs = TypeOpRef ss <$> mapMaybe (getTypeOpName <=< disqualifyFor qn) names - types = mapMaybe (getTypeName <=< disqualifyFor qn) names - dctors = mapMaybe (getDctorName <=< disqualifyFor qn) names - typesWithDctors = reconstructTypeRefs dctors - typesWithoutDctors = filter (`M.notMember` typesWithDctors) types - typesRefs - = map (flip (TypeRef ss) (Just [])) typesWithoutDctors - ++ map (\(ty, ds) -> TypeRef ss ty (Just ds)) (M.toList typesWithDctors) - in sort $ classRefs ++ typeOpRefs ++ typesRefs ++ valueRefs ++ valueOpRefs - - where - - reconstructTypeRefs - :: [ProperName 'ConstructorName] - -> M.Map (ProperName 'TypeName) [ProperName 'ConstructorName] - reconstructTypeRefs = foldr accumDctors M.empty - where - accumDctors dctor = - M.alter (Just . maybe [dctor] (dctor :)) (findTypeForDctor mni dctor) - - findTypeForDctor - :: ModuleName - -> ProperName 'ConstructorName - -> ProperName 'TypeName - findTypeForDctor mn dctor = - case mn `M.lookup` env of - Just (_, _, exps) -> - case find (elem dctor . fst . snd) (M.toList (exportedTypes exps)) of - Just (ty, _) -> ty - Nothing -> internalError $ "missing type for data constructor " ++ T.unpack (runProperName dctor) ++ " in findTypeForDctor" - Nothing -> internalError $ "missing module " ++ T.unpack (runModuleName mn) ++ " in findTypeForDctor" - -matchName - :: (ProperName 'ConstructorName -> Maybe (ProperName 'TypeName)) - -> Name - -> Maybe Name -matchName lookupDc (DctorName x) = TyName <$> lookupDc x -matchName _ ModName{} = Nothing -matchName _ name = Just name - -runDeclRef :: DeclarationRef -> Maybe Name -runDeclRef (ValueRef _ ident) = Just $ IdentName ident -runDeclRef (ValueOpRef _ op) = Just $ ValOpName op -runDeclRef (TypeRef _ pn _) = Just $ TyName pn -runDeclRef (TypeOpRef _ op) = Just $ TyOpName op -runDeclRef (TypeClassRef _ pn) = Just $ TyClassName pn -runDeclRef _ = Nothing - -checkDuplicateImports - :: MonadWriter MultipleErrors m - => ModuleName - -> [ImportDef] - -> (ImportDef, ImportDef) - -> m [ImportDef] -checkDuplicateImports mn xs ((_, t1, q1), (pos, t2, q2)) = - if t1 == t2 && q1 == q2 - then do - tell . errorMessage' pos $ DuplicateImport mn t2 q2 - return $ (pos, t2, q2) : xs - else return xs diff --git a/claude-help/original-compiler/src/Language/PureScript/Linter/Wildcards.hs b/claude-help/original-compiler/src/Language/PureScript/Linter/Wildcards.hs deleted file mode 100644 index a8b5fcf2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Linter/Wildcards.hs +++ /dev/null @@ -1,47 +0,0 @@ -module Language.PureScript.Linter.Wildcards - ( ignoreWildcardsUnderCompleteTypeSignatures - ) where - -import Protolude hiding (Type) - -import Language.PureScript.AST (Binder(..), Declaration, Expr(..), everywhereWithContextOnValues) -import Language.PureScript.Types (Type(..), WildcardData(..), everythingOnTypes, everywhereOnTypes) - --- | --- Replaces `TypeWildcard _ UnnamedWildcard` with --- `TypeWildcard _ IgnoredWildcard` in places where we don't want to emit a --- warning about wildcards. --- --- The guiding principle here is that a wildcard can be ignored if there is a --- complete (wildcard-free) type signature on a binding somewhere between the --- type in which the wildcard occurs and the top level of the module. In --- particular, this means that top-level signatures containing wildcards are --- always warnings, and a top-level signature always prevents wildcards on --- inner bindings from emitting warnings. --- -ignoreWildcardsUnderCompleteTypeSignatures :: Declaration -> Declaration -ignoreWildcardsUnderCompleteTypeSignatures = onDecl - where - (onDecl, _, _, _, _, _) = everywhereWithContextOnValues False (,) handleExpr handleBinder (,) (,) (,) - - handleExpr isCovered = \case - tv@(TypedValue chk v ty) - | isCovered -> (True, TypedValue chk v $ ignoreWildcards ty) - | otherwise -> (isComplete ty, tv) - other -> (isCovered, other) - - handleBinder isCovered = \case - tb@(TypedBinder ty b) - | isCovered -> (True, TypedBinder (ignoreWildcards ty) b) - | otherwise -> (isComplete ty, tb) - other -> (isCovered, other) - -ignoreWildcards :: Type a -> Type a -ignoreWildcards = everywhereOnTypes $ \case - TypeWildcard a UnnamedWildcard -> TypeWildcard a IgnoredWildcard - other -> other - -isComplete :: Type a -> Bool -isComplete = everythingOnTypes (&&) $ \case - TypeWildcard{} -> False - _ -> True diff --git a/claude-help/original-compiler/src/Language/PureScript/Make.hs b/claude-help/original-compiler/src/Language/PureScript/Make.hs deleted file mode 100644 index b8697e42..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make.hs +++ /dev/null @@ -1,386 +0,0 @@ -module Language.PureScript.Make - ( make - , make_ - , rebuildModule - , rebuildModule' - , inferForeignModules - , module Monad - , module Actions - ) where - -import Prelude - -import Control.Concurrent.Lifted as C -import Control.DeepSeq (force) -import Control.Exception.Lifted (onException, bracket_, evaluate) -import Control.Monad (foldM, unless, void, when, (<=<)) -import Control.Monad.Base (MonadBase(liftBase)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.IO.Class (MonadIO(..)) -import Control.Monad.Supply (evalSupplyT, runSupply, runSupplyT) -import Control.Monad.Trans.Control (MonadBaseControl(..)) -import Control.Monad.Trans.State (runStateT) -import Control.Monad.Writer.Class (MonadWriter(..), censor) -import Control.Monad.Writer.Strict (runWriterT) -import Data.Function (on) -import Data.Foldable (fold, for_) -import Data.List (foldl', sortOn) -import Data.List.NonEmpty qualified as NEL -import Data.Maybe (fromMaybe, mapMaybe) -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Text qualified as T -import Debug.Trace (traceMarkerIO) -import Language.PureScript.AST (ErrorMessageHint(..), Module(..), SourceSpan(..), getModuleName, getModuleSourceSpan, importPrim) -import Language.PureScript.Crash (internalError) -import Language.PureScript.CST qualified as CST -import Language.PureScript.Docs.Convert qualified as Docs -import Language.PureScript.Environment (initEnvironment) -import Language.PureScript.Errors (MultipleErrors(..), SimpleErrorMessage(..), addHint, defaultPPEOptions, errorMessage', errorMessage'', prettyPrintMultipleErrors) -import Language.PureScript.Externs (ExternsFile, applyExternsFileToEnvironment, moduleToExternsFile) -import Language.PureScript.Linter (Name(..), lint, lintImports) -import Language.PureScript.ModuleDependencies (DependencyDepth(..), moduleSignature, sortModules) -import Language.PureScript.Names (ModuleName(..), isBuiltinModuleName, runModuleName) -import Language.PureScript.Renamer (renameInModule) -import Language.PureScript.Sugar (Env, collapseBindingGroups, createBindingGroups, desugar, desugarCaseGuards, externsEnv, primEnv) -import Language.PureScript.TypeChecker (CheckState(..), emptyCheckState, typeCheckModule) -import Language.PureScript.Make.BuildPlan (BuildJobResult(..), BuildPlan(..), getResult, isUpToDate) -import Language.PureScript.Make.BuildPlan qualified as BuildPlan -import Language.PureScript.Make.ExternsDiff (checkDiffs, emptyDiff, diffExterns) -import Language.PureScript.Make.Cache qualified as Cache -import Language.PureScript.Make.Actions as Actions -import Language.PureScript.Make.Monad as Monad - ( Make(..), - writeTextFile, - writeJSONFile, - writeCborFileIO, - writeCborFile, - setTimestamp, - runMake, - readTextFile, - readJSONFileIO, - readJSONFile, - readExternsFile, - readCborFileIO, - readCborFile, - makeIO, - hashFile, - getTimestampMaybe, - getTimestamp, - getCurrentTime, - copyFile ) -import Language.PureScript.CoreFn qualified as CF -import System.Directory (doesFileExist) -import System.FilePath (replaceExtension) -import Language.PureScript.TypeChecker.Monad (liftTypeCheckM) - --- | Rebuild a single module. --- -rebuildModule - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => MakeActions m - -> [ExternsFile] - -> Module - -> m ExternsFile -rebuildModule actions externs m = do - env <- fmap fst . runWriterT $ foldM externsEnv primEnv externs - rebuildModule' actions env externs m - -rebuildModule' - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => MakeActions m - -> Env - -> [ExternsFile] - -> Module - -> m ExternsFile -rebuildModule' act env ext mdl = rebuildModuleWithIndex act env ext mdl Nothing - -rebuildModuleWithIndex - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => MakeActions m - -> Env - -> [ExternsFile] - -> Module - -> Maybe (Int, Int) - -> m ExternsFile -rebuildModuleWithIndex MakeActions{..} exEnv externs m@(Module _ _ moduleName _ _) moduleIndex = do - progress $ CompilingModule moduleName moduleIndex - let env = foldl' (flip applyExternsFileToEnvironment) initEnvironment externs - withPrim = importPrim m - lint withPrim - - ((Module ss coms _ elaborated exps, env'), nextVar) <- runSupplyT 0 $ do - (desugared, (exEnv', usedImports)) <- runStateT (desugar externs withPrim) (exEnv, mempty) - let modulesExports = (\(_, _, exports) -> exports) <$> exEnv' - (checked, CheckState{..}) <- runStateT (liftTypeCheckM $ typeCheckModule modulesExports desugared) $ emptyCheckState env - let usedImports' = foldl' (flip $ \(fromModuleName, newtypeCtorName) -> - M.alter (Just . (fmap DctorName newtypeCtorName :) . fold) fromModuleName) usedImports checkConstructorImportsForCoercible - -- Imports cannot be linted before type checking because we need to - -- known which newtype constructors are used to solve Coercible - -- constraints in order to not report them as unused. - censor (addHint (ErrorInModule moduleName)) $ lintImports checked exEnv' usedImports' - return (checked, checkEnv) - - -- desugar case declarations *after* type- and exhaustiveness checking - -- since pattern guards introduces cases which the exhaustiveness checker - -- reports as not-exhaustive. - (deguarded, nextVar') <- runSupplyT nextVar $ do - desugarCaseGuards elaborated - - regrouped <- createBindingGroups moduleName . collapseBindingGroups $ deguarded - let mod' = Module ss coms moduleName regrouped exps - corefn = CF.moduleToCoreFn env' mod' - (optimized, nextVar'') = runSupply nextVar' $ CF.optimizeCoreFn corefn - (renamedIdents, renamed) = renameInModule optimized - exts = moduleToExternsFile mod' env' renamedIdents - ffiCodegen renamed - - -- It may seem more obvious to write `docs <- Docs.convertModule m env' here, - -- but I have not done so for two reasons: - -- 1. This should never fail; any genuine errors in the code should have been - -- caught earlier in this function. Therefore if we do fail here it indicates - -- a bug in the compiler, which should be reported as such. - -- 2. We do not want to perform any extra work generating docs unless the - -- user has asked for docs to be generated. - let docs = case Docs.convertModule externs exEnv env' withPrim of - Left errs -> internalError $ - "Failed to produce docs for " ++ T.unpack (runModuleName moduleName) - ++ "; details:\n" ++ prettyPrintMultipleErrors defaultPPEOptions errs - Right d -> d - - evalSupplyT nextVar'' $ codegen withPrim renamed docs exts - return exts - -data MakeOptions = MakeOptions - { moCollectAllExterns :: Bool - } - --- | Compiles in "make" mode, compiling each module separately to a @.js@ file --- and an @externs.cbor@ file. --- --- If timestamps or hashes have not changed, existing externs files can be used --- to provide upstream modules' types without having to typecheck those modules --- again. --- --- It collects and returns externs for all modules passed. -make :: forall m. (MonadIO m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => MakeActions m - -> [CST.PartialResult Module] - -> m [ExternsFile] -make = make' (MakeOptions {moCollectAllExterns = True}) - --- | Compiles in "make" mode, compiling each module separately to a @.js@ file --- and an @externs.cbor@ file. --- --- This version of make returns nothing. -make_ :: forall m. (MonadIO m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => MakeActions m - -> [CST.PartialResult Module] - -> m () -make_ ma ms = void $ make' (MakeOptions {moCollectAllExterns = False}) ma ms - -make' :: forall m. (MonadIO m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => MakeOptions - -> MakeActions m - -> [CST.PartialResult Module] - -> m [ExternsFile] -make' MakeOptions{..} ma@MakeActions{..} ms = do - checkModuleNames - cacheDb <- readCacheDb - - (sorted, graph) <- sortModules Transitive (moduleSignature . CST.resPartial) ms - let opts = BuildPlan.Options {optPreloadAllExterns = moCollectAllExterns} - (buildPlan, newCacheDb) <- BuildPlan.construct opts ma cacheDb (sorted, graph) - - -- Limit concurrent module builds to the number of capabilities as - -- (by default) inferred from `+RTS -N -RTS` or set explicitly like `-N4`. - -- This is to ensure that modules complete fully before moving on, to avoid - -- holding excess memory during compilation from modules that were paused - -- by the Haskell runtime. - capabilities <- getNumCapabilities - let concurrency = max 1 capabilities - lock <- C.newQSem concurrency - - let sortedModuleNames = getModuleName . CST.resPartial <$> sorted - let toBeRebuilt = filter (BuildPlan.needsRebuild buildPlan . getModuleName . CST.resPartial) sorted - let totalModuleCount = length toBeRebuilt - for_ toBeRebuilt $ \m -> fork $ do - let moduleName = getModuleName . CST.resPartial $ m - let deps = fromMaybe (internalError "make: module not found in dependency graph.") (lookup moduleName graph) - buildModule lock buildPlan moduleName totalModuleCount - (spanName . getModuleSourceSpan . CST.resPartial $ m) - (fst $ CST.resFull m) - (fmap importPrim . snd $ CST.resFull m) - (deps `inOrderOf` sortedModuleNames) - - -- Prevent hanging on other modules when there is an internal error - -- (the exception is thrown, but other threads waiting on MVars are released) - `onException` BuildPlan.markComplete buildPlan moduleName (BuildJobFailed mempty) - - -- Wait for all threads to complete, and collect results (and errors). - (failures, successes) <- - let - splitResults = \case - BuildJobSucceeded _ exts _ -> - Right exts - BuildJobFailed errs -> - Left errs - BuildJobSkipped -> - Left mempty - in - M.mapEither splitResults <$> BuildPlan.collectResults buildPlan - - -- Write the updated build cache database to disk - writeCacheDb $ Cache.removeModules (M.keysSet failures) newCacheDb - - writePackageJson - - -- If generating docs, also generate them for the Prim modules - outputPrimDocs - -- All threads have completed, rethrow any caught errors. - let errors = M.elems failures - unless (null errors) $ throwError (mconcat errors) - - -- Here we return all the ExternsFile in the ordering of the topological sort, - -- so they can be folded into an Environment. This result is used in the tests - -- and in PSCI. - let lookupResult mn@(ModuleName name) = - fromMaybe (internalError $ "make: module not found in results: " <> T.unpack name) - $ M.lookup mn successes - - pure $ - if moCollectAllExterns then - map lookupResult sortedModuleNames - else - mapMaybe (flip M.lookup successes) sortedModuleNames - - where - checkModuleNames :: m () - checkModuleNames = checkNoPrim *> checkModuleNamesAreUnique - - checkNoPrim :: m () - checkNoPrim = - for_ ms $ \m -> - let mn = getModuleName $ CST.resPartial m - in when (isBuiltinModuleName mn) $ - throwError - . errorMessage' (getModuleSourceSpan $ CST.resPartial m) - $ CannotDefinePrimModules mn - - checkModuleNamesAreUnique :: m () - checkModuleNamesAreUnique = - for_ (findDuplicates (getModuleName . CST.resPartial) ms) $ \mss -> - throwError . flip foldMap mss $ \ms' -> - let mn = getModuleName . CST.resPartial . NEL.head $ ms' - in errorMessage'' (fmap (getModuleSourceSpan . CST.resPartial) ms') $ DuplicateModule mn - - -- Find all groups of duplicate values in a list based on a projection. - findDuplicates :: Ord b => (a -> b) -> [a] -> Maybe [NEL.NonEmpty a] - findDuplicates f xs = - case filter ((> 1) . length) . NEL.groupBy ((==) `on` f) . sortOn f $ xs of - [] -> Nothing - xss -> Just xss - - -- Sort a list so its elements appear in the same order as in another list. - inOrderOf :: (Ord a) => [a] -> [a] -> [a] - inOrderOf xs ys = let s = S.fromList xs in filter (`S.member` s) ys - - buildModule :: QSem -> BuildPlan -> ModuleName -> Int -> FilePath -> [CST.ParserWarning] -> Either (NEL.NonEmpty CST.ParserError) Module -> [ModuleName] -> m () - buildModule lock buildPlan moduleName cnt fp pwarnings mres deps = do - result <- flip catchError (return . BuildJobFailed) $ do - let pwarnings' = CST.toMultipleWarnings fp pwarnings - tell pwarnings' - m <- CST.unwrapParserError fp mres - -- We need to wait for dependencies to be built, before checking if the current - -- module should be rebuilt, so the first thing to do is to wait on the - -- MVars for the module's dependencies. - mexterns <- fmap unzip . sequence <$> traverse (getResult buildPlan) deps - - case mexterns of - Just (_, depsDiffExterns) -> do - let externs = fst <$> depsDiffExterns - let prevResult = BuildPlan.getPrevResult buildPlan moduleName - let depsDiffs = traverse snd depsDiffExterns - let maySkipBuild moduleIndex - -- We may skip built only for up-to-date modules. - | Just (status, exts) <- prevResult - , isUpToDate status - -- Check if no dep's externs have changed. If any of the diffs - -- is Nothing means we can not check and need to rebuild. - , Just False <- checkDiffs m <$> depsDiffs = do - -- We should update modification times to mark existing - -- compilation results as actual. If it fails to update timestamp - -- on any of exiting codegen targets, it will run the build process. - updated <- updateOutputTimestamp moduleName - if updated then do - progress $ SkippingModule moduleName moduleIndex - pure $ Just (exts, MultipleErrors [], Just (emptyDiff moduleName)) - else - pure Nothing - | otherwise = pure Nothing - - -- We need to ensure that all dependencies have been included in Env. - C.modifyMVar_ (bpEnv buildPlan) $ \env -> do - let - go :: Env -> ModuleName -> m Env - go e dep = case lookup dep (zip deps externs) of - Just exts - | not (M.member dep e) -> externsEnv e exts - _ -> return e - foldM go env deps - env <- C.readMVar (bpEnv buildPlan) - idx <- C.takeMVar (bpIndex buildPlan) - C.putMVar (bpIndex buildPlan) (idx + 1) - - (exts, warnings, diff) <- do - let doBuild = do - -- Bracket all of the per-module work behind the semaphore, including - -- forcing the result. This is done to limit concurrency and keep - -- memory usage down; see comments above. - (exts, warnings) <- bracket_ (C.waitQSem lock) (C.signalQSem lock) $ do - -- Eventlog markers for profiling; see debug/eventlog.js - liftBase $ traceMarkerIO $ T.unpack (runModuleName moduleName) <> " start" - -- Force the externs and warnings to avoid retaining excess module - -- data after the module is finished compiling. - extsAndWarnings <- evaluate . force <=< listen $ do - rebuildModuleWithIndex ma env externs m (Just (idx, cnt)) - liftBase $ traceMarkerIO $ T.unpack (runModuleName moduleName) <> " end" - return extsAndWarnings - let diff = diffExterns exts <$> (snd <$> prevResult) <*> depsDiffs - pure (exts, warnings, diff) - maySkipBuild (Just (idx, cnt)) >>= maybe doBuild pure - return $ BuildJobSucceeded (pwarnings' <> warnings) exts diff - - -- If we got Nothing for deps externs, that means one of the deps failed - -- to compile. Though if we have a previous built result we will keep to - -- avoid potentially unnecessary recompilation next time. - Nothing -> return $ - case BuildPlan.getPrevResult buildPlan moduleName of - Just (_, exts) -> - BuildJobSucceeded (MultipleErrors []) exts (Just (emptyDiff moduleName)) - Nothing -> - BuildJobSkipped - - BuildPlan.markComplete buildPlan moduleName result - --- | Infer the module name for a module by looking for the same filename with --- a .js extension. -inferForeignModules - :: forall m - . MonadIO m - => M.Map ModuleName (Either RebuildPolicy FilePath) - -> m (M.Map ModuleName FilePath) -inferForeignModules = - fmap (M.mapMaybe id) . traverse inferForeignModule - where - inferForeignModule :: Either RebuildPolicy FilePath -> m (Maybe FilePath) - inferForeignModule (Left _) = return Nothing - inferForeignModule (Right path) = do - let jsFile = replaceExtension path "js" - exists <- liftIO $ doesFileExist jsFile - if exists - then return (Just jsFile) - else return Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/Make/Actions.hs b/claude-help/original-compiler/src/Language/PureScript/Make/Actions.hs deleted file mode 100644 index a4b8ea22..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make/Actions.hs +++ /dev/null @@ -1,499 +0,0 @@ -module Language.PureScript.Make.Actions - ( MakeActions(..) - , RebuildPolicy(..) - , ProgressMessage(..) - , renderProgressMessage - , buildMakeActions - , checkForeignDecls - , cacheDbFile - , readCacheDb' - , writeCacheDb' - , ffiCodegen' - ) where - -import Prelude - -import Control.Monad (guard, unless, when) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.IO.Class (MonadIO(..)) -import Control.Monad.Reader (asks) -import Control.Monad.Supply (SupplyT) -import Control.Monad.Trans.Class (MonadTrans(..)) -import Control.Monad.Writer.Class (MonadWriter(..)) -import Data.Aeson (Value(String), (.=), object) -import Data.Bifunctor (bimap, first) -import Data.Either (partitionEithers) -import Data.Foldable (for_) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M -import Data.Maybe (fromMaybe, maybeToList) -import Data.Set qualified as S -import Data.Text qualified as T -import Data.Text.IO qualified as TIO -import Data.Text.Encoding qualified as TE -import Data.Time.Clock (UTCTime) -import Data.Version (showVersion) -import Language.JavaScript.Parser qualified as JS -import Language.PureScript.AST (SourcePos(..), Module) -import Language.PureScript.Bundle qualified as Bundle -import Language.PureScript.CodeGen.JS qualified as J -import Language.PureScript.CodeGen.JS.Printer (prettyPrintJS, prettyPrintJSWithSourceMaps) -import Language.PureScript.CoreFn qualified as CF -import Language.PureScript.CoreFn.ToJSON qualified as CFJ -import Language.PureScript.Crash (internalError) -import Language.PureScript.CST qualified as CST -import Language.PureScript.Docs.Prim qualified as Docs.Prim -import Language.PureScript.Docs.Types qualified as Docs -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage, errorMessage') -import Language.PureScript.Externs (ExternsFile, externsFileName) -import Language.PureScript.Make.Monad (Make, copyFile, getCurrentTime, getTimestamp, getTimestampMaybe, hashFile, makeIO, readExternsFile, readJSONFile, readTextFile, setTimestamp, writeCborFile, writeJSONFile, writeTextFile) -import Language.PureScript.Make.Cache (CacheDb, ContentHash, cacheDbIsCurrentVersion, fromCacheDbVersioned, normaliseForCache, toCacheDbVersioned) -import Language.PureScript.Names (Ident(..), ModuleName, runModuleName) -import Language.PureScript.Options (CodegenTarget(..), Options(..)) -import Language.PureScript.Pretty.Common (SMap(..)) -import Paths_purescript qualified as Paths -import SourceMap (generate) -import SourceMap.Types (Mapping(..), Pos(..), SourceMapping(..)) -import System.Directory (getCurrentDirectory) -import System.FilePath ((), makeRelative, splitPath, normalise, splitDirectories) -import System.FilePath.Posix qualified as Posix -import System.IO (stderr) -import Language.PureScript.Make.IdeCache ( sqliteExtern, sqliteInit) - --- | Determines when to rebuild a module -data RebuildPolicy - -- | Never rebuild this module - = RebuildNever - -- | Always rebuild this module - | RebuildAlways - deriving (Show, Eq, Ord) - --- | Progress messages from the make process -data ProgressMessage - = CompilingModule ModuleName (Maybe (Int, Int)) - -- ^ Compilation started for the specified module - | SkippingModule ModuleName (Maybe (Int, Int)) - deriving (Show, Eq, Ord) - --- | Render a progress message -renderProgressMessage :: T.Text -> ProgressMessage -> T.Text -renderProgressMessage infx msg = case msg of - CompilingModule mn mi -> - T.concat - [ renderProgressIndex mi - , "Compiling " - , infx - , runModuleName mn - ] - SkippingModule mn mi -> - T.concat - [renderProgressIndex mi - , "Skipping " - , infx - , runModuleName mn - ] - where - renderProgressIndex :: Maybe (Int, Int) -> T.Text - renderProgressIndex = maybe "" $ \(start, end) -> - let start' = T.pack (show start) - end' = T.pack (show end) - preSpace = T.replicate (T.length end' - T.length start') " " - in "[" <> preSpace <> start' <> " of " <> end' <> "] " - --- | Actions that require implementations when running in "make" mode. --- --- This type exists to make two things abstract: --- --- * The particular backend being used (JavaScript, C++11, etc.) --- --- * The details of how files are read/written etc. -data MakeActions m = MakeActions - { getInputTimestampsAndHashes :: ModuleName -> m (Either RebuildPolicy (M.Map FilePath (UTCTime, m ContentHash))) - -- ^ Get the timestamps and content hashes for the input files for a module. - -- The content hash is returned as a monadic action so that the file does not - -- have to be read if it's not necessary. - , getOutputTimestamp :: ModuleName -> m (Maybe UTCTime) - -- ^ Get the time this module was last compiled, provided that all of the - -- requested codegen targets were also produced then. The defaultMakeActions - -- implementation uses the modification time of the externs file, because the - -- externs file is written first and we always write one. If there is no - -- externs file, or if any of the requested codegen targets were not produced - -- the last time this module was compiled, this function must return Nothing; - -- this indicates that the module will have to be recompiled. - , updateOutputTimestamp :: ModuleName -> m Bool - -- ^ Updates the modification time of existing output files to mark them as - -- actual. - , readExterns :: ModuleName -> m (FilePath, Maybe ExternsFile) - -- ^ Read the externs file for a module as a string and also return the actual - -- path for the file. - , codegen :: Module -> CF.Module CF.Ann -> Docs.Module -> ExternsFile -> SupplyT m () - -- ^ Run the code generator for the module and write any required output files. - , ffiCodegen :: CF.Module CF.Ann -> m () - -- ^ Check ffi and print it in the output directory. - , progress :: ProgressMessage -> m () - -- ^ Respond to a progress update. - , readCacheDb :: m CacheDb - -- ^ Read the cache database (which contains timestamps and hashes for input - -- files) from some external source, e.g. a file on disk. - , writeCacheDb :: CacheDb -> m () - -- ^ Write the given cache database to some external source (e.g. a file on - -- disk). - , writePackageJson :: m () - -- ^ Write to the output directory the package.json file allowing Node.js to - -- load .js files as ES modules. - , outputPrimDocs :: m () - -- ^ If generating docs, output the documentation for the Prim modules - } - --- | Given the output directory, determines the location for the --- CacheDb file -cacheDbFile :: FilePath -> FilePath -cacheDbFile = ( "cache-db.json") - -readCacheDb' - :: (MonadIO m, MonadError MultipleErrors m) - => FilePath - -- ^ The path to the output directory - -> m CacheDb -readCacheDb' outputDir = do - mdb <- readJSONFile (cacheDbFile outputDir) - pure $ fromMaybe mempty $ do - db <- mdb - guard $ cacheDbIsCurrentVersion db - pure $ fromCacheDbVersioned db - -writeCacheDb' - :: (MonadIO m, MonadError MultipleErrors m) - => FilePath - -- ^ The path to the output directory - -> CacheDb - -- ^ The CacheDb to be written - -> m () -writeCacheDb' = (. toCacheDbVersioned) . writeJSONFile . cacheDbFile - -writePackageJson' - :: (MonadIO m, MonadError MultipleErrors m) - => FilePath - -- ^ The path to the output directory - -> m () -writePackageJson' outputDir = writeJSONFile (outputDir "package.json") $ object - [ "type" .= String "module" - ] - --- | A set of make actions that read and write modules from the given directory. -buildMakeActions - :: FilePath - -- ^ the output directory - -> M.Map ModuleName (Either RebuildPolicy FilePath) - -- ^ a map between module names and paths to the file containing the PureScript module - -> M.Map ModuleName FilePath - -- ^ a map between module name and the file containing the foreign javascript for the module - -> Bool - -- ^ Generate a prefix comment? - -> MakeActions Make -buildMakeActions outputDir filePathMap foreigns usePrefix = - MakeActions - getInputTimestampsAndHashes - getOutputTimestamp - updateOutputTimestamp - readExterns - codegen - ffiCodegen - progress - readCacheDb - writeCacheDb - writePackageJson - outputPrimDocs - where - - getInputTimestampsAndHashes - :: ModuleName - -> Make (Either RebuildPolicy (M.Map FilePath (UTCTime, Make ContentHash))) - getInputTimestampsAndHashes mn = do - let path = fromMaybe (internalError "Module has no filename in 'make'") $ M.lookup mn filePathMap - case path of - Left policy -> - return (Left policy) - Right filePath -> do - cwd <- makeIO "Getting the current directory" getCurrentDirectory - let inputPaths = map (normaliseForCache cwd) (filePath : maybeToList (M.lookup mn foreigns)) - getInfo fp = do - ts <- getTimestamp fp - return (ts, hashFile fp) - pathsWithInfo <- traverse (\fp -> (fp,) <$> getInfo fp) inputPaths - return $ Right $ M.fromList pathsWithInfo - - outputFilename :: ModuleName -> String -> FilePath - outputFilename mn fn = - let filePath = T.unpack (runModuleName mn) - in outputDir filePath fn - - targetFilename :: ModuleName -> CodegenTarget -> FilePath - targetFilename mn = \case - JS -> outputFilename mn "index.js" - JSSourceMap -> outputFilename mn "index.js.map" - CoreFn -> outputFilename mn "corefn.json" - Docs -> outputFilename mn "docs.json" - - getOutputTimestamp :: ModuleName -> Make (Maybe UTCTime) - getOutputTimestamp mn = do - codegenTargets <- asks optionsCodegenTargets - mExternsTimestamp <- getTimestampMaybe (outputFilename mn externsFileName) - case mExternsTimestamp of - Nothing -> - -- If there is no externs file, we will need to compile the module in - -- order to produce one. - pure Nothing - Just externsTimestamp -> - case NEL.nonEmpty (fmap (targetFilename mn) (S.toList codegenTargets)) of - Nothing -> - -- If the externs file exists and no other codegen targets have - -- been requested, then we can consider the module up-to-date - pure (Just externsTimestamp) - Just outputPaths -> do - -- If any of the other output paths are nonexistent or older than - -- the externs file, then they should be considered outdated, and - -- so the module will need rebuilding. - mmodTimes <- traverse getTimestampMaybe outputPaths - pure $ case sequence mmodTimes of - Nothing -> - Nothing - Just modTimes -> - if externsTimestamp <= minimum modTimes - then Just externsTimestamp - else Nothing - - updateOutputTimestamp :: ModuleName -> Make Bool - updateOutputTimestamp mn = do - curTime <- getCurrentTime - ok <- setTimestamp (outputFilename mn externsFileName) curTime - -- Then update timestamps of all actual codegen targets. - codegenTargets <- asks optionsCodegenTargets - let outputPaths = fmap (targetFilename mn) (S.toList codegenTargets) - results <- traverse (flip setTimestamp curTime) outputPaths - -- If something goes wrong (any of targets doesn't exit, a file system - -- error), return False. - pure $ and (ok : results) - - readExterns :: ModuleName -> Make (FilePath, Maybe ExternsFile) - readExterns mn = do - let path = outputDir T.unpack (runModuleName mn) externsFileName - (path, ) <$> readExternsFile path - - outputPrimDocs :: Make () - outputPrimDocs = do - codegenTargets <- asks optionsCodegenTargets - when (S.member Docs codegenTargets) $ for_ Docs.Prim.primModules $ \docsMod@Docs.Module{..} -> - writeJSONFile (outputFilename modName "docs.json") docsMod - - codegen :: Module -> CF.Module CF.Ann -> Docs.Module -> ExternsFile -> SupplyT Make () - codegen ast m docs exts = do - let mn = CF.moduleName m - lift $ writeCborFile (outputFilename mn externsFileName) exts - lift $ sqliteInit outputDir - lift $ sqliteExtern outputDir ast exts - codegenTargets <- lift $ asks optionsCodegenTargets - when (S.member CoreFn codegenTargets) $ do - let coreFnFile = targetFilename mn CoreFn - json = CFJ.moduleToJSON Paths.version m - lift $ writeJSONFile coreFnFile json - when (S.member JS codegenTargets) $ do - foreignInclude <- case mn `M.lookup` foreigns of - Just _ - | not $ requiresForeign m -> do - return Nothing - | otherwise -> do - return $ Just "./foreign.js" - Nothing | requiresForeign m -> throwError . errorMessage' (CF.moduleSourceSpan m) $ MissingFFIModule mn - | otherwise -> return Nothing - rawJs <- J.moduleToJs m foreignInclude - dir <- lift $ makeIO "get the current directory" getCurrentDirectory - let sourceMaps = S.member JSSourceMap codegenTargets - (pjs, mappings) = if sourceMaps then prettyPrintJSWithSourceMaps rawJs else (prettyPrintJS rawJs, []) - jsFile = targetFilename mn JS - mapFile = targetFilename mn JSSourceMap - prefix = ["Generated by purs version " <> T.pack (showVersion Paths.version) | usePrefix] - js = T.unlines $ map ("// " <>) prefix ++ [pjs] - mapRef = if sourceMaps then "//# sourceMappingURL=index.js.map\n" else "" - lift $ do - writeTextFile jsFile (TE.encodeUtf8 $ js <> mapRef) - when sourceMaps $ genSourceMap dir mapFile (length prefix) mappings - when (S.member Docs codegenTargets) $ do - lift $ writeJSONFile (outputFilename mn "docs.json") docs - - ffiCodegen :: CF.Module CF.Ann -> Make () - ffiCodegen m = do - codegenTargets <- asks optionsCodegenTargets - ffiCodegen' foreigns codegenTargets (Just outputFilename) m - - genSourceMap :: String -> String -> Int -> [SMap] -> Make () - genSourceMap dir mapFile extraLines mappings = do - let pathToDir = iterate (".." Posix.) ".." !! length (splitPath $ normalise outputDir) - sourceFile = case mappings of - (SMap file _ _ : _) -> Just $ pathToDir Posix. normalizeSMPath (makeRelative dir (T.unpack file)) - _ -> Nothing - let rawMapping = SourceMapping { smFile = "index.js", smSourceRoot = Nothing, smMappings = - map (\(SMap _ orig gen) -> Mapping { - mapOriginal = Just $ convertPos $ add 0 (-1) orig - , mapSourceFile = sourceFile - , mapGenerated = convertPos $ add (extraLines + 1) 0 gen - , mapName = Nothing - }) mappings - } - let mapping = generate rawMapping - writeJSONFile mapFile mapping - where - add :: Int -> Int -> SourcePos -> SourcePos - add n m (SourcePos n' m') = SourcePos (n + n') (m + m') - - convertPos :: SourcePos -> Pos - convertPos SourcePos { sourcePosLine = l, sourcePosColumn = c } = - Pos { posLine = fromIntegral l, posColumn = fromIntegral c } - - normalizeSMPath :: FilePath -> FilePath - normalizeSMPath = Posix.joinPath . splitDirectories - - requiresForeign :: CF.Module a -> Bool - requiresForeign = not . null . CF.moduleForeign - - progress :: ProgressMessage -> Make () - progress = liftIO . TIO.hPutStr stderr . (<> "\n") . renderProgressMessage "" - - readCacheDb :: Make CacheDb - readCacheDb = readCacheDb' outputDir - - writeCacheDb :: CacheDb -> Make () - writeCacheDb = writeCacheDb' outputDir - - writePackageJson :: Make () - writePackageJson = writePackageJson' outputDir - -data ForeignModuleType = ESModule | CJSModule deriving (Show) - --- | Check that the declarations in a given PureScript module match with those --- in its corresponding foreign module. -checkForeignDecls :: CF.Module ann -> FilePath -> Make (Either MultipleErrors (ForeignModuleType, S.Set Ident)) --- checkForeignDecls :: CF.Module ann -> FilePath -> Make (ForeignModuleType, S.Set Ident -checkForeignDecls m path = do - jsStr <- T.unpack <$> readTextFile path - - let - parseResult :: Either MultipleErrors JS.JSAST - parseResult = first (errorParsingModule . Bundle.UnableToParseModule) $ JS.parseModule jsStr path - traverse checkFFI parseResult - - where - mname = CF.moduleName m - modSS = CF.moduleSourceSpan m - - checkFFI :: JS.JSAST -> Make (ForeignModuleType, S.Set Ident) - checkFFI js = do - (foreignModuleType, foreignIdentsStrs) <- - case (,) <$> getForeignModuleExports js <*> getForeignModuleImports js of - Left reason -> throwError $ errorParsingModule reason - Right (Bundle.ForeignModuleExports{..}, Bundle.ForeignModuleImports{..}) - | not (null cjsExports && null cjsImports) - , null esExports - , null esImports -> do - let deprecatedFFI = filter (elem '\'') cjsExports - unless (null deprecatedFFI) $ - errorDeprecatedForeignPrimes deprecatedFFI - - pure (CJSModule, cjsExports) - | otherwise -> do - unless (null cjsImports) $ - errorUnsupportedFFICommonJSImports cjsImports - - unless (null cjsExports) $ - errorUnsupportedFFICommonJSExports cjsExports - - pure (ESModule, esExports) - - foreignIdents <- either - errorInvalidForeignIdentifiers - (pure . S.fromList) - (parseIdents foreignIdentsStrs) - let importedIdents = S.fromList (CF.moduleForeign m) - - let unusedFFI = foreignIdents S.\\ importedIdents - unless (null unusedFFI) $ - tell . errorMessage' modSS . UnusedFFIImplementations mname $ - S.toList unusedFFI - - let missingFFI = importedIdents S.\\ foreignIdents - unless (null missingFFI) $ - throwError . errorMessage' modSS . MissingFFIImplementations mname $ - S.toList missingFFI - pure (foreignModuleType, foreignIdents) - - errorParsingModule :: Bundle.ErrorMessage -> MultipleErrors - errorParsingModule = errorMessage' modSS . ErrorParsingFFIModule path . Just - - getForeignModuleExports :: JS.JSAST -> Either Bundle.ErrorMessage Bundle.ForeignModuleExports - getForeignModuleExports = Bundle.getExportedIdentifiers (T.unpack (runModuleName mname)) - - getForeignModuleImports :: JS.JSAST -> Either Bundle.ErrorMessage Bundle.ForeignModuleImports - getForeignModuleImports = Bundle.getImportedModules (T.unpack (runModuleName mname)) - - errorInvalidForeignIdentifiers :: [String] -> Make a - errorInvalidForeignIdentifiers = - throwError . mconcat . map (errorMessage . InvalidFFIIdentifier mname . T.pack) - - errorDeprecatedForeignPrimes :: [String] -> Make a - errorDeprecatedForeignPrimes = - throwError . mconcat . map (errorMessage' modSS . DeprecatedFFIPrime mname . T.pack) - - errorUnsupportedFFICommonJSExports :: [String] -> Make a - errorUnsupportedFFICommonJSExports = - throwError . errorMessage' modSS . UnsupportedFFICommonJSExports mname . map T.pack - - errorUnsupportedFFICommonJSImports :: [String] -> Make a - errorUnsupportedFFICommonJSImports = - throwError . errorMessage' modSS . UnsupportedFFICommonJSImports mname . map T.pack - - parseIdents :: [String] -> Either [String] [Ident] - parseIdents strs = - case partitionEithers (map parseIdent strs) of - ([], idents) -> - Right idents - (errs, _) -> - Left errs - - -- We ignore the error message here, just being told it's an invalid - -- identifier should be enough. - parseIdent :: String -> Either String Ident - parseIdent str = - bimap (const str) (Ident . CST.getIdent . CST.nameValue . snd) - . CST.runTokenParser CST.parseIdent - . CST.lex - $ T.pack str - --- | FFI check and codegen action. --- If path maker is supplied copies foreign module to the output. -ffiCodegen' - :: M.Map ModuleName FilePath - -> S.Set CodegenTarget - -> Maybe (ModuleName -> String -> FilePath) - -> CF.Module CF.Ann - -> Make () -ffiCodegen' foreigns codegenTargets makeOutputPath m = do - when (S.member JS codegenTargets) $ do - let mn = CF.moduleName m - case mn `M.lookup` foreigns of - Just path - | not $ requiresForeign m -> - tell $ errorMessage' (CF.moduleSourceSpan m) $ UnnecessaryFFIModule mn path - | otherwise -> do - checkResult <- checkForeignDecls m path - case checkResult of - Left _ -> copyForeign path mn - Right (ESModule, _) -> copyForeign path mn - Right (CJSModule, _) -> do - throwError $ errorMessage' (CF.moduleSourceSpan m) $ DeprecatedFFICommonJSModule mn path - Nothing | requiresForeign m -> throwError . errorMessage' (CF.moduleSourceSpan m) $ MissingFFIModule mn - | otherwise -> return () - where - requiresForeign = not . null . CF.moduleForeign - - copyForeign path mn = - for_ makeOutputPath (\outputFilename -> copyFile path (outputFilename mn "foreign.js")) diff --git a/claude-help/original-compiler/src/Language/PureScript/Make/BuildPlan.hs b/claude-help/original-compiler/src/Language/PureScript/Make/BuildPlan.hs deleted file mode 100644 index 21a221f5..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make/BuildPlan.hs +++ /dev/null @@ -1,321 +0,0 @@ -module Language.PureScript.Make.BuildPlan - ( BuildPlan(bpEnv, bpIndex) - , BuildJobResult(..) - , Options(..) - , isUpToDate - , construct - , getResult - , getPrevResult - , collectResults - , markComplete - , needsRebuild - ) where - -import Prelude - -import Control.Concurrent.Async.Lifted qualified as A -import Control.Concurrent.Lifted qualified as C -import Control.Monad.Base (liftBase) -import Control.Monad (foldM, guard) -import Control.Monad.Trans.Control (MonadBaseControl(..)) -import Data.Foldable (foldl') -import Data.Map qualified as M -import Data.Maybe (fromMaybe, isNothing, catMaybes) -import Data.Set qualified as S -import Data.Text qualified as T -import Data.Time.Clock (UTCTime) -import Language.PureScript.AST (Module, getModuleName) -import Language.PureScript.Crash (internalError) -import Language.PureScript.CST qualified as CST -import Language.PureScript.Errors (MultipleErrors(..)) -import Language.PureScript.Externs (ExternsFile) -import Language.PureScript.Make.Actions as Actions -import Language.PureScript.Make.Cache (CacheDb, CacheInfo, checkChanged) -import Language.PureScript.Make.ExternsDiff (ExternsDiff, emptyDiff) -import Language.PureScript.Names (ModuleName) -import Language.PureScript.Sugar.Names.Env (Env, primEnv) -import System.Directory (getCurrentDirectory) - --- This status tells if a module's exiting build artifacts are up to date with a --- current module's content. It would be safe to re-use them, but only if --- changes in its dependencies do require the module's rebuild. -newtype UpToDateStatus = UpToDateStatus Bool - -isUpToDate :: UpToDateStatus -> Bool -isUpToDate (UpToDateStatus b) = b - -data Prebuilt = Prebuilt - { pbExternsFile :: ExternsFile - } - --- | The BuildPlan tracks information about our build progress, and holds all --- prebuilt modules for incremental builds. -data BuildPlan = BuildPlan - { bpPrebuilt :: M.Map ModuleName Prebuilt - -- ^ Valid prebuilt results for modules, that are needed for rebuild, but - -- their rebuild is not required. - , bpPreviousBuilt :: M.Map ModuleName (UpToDateStatus, Prebuilt) - -- ^ Previously built results for modules that are potentially required to be - -- rebuilt. We will always rebuild not up to date modules. But we will only - -- rebuild up to date modules, if their deps' externs have effectively - -- changed. Previously built result is needed to compare previous and newly - -- built externs to know what have changed. - , bpBuildJobs :: M.Map ModuleName BuildJob - , bpEnv :: C.MVar Env - , bpIndex :: C.MVar Int - } - - -newtype BuildJob = BuildJob - { bjResult :: C.MVar BuildJobResult - -- ^ Note: an empty MVar indicates that the build job has not yet finished. - } - -data BuildJobResult - = BuildJobSucceeded !MultipleErrors !ExternsFile (Maybe ExternsDiff) - -- ^ Succeeded, with warnings and externs, also holds externs diff with - -- previous build result if any (lazily evaluated). - -- - | BuildJobFailed !MultipleErrors - -- ^ Failed, with errors. - - | BuildJobSkipped - -- ^ The build job was not run, because an upstream build job failed. - -type SuccessResult = (MultipleErrors, (ExternsFile, Maybe ExternsDiff)) - -buildJobSuccess :: BuildJobResult -> Maybe SuccessResult -buildJobSuccess (BuildJobSucceeded warnings externs diff) = Just (warnings, (externs, diff)) -buildJobSuccess _ = Nothing - --- | Information obtained about a particular module while constructing a build --- plan; used to decide whether a module needs rebuilding. -data RebuildStatus = RebuildStatus - { rsModuleName :: ModuleName - , rsRebuildNever :: Bool - , rsNewCacheInfo :: Maybe CacheInfo - -- ^ New cache info for this module which should be stored for subsequent - -- incremental builds. A value of Nothing indicates that cache info for - -- this module should not be stored in the build cache, because it is being - -- rebuilt according to a RebuildPolicy instead. - , rsPrebuilt :: Maybe UTCTime - -- ^ Prebuilt timestamp (compilation time) for this module. - , rsUpToDate :: Bool - -- ^ Whether or not module (timestamp or content) changed since previous - -- compilation (checked against provided cache-db info). - } - --- | Construct common error message indicating a bug in the internal logic -barrierError :: T.Text -> a -barrierError infx = internalError $ "make: " <> T.unpack infx <> " no barrier" - --- | Called when we finished compiling a module and want to report back the --- compilation result, as well as any potential errors that were thrown. -markComplete - :: (MonadBaseControl IO m) - => BuildPlan - -> ModuleName - -> BuildJobResult - -> m () -markComplete buildPlan moduleName result = do - let BuildJob rVar = - fromMaybe (barrierError "markComplete") $ M.lookup moduleName (bpBuildJobs buildPlan) - C.putMVar rVar result - --- | Whether or not the module with the given ModuleName needs to be rebuilt -needsRebuild :: BuildPlan -> ModuleName -> Bool -needsRebuild bp moduleName = M.member moduleName (bpBuildJobs bp) - --- | Collects results for all prebuilt as well as rebuilt modules. This will --- block until all build jobs are finished. Prebuilt modules always return no --- warnings. -collectResults - :: (MonadBaseControl IO m) - => BuildPlan - -> m (M.Map ModuleName BuildJobResult) -collectResults buildPlan = do - let mapExts exts = BuildJobSucceeded (MultipleErrors []) exts Nothing - let prebuiltResults = - M.map (mapExts . pbExternsFile) (bpPrebuilt buildPlan) - barrierResults <- traverse (C.readMVar . bjResult) $ bpBuildJobs buildPlan - pure (M.union prebuiltResults barrierResults) - --- | Gets the the build result for a given module name independent of whether it --- was rebuilt or prebuilt. Prebuilt modules always return no warnings. -getResult - :: (MonadBaseControl IO m) - => BuildPlan - -> ModuleName - -> m (Maybe SuccessResult) -getResult buildPlan moduleName = - case M.lookup moduleName (bpBuildJobs buildPlan) of - Just bj -> - buildJobSuccess <$> C.readMVar (bjResult bj) - Nothing -> do - let exts = pbExternsFile - $ fromMaybe (barrierError "getResult") - $ M.lookup moduleName (bpPrebuilt buildPlan) - pure (Just (MultipleErrors [], (exts, Just $ emptyDiff moduleName ))) - --- | Gets preloaded previous built result for modules that are going to be built. This --- will be used to skip compilation if dep's externs have not changed. -getPrevResult :: BuildPlan -> ModuleName -> Maybe (UpToDateStatus, ExternsFile) -getPrevResult buildPlan moduleName = - fmap pbExternsFile <$> M.lookup moduleName (bpPreviousBuilt buildPlan) - -data Options = Options - { optPreloadAllExterns :: Bool - } - --- | Constructs a BuildPlan for the given module graph. --- --- The given MakeActions are used to collect various timestamps in order to --- determine whether a module needs rebuilding. -construct - :: forall m. MonadBaseControl IO m - => Options - -> MakeActions m - -> CacheDb - -> ([CST.PartialResult Module], [(ModuleName, [ModuleName])]) - -> m (BuildPlan, CacheDb) -construct Options{..} MakeActions{..} cacheDb (sorted, graph) = do - let sortedModuleNames = map (getModuleName . CST.resPartial) sorted - rebuildStatuses <- A.forConcurrently sortedModuleNames getRebuildStatus - - -- Split modules into those that have to be rebuilt and those that have a valid - -- prebuilt input. The Bool value in rebuildMap means if we may skip the - -- compilation (if externs of dependencies have not changed). If it is False we - -- should re-compile the module due to the following: the module's source have - -- changed or some of dependencies were compiled later than the module. - let (rebuildMap, prebuiltMap) = splitModules rebuildStatuses - - let toBeRebuilt = M.keys rebuildMap - - -- Set of all dependencies of modules to be rebuilt. - let allBuildDeps = S.unions (S.fromList . moduleDeps <$> toBeRebuilt) - let inBuildDeps = flip S.member allBuildDeps - - -- We only need prebuilt results for deps of the modules to be build. - let toLoadPrebuilt = - if optPreloadAllExterns - then prebuiltMap - else M.filterWithKey (const . inBuildDeps) prebuiltMap - - -- We will need previously built results for modules to be built - -- to skip rebuilding if deps have not changed. - let toLoadPrev = - M.mapMaybeWithKey - ( \mn prev -> do - -- We load previous build result for all up-to-date modules, and - -- also for changed modules that have dependants. - status <- fst <$> prev - guard (isUpToDate status || inBuildDeps mn) - prev - ) - rebuildMap - - (prebuiltLoad, prevLoad) <- - A.concurrently - (A.mapConcurrently id $ M.mapWithKey loadPrebuilt toLoadPrebuilt) - (A.mapConcurrently id $ M.mapWithKey - (\mn (up, ts) -> fmap (up,) <$> loadPrebuilt mn ts) toLoadPrev) - - let prebuilt = M.mapMaybe id prebuiltLoad - let previous = M.mapMaybe id prevLoad - - -- If for some reason (wrong version, files corruption, etc) prebuilt - -- externs loading fails, those modules should be rebuilt too. - let failedLoads = M.keys $ M.filter isNothing prebuiltLoad - buildJobs <- foldM makeBuildJob M.empty (toBeRebuilt <> failedLoads) - - env <- C.newMVar primEnv - idx <- C.newMVar 1 - pure - ( BuildPlan prebuilt previous buildJobs env idx - , let - update = flip $ \s -> - M.alter (const (rsNewCacheInfo s)) (rsModuleName s) - in - foldl' update cacheDb rebuildStatuses - ) - where - -- Timestamp here is just to ensure that we will only try to load modules - -- that have previous built results available. - loadPrebuilt :: ModuleName -> UTCTime -> m (Maybe Prebuilt) - loadPrebuilt = const . fmap (fmap Prebuilt . snd) . readExterns - - makeBuildJob prev moduleName = do - buildJob <- BuildJob <$> C.newEmptyMVar - pure (M.insert moduleName buildJob prev) - - getRebuildStatus :: ModuleName -> m RebuildStatus - getRebuildStatus moduleName = do - inputInfo <- getInputTimestampsAndHashes moduleName - case inputInfo of - Left RebuildNever -> do - timestamp <- getOutputTimestamp moduleName - pure (RebuildStatus - { rsModuleName = moduleName - , rsRebuildNever = True - , rsPrebuilt = timestamp - , rsUpToDate = True - , rsNewCacheInfo = Nothing - }) - Left RebuildAlways -> do - pure (RebuildStatus - { rsModuleName = moduleName - , rsRebuildNever = False - , rsPrebuilt = Nothing - , rsUpToDate = False - , rsNewCacheInfo = Nothing - }) - Right cacheInfo -> do - cwd <- liftBase getCurrentDirectory - (newCacheInfo, upToDate) <- checkChanged cacheDb moduleName cwd cacheInfo - timestamp <- getOutputTimestamp moduleName - pure (RebuildStatus - { rsModuleName = moduleName - , rsRebuildNever = False - , rsPrebuilt = timestamp - , rsUpToDate = upToDate - , rsNewCacheInfo = Just newCacheInfo - }) - - moduleDeps = fromMaybe graphError . flip lookup graph - where - graphError = internalError "make: module not found in dependency graph." - - splitModules :: [RebuildStatus] -> (M.Map ModuleName (Maybe (UpToDateStatus, UTCTime)), M.Map ModuleName UTCTime) - splitModules = foldl' collectByStatus (M.empty, M.empty) - - collectByStatus (build, prebuilt) (RebuildStatus mn rebuildNever _ mbPb upToDate) - -- To build if no prebuilt result exits. - | Nothing <- mbPb = (M.insert mn Nothing build, prebuilt) - -- To build if not up to date. - | Just pb <- mbPb, not upToDate = toRebuild (False, pb) - -- To prebuilt because of policy. - | Just pb <- mbPb, rebuildNever = toPrebuilt pb - -- In other case analyze compilation times of dependencies. - | Just pb <- mbPb = do - let deps = moduleDeps mn - let modTimes = map (flip M.lookup prebuilt) deps - - case maximumMaybe (catMaybes modTimes) of - -- Check if any of deps where build later. This means we should - -- recompile even if the module's source is up-to-date. This may - -- happen due to some partial builds or ide compilation - -- workflows involved that do not assume full project - -- compilation. We should treat those modules as NOT up to date - -- to ensure they are rebuilt. - Just depModTime | pb < depModTime -> toRebuild (False, pb) - -- If one of the deps is not in the prebuilt, though the module - -- is up to date, we should add it in the rebuild queue. - _ | any isNothing modTimes -> toRebuild (upToDate, pb) - _ -> toPrebuilt pb - where - toRebuild (up, t) = (M.insert mn (Just (UpToDateStatus up, t)) build, prebuilt) - toPrebuilt v = (build, M.insert mn v prebuilt) - -maximumMaybe :: Ord a => [a] -> Maybe a -maximumMaybe [] = Nothing -maximumMaybe xs = Just $ maximum xs diff --git a/claude-help/original-compiler/src/Language/PureScript/Make/Cache.hs b/claude-help/original-compiler/src/Language/PureScript/Make/Cache.hs deleted file mode 100644 index 4582d2fd..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make/Cache.hs +++ /dev/null @@ -1,185 +0,0 @@ -module Language.PureScript.Make.Cache - ( ContentHash - , hash - , CacheDb - , CacheInfo(..) - , checkChanged - , removeModules - , normaliseForCache - , cacheDbIsCurrentVersion - , toCacheDbVersioned - , fromCacheDbVersioned - ) where - -import Prelude - -import Control.Category ((>>>)) -import Control.Monad ((>=>)) -import Crypto.Hash (HashAlgorithm, Digest, SHA512) -import Crypto.Hash qualified as Hash -import Data.Aeson qualified as Aeson -import Data.Align (align) -import Data.ByteArray.Encoding (Base(Base16), convertToBase, convertFromBase) -import Data.ByteString qualified as BS -import Data.Map (Map) -import Data.Map qualified as Map -import Data.Maybe (fromMaybe) -import Data.Monoid (All(..)) -import Data.Set (Set) -import Data.Text (Text, pack, unpack) -import Data.Text.Encoding (encodeUtf8, decodeUtf8) -import Data.These (These(..)) -import Data.Time.Clock (UTCTime) -import Data.Traversable (for) -import System.FilePath qualified as FilePath - -import Paths_purescript as Paths - -import Language.PureScript.Names (ModuleName) -import Data.Version (showVersion) -import Data.Aeson ((.=)) -import Data.Aeson.Types ((.:)) - -digestToHex :: Digest a -> Text -digestToHex = decodeUtf8 . convertToBase Base16 - -digestFromHex :: forall a. HashAlgorithm a => Text -> Maybe (Digest a) -digestFromHex = - encodeUtf8 - >>> either (const Nothing) Just . convertFromBase Base16 - >=> (Hash.digestFromByteString :: BS.ByteString -> Maybe (Digest a)) - --- | Defines the hash algorithm we use for cache invalidation of input files. -newtype ContentHash = ContentHash - { unContentHash :: Digest SHA512 } - deriving (Show, Eq, Ord) - -instance Aeson.ToJSON ContentHash where - toJSON = Aeson.toJSON . digestToHex . unContentHash - -instance Aeson.FromJSON ContentHash where - parseJSON x = do - str <- Aeson.parseJSON x - case digestFromHex str of - Just digest -> - pure $ ContentHash digest - Nothing -> - fail "Unable to decode ContentHash" - -hash :: BS.ByteString -> ContentHash -hash = ContentHash . Hash.hash - -type CacheDb = Map ModuleName CacheInfo - -data CacheDbVersioned = CacheDbVersioned { cdbVersion :: Text, cdbModules :: CacheDb } - deriving (Eq, Ord) - -instance Aeson.FromJSON CacheDbVersioned where - parseJSON = Aeson.withObject "CacheDb" $ \v -> - CacheDbVersioned - <$> v .: "version" - <*> v .: "modules" - -instance Aeson.ToJSON CacheDbVersioned where - toJSON CacheDbVersioned{..} = - Aeson.object - [ "version" .= cdbVersion - , "modules" .= cdbModules - ] - -cacheDbIsCurrentVersion :: CacheDbVersioned -> Bool -cacheDbIsCurrentVersion ef = - unpack (cdbVersion ef) == showVersion Paths.version - -toCacheDbVersioned :: CacheDb -> CacheDbVersioned -toCacheDbVersioned = - CacheDbVersioned (pack $ showVersion Paths.version) - -fromCacheDbVersioned :: CacheDbVersioned -> CacheDb -fromCacheDbVersioned = - cdbModules - --- | A CacheInfo contains all of the information we need to store about a --- particular module in the cache database. -newtype CacheInfo = CacheInfo - { unCacheInfo :: Map FilePath (UTCTime, ContentHash) } - deriving stock (Show) - deriving newtype (Eq, Ord, Semigroup, Monoid, Aeson.FromJSON, Aeson.ToJSON) - --- | Given a module name, and a map containing the associated input files --- together with current metadata i.e. timestamps and hashes, check whether the --- input files have changed, based on comparing with the database stored in the --- monadic state. --- --- The CacheInfo in the return value should be stored in the cache for future --- builds. --- --- The Bool in the return value indicates whether it is safe to use existing --- build artifacts for this module, at least based on the timestamps and hashes --- of the module's input files. --- --- If the timestamps are the same as those in the database, assume the file is --- unchanged, and return True without checking hashes. --- --- If any of the timestamps differ from what is in the database, check the --- hashes of those files. In this case, update the database with any changed --- timestamps and hashes, and return True if and only if all of the hashes are --- unchanged. -checkChanged - :: Monad m - => CacheDb - -> ModuleName - -> FilePath - -> Map FilePath (UTCTime, m ContentHash) - -> m (CacheInfo, Bool) -checkChanged cacheDb mn basePath currentInfo = do - - let dbInfo = unCacheInfo $ fromMaybe mempty (Map.lookup mn cacheDb) - (newInfo, isUpToDate) <- - fmap mconcat $ - for (Map.toList (align dbInfo currentInfo)) $ \(normaliseForCache basePath -> fp, aligned) -> do - case aligned of - This _ -> do - -- One of the input files listed in the cache no longer exists; - -- remove that file from the cache and note that the module needs - -- rebuilding - pure (Map.empty, All False) - That (timestamp, getHash) -> do - -- The module has a new input file; add it to the cache and - -- note that the module needs rebuilding. - newHash <- getHash - pure (Map.singleton fp (timestamp, newHash), All False) - These db@(dbTimestamp, _) (newTimestamp, _) | dbTimestamp == newTimestamp -> do - -- This file exists both currently and in the cache database, - -- and the timestamp is unchanged, so we skip checking the - -- hash. - pure (Map.singleton fp db, mempty) - These (_, dbHash) (newTimestamp, getHash) -> do - -- This file exists both currently and in the cache database, - -- but the timestamp has changed, so we need to check the hash. - newHash <- getHash - pure (Map.singleton fp (newTimestamp, newHash), All (dbHash == newHash)) - - pure (CacheInfo newInfo, getAll isUpToDate) - --- | Remove any modules from the given set from the cache database; used when --- they failed to build. -removeModules :: Set ModuleName -> CacheDb -> CacheDb -removeModules = flip Map.withoutKeys - --- | 1. Any path that is beneath our current working directory will be --- stored as a normalised relative path --- 2. Any path that isn't will be stored as an absolute path -normaliseForCache :: FilePath -> FilePath -> FilePath -normaliseForCache basePath fp = - if FilePath.isRelative fp then - FilePath.normalise fp - else - let relativePath = FilePath.makeRelative basePath fp in - if FilePath.isRelative relativePath then - FilePath.normalise relativePath - else - -- If the path is still absolute after trying to make it - -- relative to the base that means it is not underneath - -- the base path - FilePath.normalise fp diff --git a/claude-help/original-compiler/src/Language/PureScript/Make/ExternsDiff.hs b/claude-help/original-compiler/src/Language/PureScript/Make/ExternsDiff.hs deleted file mode 100644 index 21ef9ab3..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make/ExternsDiff.hs +++ /dev/null @@ -1,490 +0,0 @@ -module Language.PureScript.Make.ExternsDiff - ( ExternsDiff - , emptyDiff - , diffExterns - , checkDiffs - ) where - -import Protolude hiding (check, moduleName, trace) - -import Data.Graph as G (graphFromEdges, reachable) -import Data.List qualified as L -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.AST qualified as P -import Language.PureScript.AST.Declarations.ChainId (ChainId (..)) -import Language.PureScript.Constants.Prim (primModules) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment qualified as P -import Language.PureScript.Externs qualified as P -import Language.PureScript.Names (ModuleName) -import Language.PureScript.Names qualified as P -import Language.PureScript.Types qualified as P - --- Refs structure appropriate for storing and checking externs diffs. -data Ref - = TypeClassRef (P.ProperName 'P.ClassName) - | TypeOpRef (P.OpName 'P.TypeOpName) - | TypeRef (P.ProperName 'P.TypeName) - | -- We use separate ref for a data constructor and keep here origin type as well. - ConstructorRef (P.ProperName 'P.TypeName) (P.ProperName 'P.ConstructorName) - | -- A ad-hoc ref that points to the type with a set of constructors that changed. - -- It is needed to correctly handle effects of adding/removing of ctors. - CtorsSetRef (P.ProperName 'P.TypeName) - | ValueRef P.Ident - | ValueOpRef (P.OpName 'P.ValueOpName) - | -- Instance ref points to the class and types defined in the same module. - TypeInstanceRef P.Ident (ModuleName, P.ProperName 'P.ClassName) [P.ProperName 'P.TypeName] - deriving (Show, Eq, Ord) - -data RefStatus = Removed | Updated - deriving (Show) - -type RefWithDeps = (Ref, S.Set (ModuleName, Ref)) - -type RefsWithStatus = M.Map Ref RefStatus - -type ModuleRefsMap = Map ModuleName (Set Ref) - -data ExternsDiff = ExternsDiff - {edModuleName :: ModuleName, edRefs :: Map Ref RefStatus} - deriving (Show) - --- | Empty diff means no effective difference between externs. -emptyDiff :: P.ModuleName -> ExternsDiff -emptyDiff mn = ExternsDiff mn mempty - -isRefRemoved :: RefStatus -> Bool -isRefRemoved Removed = True -isRefRemoved _ = False - --- To get changed reexported refs, we take those which were removed (not --- present in new extern's exports) or changed in dependencies. -getReExported :: P.ExternsFile -> P.ExternsFile -> ModuleRefsMap -> RefsWithStatus -getReExported newExts oldExts depsDiffsMap = - M.fromList $ mapMaybe checkRe oldExports - where - goRe (P.ReExportRef _ es ref) = (P.exportSourceDefinedIn es,) <$> toRefs ref - goRe _ = [] - - oldExports = concatMap goRe (P.efExports oldExts) - newReExports = concatMap goRe (P.efExports newExts) - checkRe (mn, ref) - | (mn, ref) `notElem` newReExports = Just (ref, Removed) - | Just True <- elem ref <$> M.lookup mn depsDiffsMap = Just (ref, Updated) - | otherwise = Nothing - --- Extracts declarations from old and new externs and compares them. Returns a --- tuple of changed refs (a form of which have changed) and unchanged refs with --- dependencies (refs they depend upon). -getChanged :: P.ExternsFile -> P.ExternsFile -> ModuleRefsMap -> (RefsWithStatus, [RefWithDeps]) -getChanged newExts oldExts depsDiffsMap = - (changedRefs, unchangedRefs) - where - modName = P.efModuleName newExts - - getDecls = map stripDeclaration . P.efDeclarations - getTypeFixities = P.efTypeFixities - getFixities = P.efFixities - - -- Type class instances if changed (added/removed) indirectly effect back - -- the class or the types that are defined in the module, meaning if the - -- instance is added/removed we will recompile modules that use the type - -- class or (if the type class defined in another module) we have to - -- recompile modules that use types defined in this module affected by the - -- instance. - applyInstances (a, r, c, u) = - let checkType t (TypeRef t') = t' == t - checkType _ _ = False - uRefs = map fst u -- Unchanged refs. - go (TypeInstanceRef _ (clsMod, cls) types) - | clsRef <- TypeClassRef cls = - if clsMod == modName - then -- If the class is defined in this module we ensure that is marked as changed. - maybe [] pure $ find ((==) clsRef) uRefs - else case S.member clsRef <$> M.lookup clsMod depsDiffsMap of - Just True -> - -- If the type class is in another module and it has - -- changed we don't need to care about instance types - -- (because the instance change affects modules that use - -- the type class/its methods). - [] - _ -> - -- Otherwise mark instance types as changed. - foldMap (\t -> filter (checkType t) uRefs) types - go _ = mempty - - -- Check class instances in added, removed and changed. - affected = foldMap (S.fromList . go . fst) (a <> r <> c) - (uc, uu) = L.partition (flip S.member affected . fst) u - in (a, r, c <> uc, uu) - - -- Group/split exported refs of the module into (added, removed, changed, - -- unchanged) - (a, r, c, u). - declsSplit = - applyInstances $ - splitRefs (getDecls newExts) (getDecls oldExts) (externsDeclarationToRef modName) - - -- Make the context for fixity's data constructor search: place all - -- known refs in the map. - getRefsSet (a, r, c, u) = S.fromList $ map fst (a <> r <> c <> u) - fixityCtx = M.insert modName (getRefsSet declsSplit) depsDiffsMap - - -- Determine which declarations where directly changed or removed by - -- combining Declarations, Fixities and Type Fixities - as they are - -- separated in externs we handle them separately. We don't care about added things. - (_, removed, changed, unchangedRefs) = - fold - [ declsSplit - , splitRefs (getFixities newExts) (getFixities oldExts) (pure . externsFixityToRef fixityCtx) - , splitRefs (getTypeFixities newExts) (getTypeFixities oldExts) (pure . externsTypeFixityToRef) - ] - - changedRefs = - M.fromList $ - map ((,Removed) . fst) removed <> map ((,Updated) . fst) changed - --- Gets set of type constructors from new externs that have changed. -getCtorsSets :: P.ExternsFile -> P.ExternsFile -> Set Ref -getCtorsSets newExts oldExts = - S.map CtorsSetRef $ - M.keysSet $ - M.differenceWith comp (getSets newExts) (getSets oldExts) - where - getSets = M.fromList . foldMap goDecl . P.efDeclarations - goDecl = \case - P.EDType n _ (P.DataType _ _ ctors) -> - [(n, S.fromList $ fst <$> ctors)] - _ -> [] - comp a b = if a == b then Nothing else Just a - --- Takes a list unchanged local refs with dependencies and finds that are affected by --- changed refs. Cyclic dependencies between local refs are searched using --- directed graph. -getAffectedLocal :: ModuleName -> ModuleRefsMap -> [RefWithDeps] -> Set Ref -getAffectedLocal modName diffsMap unchangedRefs = - affectedLocalRefs - where - hasChangedDeps (mn, ref) = - Just True == (S.member ref <$> M.lookup mn diffsMap) - (affectedByChanged, restLocalRefs) = - L.partition (any hasChangedDeps . snd) unchangedRefs - - -- Use graph to go though local refs and their cyclic dependencies on each other. - -- The graph includes only local refs that depend on other local refs. - toNode (ref, deps) = (ref, ref, map snd $ filter ((== modName) . fst) (S.toList deps)) - - -- Make graph vertexes from the rest local refs with deps and affected refs - -- with no deps. - vtxs = toNode <$> restLocalRefs <> (map (const mempty) <$> affectedByChanged) - (graph, fromVtx, toVtx) = G.graphFromEdges vtxs - - -- Graph is a list of refs with (refs) dependencies. - refsGraph = do - (_, t, _) <- vtxs - let v = fromMaybe (internalError "diffExterns: vertex not found") $ toVtx t - let deps = G.reachable graph v - let toKey = (\(_, k, _) -> k) . fromVtx - pure (t, map toKey deps) - - -- Get local refs that depend on affected refs (affected refs are included - -- in the graph result because a node's reachable list includes the node - -- itself). - affectedLocalRefs = - S.fromList $ - map fst $ - filter (any (flip elem (fst <$> affectedByChanged)) . snd) refsGraph - -diffExterns :: P.ExternsFile -> P.ExternsFile -> [ExternsDiff] -> ExternsDiff -diffExterns newExts oldExts depsDiffs = - ExternsDiff modName $ - affectedReExported <> changedRefs <> affectedLocalRefs - where - modName = P.efModuleName newExts - - depsDiffsMap = M.fromList (map (liftM2 (,) edModuleName (M.keysSet . edRefs)) depsDiffs) - - -- To get changed reexported refs, we take those which were removed (not - -- present in new extern's exports) or changed in dependencies. - affectedReExported = getReExported newExts oldExts depsDiffsMap - - (changedRefs, unchangedRefs) = getChanged newExts oldExts depsDiffsMap - - ctorsSets = getCtorsSets newExts oldExts - - -- Extend dependencies' diffs map with local changes. - diffsMapWithLocal - | null changedRefs && null ctorsSets = depsDiffsMap - | otherwise = M.insert modName (M.keysSet changedRefs <> ctorsSets) depsDiffsMap - - affectedLocalRefs = - M.fromSet (const Updated) $ getAffectedLocal modName diffsMapWithLocal unchangedRefs - --- Checks if the externs diffs effect the module (the module uses any diff's --- entries). True if uses, False if not. -checkDiffs :: P.Module -> [ExternsDiff] -> Bool -checkDiffs (P.Module _ _ _ decls exports) diffs - | all isEmpty diffs = False - | isNothing mbSearch = True - | null searches = False - | otherwise = checkReExports || checkUsage searches decls - where - mbSearch = makeSearches decls diffs - searches = fromMaybe S.empty mbSearch - - -- Check if the module reexports any of searched refs. - checkReExports = flip (maybe False) exports $ any $ \case - P.ModuleRef _ mn -> not . null $ S.filter ((== Just mn) . fst) searches - _ -> False - --- Goes though the module and try to find any usage of the refs. --- Takes a set of refs to search in module's declarations, if found returns True. -checkUsage :: Set (Maybe ModuleName, Ref) -> [P.Declaration] -> Bool -checkUsage searches decls = anyUsages - where - -- Two traversals: one to pick up usages of types, one for the rest. - Any anyUsages = - foldMap checkUsageInTypes decls - <> foldMap checkOtherUsages decls - - -- To check data constructors we remove an origin type from it (see `checkCtor`). - searches' = S.map (map stripCtorType) searches - - -- To check data constructors we remove an origin type from it. - emptyName = P.ProperName "" - stripCtorType (ConstructorRef _ n) = ConstructorRef emptyName n - stripCtorType x = x - - check q = Any $ S.member (P.getQual q, P.disqualify q) searches' - - checkType = check . map TypeRef - checkTypeOp = check . map TypeOpRef - checkValue = check . map ValueRef - checkValueOp = check . map ValueOpRef - checkCtor = check . map (ConstructorRef emptyName) - checkClass = check . map TypeClassRef - - -- A nested traversal: pick up types in the module then traverse the structure of the types - (checkUsageInTypes, _, _, _, _) = - P.accumTypes $ P.everythingOnTypes (<>) $ \case - P.TypeConstructor _ n -> checkType n - P.TypeOp _ n -> checkTypeOp n - P.ConstrainedType _ c _ -> checkClass (P.constraintClass c) - _ -> mempty - - checkOtherUsages = - let (extr, _, _, _, _) = P.everythingWithScope goDecl goExpr goBinder mempty mempty - in extr mempty - - goDecl _ = \case - P.TypeInstanceDeclaration _ _ _ _ _ _ tc _ _ -> - checkClass tc - _ -> mempty - - isLocal scope ident = P.LocalIdent ident `S.member` scope - goExpr scope expr = case expr of - P.Var _ n - | P.isUnqualified n && isLocal scope (P.disqualify n) -> mempty - | otherwise -> checkValue n - P.Constructor _ n -> checkCtor n - P.Op _ n -> checkValueOp n - _ -> mempty - - goBinder _ binder = case binder of - P.ConstructorBinder _ n _ -> checkCtor n - P.OpBinder _ n -> checkValueOp n - _ -> mempty - --- | Traverses imports and returns a set of refs to be searched though the --- module. Returns Nothing if removed refs found in imports (no need to search --- through the module - the module needs to be recompiled). If an empty set is --- returned then no changes apply to the module. -makeSearches :: [P.Declaration] -> [ExternsDiff] -> Maybe (Set (Maybe ModuleName, Ref)) -makeSearches decls depsDiffs = - foldM go mempty decls - where - diffsMap = M.fromList (map (liftM2 (,) edModuleName edRefs) depsDiffs) - - -- Add data constructors to refs if all are implicitly imported using (..). - getCtor n (ConstructorRef tn _) = tn == n - getCtor _ _ = False - getCtors n = M.keys . M.filterWithKey (const . getCtor n) - addCtors mn (P.TypeRef _ n Nothing) = maybe [] (getCtors n) (M.lookup mn diffsMap) - addCtors _ _ = [] - getRefs = (toRefs <>) . addCtors - - go s (P.ImportDeclaration _ mn dt qual) - -- We return Nothing if we encounter removed refs in imports. - | Just diffs <- M.lookup mn diffsMap - , removed <- M.keysSet $ M.filter isRefRemoved diffs = - fmap ((s <>) . S.map (qual,) . M.keysSet) $ case dt of - P.Explicit dRefs - | any (flip S.member removed) refs -> Nothing - | otherwise -> - -- Search only refs encountered in the import. - Just $ M.filterWithKey (const . flip elem refs) diffs - where - refs = foldMap (getRefs mn) dRefs - P.Hiding dRefs - | any (flip S.member removed) refs -> Nothing - | otherwise -> - -- Search only refs not encountered in the import. - Just $ M.filterWithKey (const . not . flip elem refs) diffs - where - refs = foldMap (getRefs mn) dRefs - -- Search all changed refs. - P.Implicit -> Just diffs - go s _ = Just s - -toRefs :: P.DeclarationRef -> [Ref] -toRefs = \case - P.TypeClassRef _ n -> [TypeClassRef n] - P.TypeOpRef _ n -> [TypeOpRef n] - P.TypeRef _ n c -> [TypeRef n] <> (ConstructorRef n <$> fromMaybe [] c) - P.ValueRef _ i -> [ValueRef i] - P.ValueOpRef _ n -> [ValueOpRef n] - _ -> [] - -isEmpty :: ExternsDiff -> Bool -isEmpty (ExternsDiff _ refs) - | null refs = True - | otherwise = False - -type Tuple4 m a = (m a, m a, m a, m a) - --- | Returns refs as a tuple of four (added, removed, changed, unchanged). -splitRefs :: forall ref a deps. Monoid deps => Ord ref => Eq a => [a] -> [a] -> (a -> Maybe (ref, deps)) -> Tuple4 [] (ref, deps) -splitRefs new old toRef = - M.foldrWithKey go (added, [], [], []) oldMap - where - toMap :: [a] -> Map ref (deps, [a]) - toMap = M.fromListWith (<>) . mapMaybe (\decl -> do (ref, deps) <- toRef decl; pure (ref, (deps, [decl]))) - newMap = toMap new - oldMap = toMap old - added = fmap (\(ref, (deps, _)) -> (ref, deps)) $ M.toList $ M.difference newMap oldMap - go :: ref -> (deps, [a]) -> Tuple4 [] (ref, deps) -> Tuple4 [] (ref, deps) - go ref (deps, decls) (a, r, c, u) = case M.lookup ref newMap of - Nothing -> (a, r <> [(ref, deps)], c, u) - Just (_, newDecls) - | decls /= newDecls -> (a, r, (ref, deps) : c, u) - | otherwise -> (a, r, c, (ref, deps) : u) - --- | Traverses the type and finds all the refs within. -typeDeps :: P.Type a -> S.Set (ModuleName, Ref) -typeDeps = P.everythingOnTypes (<>) $ - \case - P.TypeConstructor _ (P.Qualified (P.ByModuleName mn) tn) - | isPrimModule mn -> mempty - | otherwise -> S.singleton (mn, TypeRef tn) - P.TypeConstructor _ _ -> - internalError "typeDeps: type is not qualified" - P.TypeOp _ (P.Qualified (P.ByModuleName mn) tn) - | isPrimModule mn -> mempty - | otherwise -> S.singleton (mn, TypeOpRef tn) - P.ConstrainedType _ c _ -> - S.singleton (map TypeClassRef (qualified $ P.constraintClass c)) - P.TypeOp _ _ -> - internalError "typeDeps: type is not qualified" - _ -> mempty - -qualified :: P.Qualified b -> (ModuleName, b) -qualified (P.Qualified (P.ByModuleName mn) v) = (mn, v) -qualified _ = internalError "ExternsDiff: type is not qualified" - --- | To get fixity's data constructor dependency we should provide it with the --- context (that contains all known refs) to search in. -externsFixityToRef :: Map ModuleName (Set Ref) -> P.ExternsFixity -> RefWithDeps -externsFixityToRef refs (P.ExternsFixity _ _ n alias) = - (ValueOpRef n, maybe mempty S.singleton $ getDep (qualified alias)) - where - getDep (mn, Left i) = Just (mn, ValueRef i) - getDep (mn, Right p) = - (mn,) <$> (M.lookup mn refs >>= S.lookupMin . S.filter (goRef p)) - goRef c (ConstructorRef _ c') = c' == c - goRef _ _ = False - -externsTypeFixityToRef :: P.ExternsTypeFixity -> RefWithDeps -externsTypeFixityToRef (P.ExternsTypeFixity _ _ n alias) = - ( TypeOpRef n - , S.singleton (map TypeRef (qualified alias)) - ) - -externsDeclarationToRef :: ModuleName -> P.ExternsDeclaration -> Maybe RefWithDeps -externsDeclarationToRef moduleName = \case - P.EDType n t tk - | P.isDictTypeName n -> Nothing - | otherwise -> Just (TypeRef n, typeDeps t <> typeKindDeps tk) - -- - P.EDTypeSynonym n args t -> - Just (TypeRef n, typeDeps t <> foldArgs args) - -- - P.EDDataConstructor n _ tn t _ - | P.isDictTypeName n -> Nothing - | otherwise -> - Just - ( ConstructorRef tn n - , -- Add the type as a dependency: if the type has changed (e.g. left side - -- param is added) we should recompile the module which uses the - -- constructor (even if there no the explicit type import). - -- Aso add the ad-hoc constructors set ref dependency: if a ctor - -- added/removed it should affect all constructors in the type, - -- because case statement's validity may be affected by newly added - -- or removed constructors. - typeDeps t <> S.fromList [(moduleName, TypeRef tn), (moduleName, CtorsSetRef tn)] - ) - -- - P.EDValue n t -> - Just (ValueRef n, typeDeps t) - -- - P.EDClass n args members constraints _ _ -> - Just - ( TypeClassRef n - , foldArgs args <> constraintsDeps constraints <> foldMap (typeDeps . snd) members - ) - -- - P.EDInstance cn n args kinds types constraints _ _ _ _ -> - Just - ( TypeInstanceRef n (qualified cn) (mapMaybe myType types) - , maybe mempty constraintsDeps constraints <> instanceArgsDeps args <> foldMap typeDeps kinds - ) - where - goDataTypeArg (_, st, _) = maybe mempty typeDeps st - typeKindDeps (P.DataType _ args _) = foldMap goDataTypeArg args - typeKindDeps _ = mempty - - myType (P.TypeConstructor _ (P.Qualified (P.ByModuleName mn) tn)) - | isPrimModule mn || moduleName /= mn = Nothing - | otherwise = Just tn - myType _ = Nothing - - foldArgs = foldMap typeDeps . mapMaybe snd - instanceArgsDeps = foldMap (typeDeps . snd) - constraintsDeps = - foldMap - ( \(P.Constraint _ cls kArgs args _) -> - S.singleton (TypeClassRef <$> qualified cls) - <> foldMap typeDeps kArgs - <> foldMap typeDeps args - ) - --- | Removes excessive info from declarations before comparing. --- --- TODO: params renaming will be needed to avoid recompilation because of params --- name changes. -stripDeclaration :: P.ExternsDeclaration -> P.ExternsDeclaration -stripDeclaration = \case - P.EDType n t (P.DataType dt args _) -> - -- Remove the notion of data constructors, we only compare type's left side. - P.EDType n t (P.DataType dt args []) - -- - P.EDInstance cn n fa ks ts cs ch chi ns ss -> - P.EDInstance cn n fa ks ts cs (map stripChain ch) chi ns ss - -- - decl -> decl - where - emptySP = P.SourcePos 0 0 - stripChain (ChainId (n, _)) = ChainId (n, emptySP) - -isPrimModule :: ModuleName -> Bool -isPrimModule = flip S.member (S.fromList primModules) diff --git a/claude-help/original-compiler/src/Language/PureScript/Make/IdeCache.hs b/claude-help/original-compiler/src/Language/PureScript/Make/IdeCache.hs deleted file mode 100644 index 7d07cb04..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make/IdeCache.hs +++ /dev/null @@ -1,235 +0,0 @@ -module Language.PureScript.Make.IdeCache where - -import Prelude - -import Language.PureScript.Ide.ToIde (toIdeDeclarationAnn) -import Database.SQLite.Simple (NamedParam(..)) -import Database.SQLite.Simple qualified as SQLite -import Codec.Serialise qualified as Serialise -import System.FilePath ((), takeDirectory) -import Language.PureScript.Names (runModuleName, ProperName (runProperName), runIdent, disqualify, Ident (..), OpName (OpName)) -import Language.PureScript.Externs (ExternsFile(..), ExternsImport(..)) -import Data.Foldable (for_) -import Control.Monad.IO.Class (MonadIO (liftIO)) -import System.Directory (createDirectoryIfMissing) -import Language.PureScript.Externs qualified as P -import Data.Text qualified as Text -import Language.PureScript.Docs.Types qualified as Docs -import Language.PureScript.Ide.Util (identifierFromIdeDeclaration, discardAnn, namespaceForDeclaration) -import Data.Text (Text) -import Language.PureScript.Ide.Types (Annotation(..), declarationType, IdeDeclarationAnn (_idaAnnotation), IdeNamespace (IdeNSValue, IdeNSType)) -import Language.PureScript.Docs.Types (Declaration(declChildren)) -import Language.PureScript.Docs.AsMarkdown (declAsMarkdown, runDocs) -import Codec.Serialise (serialise) -import Language.PureScript.AST.Declarations (Module, Expr (Var, Constructor), getModuleDeclarations, DeclarationRef (..), ExportSource (..)) -import Language.PureScript.AST.Binders (Binder (ConstructorBinder, OpBinder)) -import Language.PureScript.Ide.Filter.Declaration (DeclarationType (..)) -import Data.Aeson qualified as Aeson -import Language.PureScript.AST.Traversals (everywhereOnValuesM) -import Protolude (identity) -import Language.PureScript.Names qualified as T - -sqliteExtern :: (MonadIO m) => FilePath -> Module -> ExternsFile -> m () -sqliteExtern outputDir m extern = liftIO $ do - conn <- SQLite.open db - SQLite.execute_ conn "pragma busy_timeout = 300000;" - - let (doDecl, _, _) = everywhereOnValuesM (pure . identity) (\expr -> case expr of - Var ss i -> do - let iv = disqualify i - case iv of - Ident t -> do - SQLite.executeNamed conn - "insert into asts (module_name, name, span) values (:module_name, :name, :span)" - [ ":module_name" := runModuleName ( efModuleName extern ) - , ":name" := t - , ":span" := Aeson.encode ss - ] - _ -> pure () - pure expr - Constructor ss qctor -> do - let ctor = disqualify qctor - SQLite.executeNamed conn - "insert into asts (module_name, name, span) values (:module_name, :name, :span)" - [ ":module_name" := runModuleName ( efModuleName extern ) - , ":name" := runProperName ctor - , ":span" := Aeson.encode ss - ] - pure expr - _ -> pure expr - ) (\binder -> case binder of - ConstructorBinder ss qctor _ -> do - let ctor = disqualify qctor - SQLite.executeNamed conn - "insert into asts (module_name, name, span) values (:module_name, :name, :span)" - [ ":module_name" := runModuleName ( efModuleName extern ) - , ":name" := runProperName ctor - , ":span" := Aeson.encode ss - ] - pure binder - OpBinder ss qop -> do - let op = disqualify qop - SQLite.executeNamed conn - "insert into asts (module_name, name, span) values (:module_name, :name, :span)" - [ ":module_name" := runModuleName ( efModuleName extern ) - , ":name" := (\(OpName o) -> o) op - , ":span" := Aeson.encode ss - ] - pure binder - _ -> pure binder - ) - - SQLite.execute_ conn "pragma foreign_keys = ON;" - - SQLite.executeNamed conn - "delete from modules where module_name = :module_name" - [ ":module_name" := runModuleName ( efModuleName extern ) - ] - - - SQLite.executeNamed conn - "insert into modules (module_name, extern) values (:module_name, :extern)" - [ ":module_name" := runModuleName ( efModuleName extern ) - , ":extern" := Serialise.serialise extern - ] - - for_ (getModuleDeclarations m) (\d -> doDecl d) - - for_ (efExports extern) (\case - ReExportRef _ (ExportSource _ definedIn) (ValueRef _ (Ident i)) -> do - SQLite.executeNamed conn "insert into exports (module_name, name, defined_in, declaration_type) values (:module_name, :name, :defined_in, 'value')" - [ ":module_name" := runModuleName (efModuleName extern ) - , ":name" := i - , ":defined_in" := runModuleName definedIn - ] - ReExportRef _ (ExportSource _ definedIn) (ValueOpRef _ (OpName n)) -> do - SQLite.executeNamed conn "insert into exports (module_name, name, defined_in, declaration_type) values (:module_name, :name, :defined_in, 'valueoperator')" - [ ":module_name" := runModuleName (efModuleName extern ) - , ":name" := n - , ":defined_in" := runModuleName definedIn - ] - ReExportRef _ (ExportSource _ definedIn) (TypeClassRef _ (T.ProperName n)) -> do - SQLite.executeNamed conn "insert into exports (module_name, name, defined_in, declaration_type) values (:module_name, :name, :defined_in, 'typeclass')" - [ ":module_name" := runModuleName (efModuleName extern ) - , ":name" := n - , ":defined_in" := runModuleName definedIn - ] - _ -> pure () - ) - - for_ (efImports extern) (\i -> do - SQLite.executeNamed conn "insert into dependencies (module_name, dependency) values (:module_name, :dependency)" - [ ":module_name" := runModuleName (efModuleName extern ) - , ":dependency" := runModuleName (eiModule i) - ]) - - for_ (toIdeDeclarationAnn m extern) (\ideDeclaration -> do - SQLite.executeNamed conn - ("insert into ide_declarations (module_name, name, namespace, declaration_type, span, declaration) " <> - "values (:module_name, :name, :namespace, :declaration_type, :span, :declaration)" - ) - [ ":module_name" := runModuleName (efModuleName extern ) - , ":name" := identifierFromIdeDeclaration (discardAnn ideDeclaration) - , ":namespace" := namespaceForDeclaration (discardAnn ideDeclaration) - , ":declaration_type" := declarationType (discardAnn ideDeclaration) - , ":span" := serialise (_annLocation $ _idaAnnotation ideDeclaration) - , ":declaration" := serialise ideDeclaration - ]) - - SQLite.close conn - return () - where - db = outputDir "cache.db" - - -convertDecl :: P.ExternsDeclaration -> Text.Text -convertDecl = \case - P.EDType{..} -> runProperName edTypeName - P.EDDataConstructor{..} -> runProperName edDataCtorName - P.EDValue{..} -> runIdent edValueName - _ -> "OTHER" - -spanDecl :: P.ExternsDeclaration -> Text.Text -spanDecl = \case - _ -> "NO SPAN" - -createParentDirectory :: FilePath -> IO () -createParentDirectory = createDirectoryIfMissing True . takeDirectory - -sqliteInit :: (MonadIO m) => FilePath -> m () -sqliteInit outputDir = liftIO $ do - createParentDirectory db - conn <- SQLite.open db - SQLite.execute_ conn "pragma busy_timeout = 300000;" - SQLite.execute_ conn "pragma journal_mode=wal;" - SQLite.execute_ conn "pragma foreign_keys = ON;" - SQLite.execute_ conn $ SQLite.Query $ Text.pack $ unlines - [ "create table if not exists modules (" - , " module_name text primary key," - , " extern blob," - , " unique (module_name) on conflict replace" - , ")" - ] - - SQLite.execute_ conn $ SQLite.Query $ Text.pack $ unlines - [ "create table if not exists dependencies (" - , " module_name text not null references modules(module_name) on delete cascade," - , " dependency text not null," - , " unique (module_name, dependency) on conflict ignore" - , ")" - ] - - SQLite.execute_ conn $ SQLite.Query $ Text.pack $ unlines - [ "create table if not exists asts (" - , " module_name text references modules(module_name) on delete cascade," - , " name text not null," - , " span text" - , ")" - ] - - SQLite.execute_ conn $ SQLite.Query $ Text.pack $ unlines - [ "create table if not exists exports (" - , "module_name text references modules(module_name) on delete cascade," - , "name text not null," - , "defined_in text," - , "declaration_type text" - , ")" - ] - - SQLite.execute_ conn "create index if not exists asts_module_name_idx on asts(module_name);" - SQLite.execute_ conn "create index if not exists asts_name_idx on asts(name);" - - SQLite.execute_ conn "create index if not exists exports_name_idx on exports(name);" - SQLite.execute_ conn "create index if not exists exports_module_name_idx on exports(module_name);" - - SQLite.execute_ conn "create index if not exists exports_defined_in_id on exports(defined_in);" - SQLite.execute_ conn "create index if not exists exports_declaration_type_idx on exports(declaration_type);" - - SQLite.execute_ conn "create table if not exists ide_declarations (module_name text references modules(module_name) on delete cascade, name text, namespace text, declaration_type text, span blob, declaration blob)" - - SQLite.execute_ conn "create index if not exists ide_declarations_name_idx on ide_declarations(name);" - - SQLite.execute_ conn "create index if not exists ide_declarations_module_name_idx on ide_declarations(module_name);" - - SQLite.execute_ conn "create index if not exists exports_idx on exports(defined_in,name,declaration_type,module_name);" - - SQLite.close conn - where - db = outputDir "cache.db" - -toDeclarationType :: Declaration -> DeclarationType -toDeclarationType (Docs.Declaration _ _ _ _ (Docs.ValueDeclaration _) _) = Value -toDeclarationType (Docs.Declaration _ _ _ _ (Docs.DataDeclaration _ _ _) _) = Type -toDeclarationType (Docs.Declaration _ _ _ _ _ _ ) = Value - -toIdeN :: Docs.Namespace -> IdeNamespace -toIdeN Docs.ValueLevel = IdeNSValue -toIdeN Docs.TypeLevel = IdeNSType - -toIdeNamespace :: Declaration -> IdeNamespace -toIdeNamespace (Docs.Declaration _ _ _ _ declInfo _) = case Docs.declInfoNamespace declInfo of - Docs.ValueLevel -> IdeNSValue - Docs.TypeLevel -> IdeNSType - -childDeclInfoNamespaceIde :: Docs.ChildDeclarationInfo -> IdeNamespace -childDeclInfoNamespaceIde = toIdeN . Docs.childDeclInfoNamespace diff --git a/claude-help/original-compiler/src/Language/PureScript/Make/Monad.hs b/claude-help/original-compiler/src/Language/PureScript/Make/Monad.hs deleted file mode 100644 index ed553cf2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Make/Monad.hs +++ /dev/null @@ -1,203 +0,0 @@ -module Language.PureScript.Make.Monad - ( -- * Implementation of Make API using files on disk - Make(..) - , runMake - , makeIO - , getTimestamp - , getTimestampMaybe - , getCurrentTime - , setTimestamp - , readTextFile - , readJSONFile - , readJSONFileIO - , readCborFile - , readCborFileIO - , readExternsFile - , hashFile - , writeTextFile - , writeJSONFile - , writeCborFile - , writeCborFileIO - , copyFile - ) where - -import Prelude - -import Codec.Serialise (Serialise) -import Codec.Serialise qualified as Serialise -import Control.Exception (fromException, tryJust, Exception (displayException)) -import Control.Monad (join, guard) -import Control.Monad.Base (MonadBase(..)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.IO.Class (MonadIO(..)) -import Control.Monad.Logger (Logger, runLogger') -import Control.Monad.Reader (MonadReader(..), ReaderT(..)) -import Control.Monad.Trans.Control (MonadBaseControl(..)) -import Control.Monad.Trans.Except (ExceptT, runExceptT) -import Control.Monad.Writer.Class (MonadWriter(..)) -import Data.Aeson qualified as Aeson -import Data.ByteString qualified as B -import Data.Maybe (isJust) -import Data.Text (Text) -import Data.Text qualified as Text -import Data.Time.Clock (UTCTime) -import Data.Time.Clock qualified as Time -import Language.PureScript.Errors (ErrorMessage(..), MultipleErrors, SimpleErrorMessage(..), singleError) -import Language.PureScript.Externs (ExternsFile, externsIsCurrentVersion) -import Language.PureScript.Make.Cache (ContentHash, hash) -import Language.PureScript.Options (Options) -import System.Directory (createDirectoryIfMissing, getModificationTime, setModificationTime) -import System.Directory qualified as Directory -import System.FilePath (takeDirectory) -import System.IO.Error (tryIOError, isDoesNotExistError) -import System.IO.UTF8 (readUTF8FileT) - --- | A monad for running make actions -newtype Make a = Make - { unMake :: ReaderT Options (ExceptT MultipleErrors (Logger MultipleErrors)) a - } deriving (Functor, Applicative, Monad, MonadIO, MonadError MultipleErrors, MonadWriter MultipleErrors, MonadReader Options) - -instance MonadBase IO Make where - liftBase = liftIO - -instance MonadBaseControl IO Make where - type StM Make a = Either MultipleErrors a - liftBaseWith f = Make $ liftBaseWith $ \q -> f (q . unMake) - restoreM = Make . restoreM - --- | Execute a 'Make' monad, returning either errors, or the result of the compile plus any warnings. -runMake :: Options -> Make a -> IO (Either MultipleErrors a, MultipleErrors) -runMake opts = runLogger' . runExceptT . flip runReaderT opts . unMake - --- | Run an 'IO' action in the 'Make' monad. The 'String' argument should --- describe what we were trying to do; it is used for rendering errors in the --- case that an IOException is thrown. -makeIO :: (MonadIO m, MonadError MultipleErrors m) => Text -> IO a -> m a -makeIO description io = do - res <- liftIO (tryIOError io) - either (throwError . singleError . ErrorMessage [] . FileIOError description . Text.pack . displayException) pure res - --- | Get a file's modification time in the 'Make' monad, capturing any errors --- using the 'MonadError' instance. -getTimestamp :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> m UTCTime -getTimestamp path = - makeIO ("get a timestamp for file: " <> Text.pack path) $ getModificationTime path - --- | Get a file's modification time in the 'Make' monad, returning Nothing if --- the file does not exist. -getTimestampMaybe :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> m (Maybe UTCTime) -getTimestampMaybe path = - makeIO ("get a timestamp for file: " <> Text.pack path) $ catchDoesNotExist $ getModificationTime path - --- | Get current system time. -getCurrentTime :: (MonadIO m) => m UTCTime -getCurrentTime = - liftIO Time.getCurrentTime - --- | Set a file's modification time in the 'Make' monad, returning False if --- the file does not exist. -setTimestamp :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> UTCTime -> m Bool -setTimestamp path time = - makeIO ("set a timestamp for file: " <> Text.pack path) $ (fmap isJust . catchDoesNotExist) $ setModificationTime path time - - --- | Read a text file strictly in the 'Make' monad, capturing any errors using --- the 'MonadError' instance. -readTextFile :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> m Text -readTextFile path = - makeIO ("read file: " <> Text.pack path) $ - readUTF8FileT path - --- | Read a JSON file in the 'Make' monad, returning 'Nothing' if the file does --- not exist or could not be parsed. Errors are captured using the 'MonadError' --- instance. -readJSONFile :: (MonadIO m, MonadError MultipleErrors m) => Aeson.FromJSON a => FilePath -> m (Maybe a) -readJSONFile path = - makeIO ("read JSON file: " <> Text.pack path) (readJSONFileIO path) - -readJSONFileIO :: Aeson.FromJSON a => FilePath -> IO (Maybe a) -readJSONFileIO path = do - r <- catchDoesNotExist $ Aeson.decodeFileStrict' path - return $ join r - --- | Read a Cbor encoded file in the 'Make' monad, returning --- 'Nothing' if the file does not exist or could not be parsed. Errors --- are captured using the 'MonadError' instance. -readCborFile :: (MonadIO m, MonadError MultipleErrors m) => Serialise a => FilePath -> m (Maybe a) -readCborFile path = - makeIO ("read Binary file: " <> Text.pack path) (readCborFileIO path) - -readCborFileIO :: Serialise a => FilePath -> IO (Maybe a) -readCborFileIO path = do - r <- catchDoesNotExist $ catchDeserialiseFailure $ Serialise.readFileDeserialise path - return (join r) - --- | Read an externs file, returning 'Nothing' if the file does not exist, --- could not be parsed, or was generated by a different version of the --- compiler. -readExternsFile :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> m (Maybe ExternsFile) -readExternsFile path = do - mexterns <- readCborFile path - return $ do - externs <- mexterns - guard $ externsIsCurrentVersion externs - return externs - -hashFile :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> m ContentHash -hashFile path = do - makeIO ("hash file: " <> Text.pack path) - (hash <$> B.readFile path) - --- | If the provided action threw an 'isDoesNotExist' error, catch it and --- return Nothing. Otherwise return Just the result of the inner action. -catchDoesNotExist :: IO a -> IO (Maybe a) -catchDoesNotExist inner = do - r <- tryJust (guard . isDoesNotExistError) inner - case r of - Left () -> - return Nothing - Right x -> - return (Just x) - -catchDeserialiseFailure :: IO a -> IO (Maybe a) -catchDeserialiseFailure inner = do - r <- tryJust fromException inner - case r of - Left (_ :: Serialise.DeserialiseFailure) -> - return Nothing - Right x -> - return (Just x) - --- | Write a text file in the 'Make' monad, capturing any errors using the --- 'MonadError' instance. -writeTextFile :: FilePath -> B.ByteString -> Make () -writeTextFile path text = makeIO ("write file: " <> Text.pack path) $ do - createParentDirectory path - B.writeFile path text - --- | Write a JSON file in the 'Make' monad, capturing any errors using the --- 'MonadError' instance. -writeJSONFile :: (MonadIO m, MonadError MultipleErrors m) => Aeson.ToJSON a => FilePath -> a -> m () -writeJSONFile path value = makeIO ("write JSON file: " <> Text.pack path) $ do - createParentDirectory path - Aeson.encodeFile path value - -writeCborFile :: (MonadIO m, MonadError MultipleErrors m) => Serialise a => FilePath -> a -> m () -writeCborFile path value = - makeIO ("write Cbor file: " <> Text.pack path) (writeCborFileIO path value) - -writeCborFileIO :: Serialise a => FilePath -> a -> IO () -writeCborFileIO path value = do - createParentDirectory path - Serialise.writeFileSerialise path value - --- | Copy a file in the 'Make' monad, capturing any errors using the --- 'MonadError' instance. -copyFile :: (MonadIO m, MonadError MultipleErrors m) => FilePath -> FilePath -> m () -copyFile src dest = - makeIO ("copy file: " <> Text.pack src <> " -> " <> Text.pack dest) $ do - createParentDirectory dest - Directory.copyFile src dest - -createParentDirectory :: FilePath -> IO () -createParentDirectory = createDirectoryIfMissing True . takeDirectory diff --git a/claude-help/original-compiler/src/Language/PureScript/ModuleDependencies.hs b/claude-help/original-compiler/src/Language/PureScript/ModuleDependencies.hs deleted file mode 100644 index 3bcb914f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/ModuleDependencies.hs +++ /dev/null @@ -1,89 +0,0 @@ --- | Provides the ability to sort modules based on module dependencies -module Language.PureScript.ModuleDependencies - ( DependencyDepth(..) - , sortModules - , ModuleGraph - , ModuleSignature(..) - , moduleSignature - ) where - -import Protolude hiding (head) - -import Data.Array ((!)) -import Data.Graph (SCC(..), graphFromEdges, reachable, stronglyConnComp) -import Data.Set qualified as S -import Language.PureScript.AST (Declaration(..), ErrorMessageHint(..), Module(..), SourceSpan) -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage', errorMessage'', parU) -import Language.PureScript.Names (ModuleName) - --- | A list of modules with their transitive dependencies -type ModuleGraph = [(ModuleName, [ModuleName])] - --- | A module signature for sorting dependencies. -data ModuleSignature = ModuleSignature - { sigSourceSpan :: SourceSpan - , sigModuleName :: ModuleName - , sigImports :: [(ModuleName, SourceSpan)] - } - -data DependencyDepth = Direct | Transitive - --- | Sort a collection of modules based on module dependencies. --- --- Reports an error if the module graph contains a cycle. -sortModules - :: forall m a - . MonadError MultipleErrors m - => DependencyDepth - -> (a -> ModuleSignature) - -> [a] - -> m ([a], ModuleGraph) -sortModules dependencyDepth toSig ms = do - let - ms' = (\m -> (m, toSig m)) <$> ms - mns = S.fromList $ map (sigModuleName . snd) ms' - verts <- parU ms' (toGraphNode mns) - ms'' <- parU (stronglyConnComp verts) toModule - let (graph, fromVertex, toVertex) = graphFromEdges verts - moduleGraph = do (_, mn, _) <- verts - let v = fromMaybe (internalError "sortModules: vertex not found") (toVertex mn) - deps = case dependencyDepth of - Direct -> graph ! v - Transitive -> reachable graph v - toKey i = case fromVertex i of (_, key, _) -> key - return (mn, filter (/= mn) (map toKey deps)) - return (fst <$> ms'', moduleGraph) - where - toGraphNode :: S.Set ModuleName -> (a, ModuleSignature) -> m ((a, ModuleSignature), ModuleName, [ModuleName]) - toGraphNode mns m@(_, ModuleSignature _ mn deps) = do - void . parU deps $ \(dep, pos) -> - when (dep `notElem` C.primModules && S.notMember dep mns) . - throwError - . addHint (ErrorInModule mn) - . errorMessage' pos - $ ModuleNotFound dep - pure (m, mn, map fst deps) - --- | Calculate a list of used modules based on explicit imports and qualified names. -usedModules :: Declaration -> Maybe (ModuleName, SourceSpan) --- Regardless of whether an imported module is qualified we still need to --- take into account its import to build an accurate list of dependencies. -usedModules (ImportDeclaration (ss, _) mn _ _) = pure (mn, ss) -usedModules _ = Nothing - --- | Convert a strongly connected component of the module graph to a module -toModule :: MonadError MultipleErrors m => SCC (a, ModuleSignature) -> m (a, ModuleSignature) -toModule (AcyclicSCC m) = return m -toModule (CyclicSCC ms) = - case nonEmpty ms of - Nothing -> - internalError "toModule: empty CyclicSCC" - Just ms' -> - throwError - . errorMessage'' (fmap (sigSourceSpan . snd) ms') - $ CycleInModules (map (sigModuleName . snd) ms') - -moduleSignature :: Module -> ModuleSignature -moduleSignature (Module ss _ mn ds _) = ModuleSignature ss mn (ordNub (mapMaybe usedModules ds)) diff --git a/claude-help/original-compiler/src/Language/PureScript/Names.hs b/claude-help/original-compiler/src/Language/PureScript/Names.hs deleted file mode 100644 index 094ae577..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Names.hs +++ /dev/null @@ -1,323 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - --- | --- Data types for names --- -module Language.PureScript.Names where - -import Prelude - -import Codec.Serialise (Serialise) -import Control.Applicative ((<|>)) -import Control.Monad.Supply.Class (MonadSupply(..)) -import Control.DeepSeq (NFData) -import Data.Functor.Contravariant (contramap) -import Data.Vector qualified as V - -import GHC.Generics (Generic) -import Data.Aeson (FromJSON(..), FromJSONKey(..), Options(..), SumEncoding(..), ToJSON(..), ToJSONKey(..), defaultOptions, parseJSON2, toJSON2, withArray) -import Data.Aeson.TH (deriveJSON) -import Data.Text (Text) -import Data.Text qualified as T -import Data.Int (Int64) - -import Language.PureScript.AST.SourcePos (SourcePos, pattern SourcePos) - --- | A sum of the possible name types, useful for error and lint messages. -data Name - = IdentName Ident - | ValOpName (OpName 'ValueOpName) - | TyName (ProperName 'TypeName) - | TyOpName (OpName 'TypeOpName) - | DctorName (ProperName 'ConstructorName) - | TyClassName (ProperName 'ClassName) - | ModName ModuleName - deriving (Eq, Ord, Show, Generic) - -instance NFData Name -instance Serialise Name - -getIdentName :: Name -> Maybe Ident -getIdentName (IdentName name) = Just name -getIdentName _ = Nothing - -getValOpName :: Name -> Maybe (OpName 'ValueOpName) -getValOpName (ValOpName name) = Just name -getValOpName _ = Nothing - -getTypeName :: Name -> Maybe (ProperName 'TypeName) -getTypeName (TyName name) = Just name -getTypeName _ = Nothing - -getTypeOpName :: Name -> Maybe (OpName 'TypeOpName) -getTypeOpName (TyOpName name) = Just name -getTypeOpName _ = Nothing - -getDctorName :: Name -> Maybe (ProperName 'ConstructorName) -getDctorName (DctorName name) = Just name -getDctorName _ = Nothing - -getClassName :: Name -> Maybe (ProperName 'ClassName) -getClassName (TyClassName name) = Just name -getClassName _ = Nothing - --- | --- This type is meant to be extended with any new uses for idents that come --- along. Adding constructors to this type is cheaper than adding them to --- `Ident` because functions that match on `Ident` can ignore all --- `InternalIdent`s with a single pattern, and thus don't have to change if --- a new `InternalIdentData` constructor is created. --- -data InternalIdentData - -- Used by CoreFn.Laziness - = RuntimeLazyFactory | Lazy !Text - deriving (Show, Eq, Ord, Generic) - -instance NFData InternalIdentData -instance Serialise InternalIdentData - --- | --- Names for value identifiers --- -data Ident - -- | - -- An alphanumeric identifier - -- - = Ident Text - -- | - -- A generated name for an identifier - -- - | GenIdent (Maybe Text) Int64 - -- | - -- A generated name used only for type-checking - -- - | UnusedIdent - -- | - -- A generated name used only for internal transformations - -- - | InternalIdent !InternalIdentData - deriving (Show, Eq, Ord, Generic) - -instance NFData Ident -instance Serialise Ident - -unusedIdent :: Text -unusedIdent = "$__unused" - -runIdent :: Ident -> Text -runIdent (Ident i) = i -runIdent (GenIdent Nothing n) = "$" <> T.pack (show n) -runIdent (GenIdent (Just name) n) = "$" <> name <> T.pack (show n) -runIdent UnusedIdent = unusedIdent -runIdent InternalIdent{} = error "unexpected InternalIdent" - -showIdent :: Ident -> Text -showIdent = runIdent - -freshIdent :: MonadSupply m => Text -> m Ident -freshIdent name = GenIdent (Just name) <$> fresh - -freshIdent' :: MonadSupply m => m Ident -freshIdent' = GenIdent Nothing <$> fresh - -isPlainIdent :: Ident -> Bool -isPlainIdent Ident{} = True -isPlainIdent _ = False - --- | --- Operator alias names. --- -newtype OpName (a :: OpNameType) = OpName { runOpName :: Text } - deriving (Show, Eq, Ord, Generic) - -instance NFData (OpName a) -instance Serialise (OpName a) - -instance ToJSON (OpName a) where - toJSON = toJSON . runOpName - -instance FromJSON (OpName a) where - parseJSON = fmap OpName . parseJSON - -showOp :: OpName a -> Text -showOp op = "(" <> runOpName op <> ")" - --- | --- The closed set of operator alias types. --- -data OpNameType = ValueOpName | TypeOpName | AnyOpName - -eraseOpName :: OpName a -> OpName 'AnyOpName -eraseOpName = OpName . runOpName - -coerceOpName :: OpName a -> OpName b -coerceOpName = OpName . runOpName - --- | --- Proper names, i.e. capitalized names for e.g. module names, type//data constructors. --- -newtype ProperName (a :: ProperNameType) = ProperName { runProperName :: Text } - deriving (Show, Eq, Ord, Generic) - -instance NFData (ProperName a) -instance Serialise (ProperName a) - -instance ToJSON (ProperName a) where - toJSON = toJSON . runProperName - -instance FromJSON (ProperName a) where - parseJSON = fmap ProperName . parseJSON - --- | --- The closed set of proper name types. --- -data ProperNameType - = TypeName - | ConstructorName - | ClassName - | Namespace - --- | --- Coerces a ProperName from one ProperNameType to another. This should be used --- with care, and is primarily used to convert ClassNames into TypeNames after --- classes have been desugared. --- -coerceProperName :: ProperName a -> ProperName b -coerceProperName = ProperName . runProperName - --- | --- Module names --- -newtype ModuleName = ModuleName Text - deriving (Show, Eq, Ord, Generic) - deriving newtype Serialise - -instance NFData ModuleName - -runModuleName :: ModuleName -> Text -runModuleName (ModuleName name) = name - -moduleNameFromString :: Text -> ModuleName -moduleNameFromString = ModuleName - -isBuiltinModuleName :: ModuleName -> Bool -isBuiltinModuleName (ModuleName mn) = mn == "Prim" || "Prim." `T.isPrefixOf` mn - -data QualifiedBy - = BySourcePos SourcePos - | ByModuleName ModuleName - deriving (Show, Eq, Ord, Generic) - -pattern ByNullSourcePos :: QualifiedBy -pattern ByNullSourcePos = BySourcePos (SourcePos 0 0) - -instance NFData QualifiedBy -instance Serialise QualifiedBy - -isBySourcePos :: QualifiedBy -> Bool -isBySourcePos (BySourcePos _) = True -isBySourcePos _ = False - -byMaybeModuleName :: Maybe ModuleName -> QualifiedBy -byMaybeModuleName (Just mn) = ByModuleName mn -byMaybeModuleName Nothing = ByNullSourcePos - -toMaybeModuleName :: QualifiedBy -> Maybe ModuleName -toMaybeModuleName (ByModuleName mn) = Just mn -toMaybeModuleName (BySourcePos _) = Nothing - --- | --- A qualified name, i.e. a name with an optional module name --- -data Qualified a = Qualified QualifiedBy a - deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic) - -instance NFData a => NFData (Qualified a) -instance Serialise a => Serialise (Qualified a) - -showQualified :: (a -> Text) -> Qualified a -> Text -showQualified f (Qualified (BySourcePos _) a) = f a -showQualified f (Qualified (ByModuleName name) a) = runModuleName name <> "." <> f a - -getQual :: Qualified a -> Maybe ModuleName -getQual (Qualified qb _) = toMaybeModuleName qb - --- | --- Provide a default module name, if a name is unqualified --- -qualify :: ModuleName -> Qualified a -> (ModuleName, a) -qualify m (Qualified (BySourcePos _) a) = (m, a) -qualify _ (Qualified (ByModuleName m) a) = (m, a) - --- | --- Makes a qualified value from a name and module name. --- -mkQualified :: a -> ModuleName -> Qualified a -mkQualified name mn = Qualified (ByModuleName mn) name - --- | Remove the module name from a qualified name -disqualify :: Qualified a -> a -disqualify (Qualified _ a) = a - --- | --- Remove the qualification from a value when it is qualified with a particular --- module name. --- -disqualifyFor :: Maybe ModuleName -> Qualified a -> Maybe a -disqualifyFor mn (Qualified qb a) | mn == toMaybeModuleName qb = Just a -disqualifyFor _ _ = Nothing - --- | --- Checks whether a qualified value is actually qualified with a module reference --- -isQualified :: Qualified a -> Bool -isQualified (Qualified (BySourcePos _) _) = False -isQualified _ = True - --- | --- Checks whether a qualified value is not actually qualified with a module reference --- -isUnqualified :: Qualified a -> Bool -isUnqualified = not . isQualified - --- | --- Checks whether a qualified value is qualified with a particular module --- -isQualifiedWith :: ModuleName -> Qualified a -> Bool -isQualifiedWith mn (Qualified (ByModuleName mn') _) = mn == mn' -isQualifiedWith _ _ = False - -instance ToJSON a => ToJSON (Qualified a) where - toJSON (Qualified qb a) = case qb of - ByModuleName mn -> toJSON2 (mn, a) - BySourcePos ss -> toJSON2 (ss, a) - -instance FromJSON a => FromJSON (Qualified a) where - parseJSON v = byModule <|> bySourcePos <|> byMaybeModuleName' - where - byModule = do - (mn, a) <- parseJSON2 v - pure $ Qualified (ByModuleName mn) a - bySourcePos = do - (ss, a) <- parseJSON2 v - pure $ Qualified (BySourcePos ss) a - byMaybeModuleName' = do - (mn, a) <- parseJSON2 v - pure $ Qualified (byMaybeModuleName mn) a - -instance ToJSON ModuleName where - toJSON (ModuleName name) = toJSON (T.splitOn "." name) - -instance FromJSON ModuleName where - parseJSON = withArray "ModuleName" $ \names -> do - names' <- traverse parseJSON names - pure (ModuleName (T.intercalate "." (V.toList names'))) - -instance ToJSONKey ModuleName where - toJSONKey = contramap runModuleName toJSONKey - -instance FromJSONKey ModuleName where - fromJSONKey = fmap moduleNameFromString fromJSONKey - -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''InternalIdentData) -$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''Ident) diff --git a/claude-help/original-compiler/src/Language/PureScript/Options.hs b/claude-help/original-compiler/src/Language/PureScript/Options.hs deleted file mode 100644 index d94d344c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Options.hs +++ /dev/null @@ -1,32 +0,0 @@ --- | The data type of compiler options -module Language.PureScript.Options where - -import Prelude -import Data.Set qualified as S -import Data.Map (Map) -import Data.Map qualified as Map - --- | The data type of compiler options -data Options = Options - { optionsVerboseErrors :: Bool - -- ^ Verbose error message - , optionsNoComments :: Bool - -- ^ Remove the comments from the generated js - , optionsCodegenTargets :: S.Set CodegenTarget - -- ^ Codegen targets (JS, CoreFn, etc.) - } deriving Show - --- Default make options -defaultOptions :: Options -defaultOptions = Options False False (S.singleton JS) - -data CodegenTarget = JS | JSSourceMap | CoreFn | Docs - deriving (Eq, Ord, Show) - -codegenTargets :: Map String CodegenTarget -codegenTargets = Map.fromList - [ ("js", JS) - , ("sourcemaps", JSSourceMap) - , ("corefn", CoreFn) - , ("docs", Docs) - ] diff --git a/claude-help/original-compiler/src/Language/PureScript/PSString.hs b/claude-help/original-compiler/src/Language/PureScript/PSString.hs deleted file mode 100644 index 2ceb4811..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/PSString.hs +++ /dev/null @@ -1,240 +0,0 @@ -module Language.PureScript.PSString - ( PSString - , toUTF16CodeUnits - , decodeString - , decodeStringEither - , decodeStringWithReplacement - , prettyPrintString - , prettyPrintStringJS - , mkString - ) where - -import Prelude -import GHC.Generics (Generic) -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Control.Exception (try, evaluate) -import Control.Applicative ((<|>)) -import Data.Char qualified as Char -import Data.Bits (shiftR) -import Data.Either (fromRight) -import Data.List (unfoldr) -import Data.Scientific (toBoundedInteger) -import Data.String (IsString(..)) -import Data.ByteString (ByteString) -import Data.ByteString qualified as BS -import Data.Text (Text) -import Data.Text qualified as T -import Data.Text.Encoding (decodeUtf16BE) -import Data.Text.Encoding.Error (UnicodeException) -import Data.Vector qualified as V -import Data.Word (Word16, Word8) -import Numeric (showHex) -import System.IO.Unsafe (unsafePerformIO) -import Data.Aeson qualified as A -import Data.Aeson.Types qualified as A - --- | --- Strings in PureScript are sequences of UTF-16 code units, which do not --- necessarily represent UTF-16 encoded text. For example, it is permissible --- for a string to contain *lone surrogates,* i.e. characters in the range --- U+D800 to U+DFFF which do not appear as a part of a surrogate pair. --- --- The Show instance for PSString produces a string literal which would --- represent the same data were it inserted into a PureScript source file. --- --- Because JSON parsers vary wildly in terms of how they deal with lone --- surrogates in JSON strings, the ToJSON instance for PSString produces JSON --- strings where that would be safe (i.e. when there are no lone surrogates), --- and arrays of UTF-16 code units (integers) otherwise. --- -newtype PSString = PSString { toUTF16CodeUnits :: [Word16] } - deriving (Eq, Ord, Semigroup, Monoid, Generic) - -instance NFData PSString -instance Serialise PSString - -instance Show PSString where - show = show . codePoints - --- | --- Decode a PSString to a String, representing any lone surrogates as the --- reserved code point with that index. Warning: if there are any lone --- surrogates, converting the result to Text via Data.Text.pack will result in --- loss of information as those lone surrogates will be replaced with U+FFFD --- REPLACEMENT CHARACTER. Because this function requires care to use correctly, --- we do not export it. --- -codePoints :: PSString -> String -codePoints = map (either (Char.chr . fromIntegral) id) . decodeStringEither - --- | --- Decode a PSString as UTF-16 text. Lone surrogates will be replaced with --- U+FFFD REPLACEMENT CHARACTER --- -decodeStringWithReplacement :: PSString -> String -decodeStringWithReplacement = map (fromRight '\xFFFD') . decodeStringEither - --- | --- Decode a PSString as UTF-16. Lone surrogates in the input are represented in --- the output with the Left constructor; characters which were successfully --- decoded are represented with the Right constructor. --- -decodeStringEither :: PSString -> [Either Word16 Char] -decodeStringEither = unfoldr decode . toUTF16CodeUnits - where - decode :: [Word16] -> Maybe (Either Word16 Char, [Word16]) - decode (h:l:rest) | isLead h && isTrail l = Just (Right (unsurrogate h l), rest) - decode (c:rest) | isSurrogate c = Just (Left c, rest) - decode (c:rest) = Just (Right (toChar c), rest) - decode [] = Nothing - - unsurrogate :: Word16 -> Word16 -> Char - unsurrogate h l = toEnum ((toInt h - 0xD800) * 0x400 + (toInt l - 0xDC00) + 0x10000) - --- | --- Attempt to decode a PSString as UTF-16 text. This will fail (returning --- Nothing) if the argument contains lone surrogates. --- -decodeString :: PSString -> Maybe Text -decodeString = hush . decodeEither . BS.pack . concatMap unpair . toUTF16CodeUnits - where - unpair w = [highByte w, lowByte w] - - lowByte :: Word16 -> Word8 - lowByte = fromIntegral - - highByte :: Word16 -> Word8 - highByte = fromIntegral . (`shiftR` 8) - - -- Based on a similar function from Data.Text.Encoding for utf8. This is a - -- safe usage of unsafePerformIO because there are no side effects after - -- handling any thrown UnicodeExceptions. - decodeEither :: ByteString -> Either UnicodeException Text - decodeEither = unsafePerformIO . try . evaluate . decodeUtf16BE - - hush = either (const Nothing) Just - -instance IsString PSString where - fromString a = PSString $ concatMap encodeUTF16 a - where - surrogates :: Char -> (Word16, Word16) - surrogates c = (toWord (h + 0xD800), toWord (l + 0xDC00)) - where (h, l) = divMod (fromEnum c - 0x10000) 0x400 - - encodeUTF16 :: Char -> [Word16] - encodeUTF16 c | fromEnum c > 0xFFFF = [high, low] - where (high, low) = surrogates c - encodeUTF16 c = [toWord $ fromEnum c] - -instance A.ToJSON PSString where - toJSON str = - case decodeString str of - Just t -> A.toJSON t - Nothing -> A.toJSON (toUTF16CodeUnits str) - -instance A.FromJSON PSString where - parseJSON a = jsonString <|> arrayOfCodeUnits - where - jsonString = fromString <$> A.parseJSON a - - arrayOfCodeUnits = PSString <$> parseArrayOfCodeUnits a - - parseArrayOfCodeUnits :: A.Value -> A.Parser [Word16] - parseArrayOfCodeUnits = A.withArray "array of UTF-16 code units" (traverse parseCodeUnit . V.toList) - - parseCodeUnit :: A.Value -> A.Parser Word16 - parseCodeUnit b = A.withScientific "two-byte non-negative integer" (maybe (A.typeMismatch "" b) return . toBoundedInteger) b - --- | --- Pretty print a PSString, using PureScript escape sequences. --- -prettyPrintString :: PSString -> Text -prettyPrintString s = "\"" <> foldMap encodeChar (decodeStringEither s) <> "\"" - where - encodeChar :: Either Word16 Char -> Text - encodeChar (Left c) = "\\x" <> showHex' 6 c - encodeChar (Right c) - | c == '\t' = "\\t" - | c == '\r' = "\\r" - | c == '\n' = "\\n" - | c == '"' = "\\\"" - | c == '\'' = "\\\'" - | c == '\\' = "\\\\" - | shouldPrint c = T.singleton c - | otherwise = "\\x" <> showHex' 6 (Char.ord c) - - -- Note we do not use Data.Char.isPrint here because that includes things - -- like zero-width spaces and combining punctuation marks, which could be - -- confusing to print unescaped. - shouldPrint :: Char -> Bool - -- The standard space character, U+20 SPACE, is the only space char we should - -- print without escaping - shouldPrint ' ' = True - shouldPrint c = - Char.generalCategory c `elem` - [ Char.UppercaseLetter - , Char.LowercaseLetter - , Char.TitlecaseLetter - , Char.OtherLetter - , Char.DecimalNumber - , Char.LetterNumber - , Char.OtherNumber - , Char.ConnectorPunctuation - , Char.DashPunctuation - , Char.OpenPunctuation - , Char.ClosePunctuation - , Char.InitialQuote - , Char.FinalQuote - , Char.OtherPunctuation - , Char.MathSymbol - , Char.CurrencySymbol - , Char.ModifierSymbol - , Char.OtherSymbol - ] - --- | --- Pretty print a PSString, using JavaScript escape sequences. Intended for --- use in compiled JS output. --- -prettyPrintStringJS :: PSString -> Text -prettyPrintStringJS s = "\"" <> foldMap encodeChar (toUTF16CodeUnits s) <> "\"" - where - encodeChar :: Word16 -> Text - encodeChar c | c > 0xFF = "\\u" <> showHex' 4 c - encodeChar c | c > 0x7E || c < 0x20 = "\\x" <> showHex' 2 c - encodeChar c | toChar c == '\b' = "\\b" - encodeChar c | toChar c == '\t' = "\\t" - encodeChar c | toChar c == '\n' = "\\n" - encodeChar c | toChar c == '\v' = "\\v" - encodeChar c | toChar c == '\f' = "\\f" - encodeChar c | toChar c == '\r' = "\\r" - encodeChar c | toChar c == '"' = "\\\"" - encodeChar c | toChar c == '\\' = "\\\\" - encodeChar c = T.singleton $ toChar c - -showHex' :: Enum a => Int -> a -> Text -showHex' width c = - let hs = showHex (fromEnum c) "" in - T.pack (replicate (width - length hs) '0' <> hs) - -isLead :: Word16 -> Bool -isLead h = h >= 0xD800 && h <= 0xDBFF - -isTrail :: Word16 -> Bool -isTrail l = l >= 0xDC00 && l <= 0xDFFF - -isSurrogate :: Word16 -> Bool -isSurrogate c = isLead c || isTrail c - -toChar :: Word16 -> Char -toChar = toEnum . fromIntegral - -toWord :: Int -> Word16 -toWord = fromIntegral - -toInt :: Word16 -> Int -toInt = fromIntegral - -mkString :: Text -> PSString -mkString = fromString . T.unpack diff --git a/claude-help/original-compiler/src/Language/PureScript/Pretty.hs b/claude-help/original-compiler/src/Language/PureScript/Pretty.hs deleted file mode 100644 index 87c42cf7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Pretty.hs +++ /dev/null @@ -1,12 +0,0 @@ --- | A collection of pretty printers for core data types: --- --- * [@Language.PureScript.Pretty.Kinds@] Pretty printer for kinds --- --- * [@Language.PureScript.Pretty.Values@] Pretty printer for values --- --- * [@Language.PureScript.Pretty.Types@] Pretty printer for types -module Language.PureScript.Pretty (module P) where - -import Language.PureScript.Pretty.Types as P -import Language.PureScript.Pretty.Values as P -import Language.PureScript.PSString as P (prettyPrintString) diff --git a/claude-help/original-compiler/src/Language/PureScript/Pretty/Common.hs b/claude-help/original-compiler/src/Language/PureScript/Pretty/Common.hs deleted file mode 100644 index a62e776c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Pretty/Common.hs +++ /dev/null @@ -1,138 +0,0 @@ --- | --- Common pretty-printing utility functions --- -module Language.PureScript.Pretty.Common where - -import Prelude - -import Control.Monad.State (StateT, modify, get) - -import Data.List (elemIndices, intersperse) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.AST (SourcePos(..), SourceSpan(..), nullSourceSpan) -import Language.PureScript.CST.Lexer (isUnquotedKey) - -import Text.PrettyPrint.Boxes (Box(..), emptyBox, text, top, vcat, (//)) -import Text.PrettyPrint.Boxes qualified as Box - -parensT :: Text -> Text -parensT s = "(" <> s <> ")" - -parensPos :: (Emit gen) => gen -> gen -parensPos s = emit "(" <> s <> emit ")" - --- | --- Generalize intercalate slightly for monoids --- -intercalate :: Monoid m => m -> [m] -> m -intercalate x xs = mconcat (intersperse x xs) - -class (Monoid gen) => Emit gen where - emit :: Text -> gen - addMapping :: SourceSpan -> gen - -data SMap = SMap Text SourcePos SourcePos - --- | --- String with length and source-map entries --- -newtype StrPos = StrPos (SourcePos, Text, [SMap]) - --- | --- Make a monoid where append consists of concatenating the string part, adding the lengths --- appropriately and advancing source mappings on the right hand side to account for --- the length of the left. --- -instance Semigroup StrPos where - StrPos (a,b,c) <> StrPos (a',b',c') = StrPos (a `addPos` a', b <> b', c ++ (bumpPos a <$> c')) - -instance Monoid StrPos where - mempty = StrPos (SourcePos 0 0, "", []) - - mconcat ms = - let s' = foldMap (\(StrPos(_, s, _)) -> s) ms - (p, maps) = foldl plus (SourcePos 0 0, []) ms - in - StrPos (p, s', concat $ reverse maps) - where - plus :: (SourcePos, [[SMap]]) -> StrPos -> (SourcePos, [[SMap]]) - plus (a, c) (StrPos (a', _, c')) = (a `addPos` a', (bumpPos a <$> c') : c) - -instance Emit StrPos where - -- Augment a string with its length (rows/column) - emit str = - -- TODO(Christoph): get rid of T.unpack - let newlines = elemIndices '\n' (T.unpack str) - index = if null newlines then 0 else last newlines + 1 - in - StrPos (SourcePos { sourcePosLine = length newlines, sourcePosColumn = T.length str - index }, str, []) - - -- Add a new mapping entry for given source position with initially zero generated position - addMapping ss@SourceSpan { spanName = file, spanStart = startPos } = StrPos (zeroPos, mempty, [ mapping | ss /= nullSourceSpan ]) - where - mapping = SMap (T.pack file) startPos zeroPos - zeroPos = SourcePos 0 0 - -newtype PlainString = PlainString Text deriving (Semigroup, Monoid) - -runPlainString :: PlainString -> Text -runPlainString (PlainString s) = s - -instance Emit PlainString where - emit = PlainString - addMapping _ = mempty - -addMapping' :: (Emit gen) => Maybe SourceSpan -> gen -addMapping' (Just ss) = addMapping ss -addMapping' Nothing = mempty - -bumpPos :: SourcePos -> SMap -> SMap -bumpPos p (SMap f s g) = SMap f s $ p `addPos` g - -addPos :: SourcePos -> SourcePos -> SourcePos -addPos (SourcePos n m) (SourcePos 0 m') = SourcePos n (m + m') -addPos (SourcePos n _) (SourcePos n' m') = SourcePos (n + n') m' - - -data PrinterState = PrinterState { indent :: Int } - --- | --- Number of characters per indentation level --- -blockIndent :: Int -blockIndent = 4 - --- | --- Pretty print with a new indentation level --- -withIndent :: StateT PrinterState Maybe gen -> StateT PrinterState Maybe gen -withIndent action = do - modify $ \st -> st { indent = indent st + blockIndent } - result <- action - modify $ \st -> st { indent = indent st - blockIndent } - return result - --- | --- Get the current indentation level --- -currentIndent :: (Emit gen) => StateT PrinterState Maybe gen -currentIndent = do - current <- get - return $ emit $ T.replicate (indent current) " " - -objectKeyRequiresQuoting :: Text -> Bool -objectKeyRequiresQuoting = not . isUnquotedKey - --- | Place a box before another, vertically when the first box takes up multiple lines. -before :: Box -> Box -> Box -before b1 b2 | rows b1 > 1 = b1 // b2 - | otherwise = b1 Box.<> b2 - -beforeWithSpace :: Box -> Box -> Box -beforeWithSpace b1 = before (b1 Box.<> text " ") - --- | Place a Box on the bottom right of another -endWith :: Box -> Box -> Box -endWith l r = l Box.<> vcat top [emptyBox (rows l - 1) (cols r), r] diff --git a/claude-help/original-compiler/src/Language/PureScript/Pretty/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Pretty/Types.hs deleted file mode 100644 index 9b3be469..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Pretty/Types.hs +++ /dev/null @@ -1,310 +0,0 @@ --- | --- Pretty printer for Types --- -module Language.PureScript.Pretty.Types - ( PrettyPrintType(..) - , PrettyPrintConstraint - , convertPrettyPrintType - , typeAsBox - , typeDiffAsBox - , prettyPrintType - , prettyPrintTypeWithUnicode - , prettyPrintSuggestedType - , typeAtomAsBox - , prettyPrintTypeAtom - , prettyPrintLabel - , prettyPrintObjectKey - ) where - -import Prelude hiding ((<>)) - -import Control.Arrow ((<+>)) -import Control.Lens (_2, (%~)) -import Control.PatternArrows as PA - -import Data.Maybe (fromMaybe, catMaybes) -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (tyFunction, tyRecord) -import Language.PureScript.Names (OpName(..), OpNameType(..), ProperName(..), ProperNameType(..), Qualified, coerceProperName, disqualify, showQualified) -import Language.PureScript.Pretty.Common (before, objectKeyRequiresQuoting) -import Language.PureScript.Types (Constraint(..), pattern REmptyKinded, RowListItem(..), Type(..), TypeVarVisibility(..), WildcardData(..), eqType, rowToSortedList, typeVarVisibilityPrefix) -import Language.PureScript.PSString (PSString, prettyPrintString, decodeString) -import Language.PureScript.Label (Label(..)) - -import Text.PrettyPrint.Boxes (Box(..), hcat, hsep, left, moveRight, nullBox, render, text, top, vcat, (<>)) - -data PrettyPrintType - = PPTUnknown Int - | PPTypeVar Text (Maybe Text) - | PPTypeLevelString PSString - | PPTypeLevelInt Integer - | PPTypeWildcard (Maybe Text) - | PPTypeConstructor (Qualified (ProperName 'TypeName)) - | PPTypeOp (Qualified (OpName 'TypeOpName)) - | PPSkolem Text Int - | PPTypeApp PrettyPrintType PrettyPrintType - | PPKindArg PrettyPrintType - | PPConstrainedType PrettyPrintConstraint PrettyPrintType - | PPKindedType PrettyPrintType PrettyPrintType - | PPBinaryNoParensType PrettyPrintType PrettyPrintType PrettyPrintType - | PPParensInType PrettyPrintType - | PPForAll [(TypeVarVisibility, Text, Maybe PrettyPrintType)] PrettyPrintType - | PPFunction PrettyPrintType PrettyPrintType - | PPRecord [(Label, PrettyPrintType)] (Maybe PrettyPrintType) - | PPRow [(Label, PrettyPrintType)] (Maybe PrettyPrintType) - | PPTruncated - -type PrettyPrintConstraint = (Qualified (ProperName 'ClassName), [PrettyPrintType], [PrettyPrintType]) - -convertPrettyPrintType :: Int -> Type a -> PrettyPrintType -convertPrettyPrintType = go - where - go _ (TUnknown _ n) = PPTUnknown n - go _ (TypeVar _ t) = PPTypeVar t Nothing - go _ (TypeLevelString _ s) = PPTypeLevelString s - go _ (TypeLevelInt _ n) = PPTypeLevelInt n - go _ (TypeWildcard _ (HoleWildcard n)) = PPTypeWildcard (Just n) - go _ (TypeWildcard _ _) = PPTypeWildcard Nothing - go _ (TypeConstructor _ c) = PPTypeConstructor c - go _ (TypeOp _ o) = PPTypeOp o - go _ (Skolem _ t _ n _) = PPSkolem t n - go _ (REmpty _) = PPRow [] Nothing - -- Guard the remaining "complex" type atoms on the current depth value. The - -- prior constructors can all be printed simply so it's not really helpful to - -- truncate them. - go d _ | d < 0 = PPTruncated - go d (ConstrainedType _ (Constraint _ cls kargs args _) ty) = PPConstrainedType (cls, go (d-1) <$> kargs, go (d-1) <$> args) (go d ty) - go d (KindedType _ ty k) = PPKindedType (go (d-1) ty) (go (d-1) k) - go d (BinaryNoParensType _ ty1 ty2 ty3) = PPBinaryNoParensType (go (d-1) ty1) (go (d-1) ty2) (go (d-1) ty3) - go d (ParensInType _ ty) = PPParensInType (go (d-1) ty) - go d ty@RCons{} = uncurry PPRow (goRow d ty) - go d (ForAll _ vis v mbK ty _) = goForAll d [(vis, v, fmap (go (d-1)) mbK)] ty - go d (TypeApp _ a b) = goTypeApp d a b - go d (KindApp _ a b) = PPTypeApp (go (d-1) a) (PPKindArg (go (d-1) b)) - - goForAll d vs (ForAll _ vis v mbK ty _) = goForAll d ((vis, v, fmap (go (d-1)) mbK) : vs) ty - goForAll d vs ty = PPForAll (reverse vs) (go (d-1) ty) - - goRow d ty = - let (items, tail_) = rowToSortedList ty - in ( map (\item -> (rowListLabel item, go (d-1) (rowListType item))) items - , case tail_ of - REmptyKinded _ _ -> Nothing - _ -> Just (go (d-1) tail_) - ) - - goTypeApp d (TypeApp _ f a) b - | eqType f tyFunction = PPFunction (go (d-1) a) (go (d-1) b) - | otherwise = PPTypeApp (goTypeApp d f a) (go (d-1) b) - goTypeApp d o ty@RCons{} - | eqType o tyRecord = uncurry PPRecord (goRow d ty) - goTypeApp d a b = PPTypeApp (go (d-1) a) (go (d-1) b) - --- TODO(Christoph): get rid of T.unpack s - -constraintsAsBox :: TypeRenderOptions -> PrettyPrintConstraint -> Box -> Box -constraintsAsBox tro con ty = - constraintAsBox con `before` (" " <> text doubleRightArrow <> " " <> ty) - where - doubleRightArrow = if troUnicode tro then "⇒" else "=>" - -constraintAsBox :: PrettyPrintConstraint -> Box -constraintAsBox (pn, ks, tys) = typeAsBox' (foldl PPTypeApp (foldl (\a b -> PPTypeApp a (PPKindArg b)) (PPTypeConstructor (fmap coerceProperName pn)) ks) tys) - --- | --- Generate a pretty-printed string representing a Row --- -prettyPrintRowWith :: TypeRenderOptions -> Char -> Char -> [(Label, PrettyPrintType)] -> Maybe PrettyPrintType -> Box -prettyPrintRowWith tro open close labels rest = - case (labels, rest) of - ([], Nothing) -> - if troRowAsDiff tro then text [ open, ' ' ] <> text "..." <> text [ ' ', close ] else text [ open, close ] - ([], Just _) -> - text [ open, ' ' ] <> tailToPs rest <> text [ ' ', close ] - _ -> - vcat left $ - zipWith (\(nm, ty) i -> nameAndTypeToPs (if i == 0 then open else ',') nm ty) labels [0 :: Int ..] ++ - catMaybes [ rowDiff, pure $ tailToPs rest, pure $ text [close] ] - - where - nameAndTypeToPs :: Char -> Label -> PrettyPrintType -> Box - nameAndTypeToPs start name ty = text (start : ' ' : T.unpack (prettyPrintLabel name) ++ " " ++ doubleColon ++ " ") <> typeAsBox' ty - - doubleColon = if troUnicode tro then "∷" else "::" - - rowDiff = if troRowAsDiff tro then Just (text "...") else Nothing - - tailToPs :: Maybe PrettyPrintType -> Box - tailToPs Nothing = nullBox - tailToPs (Just other) = text "| " <> typeAsBox' other - -typeApp :: Pattern () PrettyPrintType (PrettyPrintType, PrettyPrintType) -typeApp = mkPattern match - where - match (PPTypeApp f x) = Just (f, x) - match _ = Nothing - -kindArg :: Pattern () PrettyPrintType ((), PrettyPrintType) -kindArg = mkPattern match - where - match (PPKindArg ty) = Just ((), ty) - match _ = Nothing - -appliedFunction :: Pattern () PrettyPrintType (PrettyPrintType, PrettyPrintType) -appliedFunction = mkPattern match - where - match (PPFunction arg ret) = Just (arg, ret) - match _ = Nothing - -kinded :: Pattern () PrettyPrintType (PrettyPrintType, PrettyPrintType) -kinded = mkPattern match - where - match (PPKindedType t k) = Just (t, k) - match _ = Nothing - -constrained :: Pattern () PrettyPrintType (PrettyPrintConstraint, PrettyPrintType) -constrained = mkPattern match - where - match (PPConstrainedType deps ty) = Just (deps, ty) - match _ = Nothing - -explicitParens :: Pattern () PrettyPrintType ((), PrettyPrintType) -explicitParens = mkPattern match - where - match (PPParensInType ty) = Just ((), ty) - match _ = Nothing - -matchTypeAtom :: TypeRenderOptions -> Pattern () PrettyPrintType Box -matchTypeAtom tro@TypeRenderOptions{troSuggesting = suggesting} = - typeLiterals <+> fmap ((`before` text ")") . (text "(" <>)) (matchType tro) - where - typeLiterals :: Pattern () PrettyPrintType Box - typeLiterals = mkPattern match where - match (PPTypeWildcard name) = Just $ text $ maybe "_" (('?' :) . T.unpack) name - match (PPTypeVar var _) = Just $ text $ T.unpack var - match (PPTypeLevelString s) = Just $ text $ T.unpack $ prettyPrintString s - match (PPTypeLevelInt n) = Just $ text $ show n - match (PPTypeConstructor ctor) = Just $ text $ T.unpack $ runProperName $ disqualify ctor - match (PPTUnknown u) - | suggesting = Just $ text "_" - | otherwise = Just $ text $ 't' : show u - match (PPSkolem name s) - | suggesting = Just $ text $ T.unpack name - | otherwise = Just $ text $ T.unpack name ++ show s - match (PPRecord labels tail_) = Just $ prettyPrintRowWith tro '{' '}' labels tail_ - match (PPRow labels tail_) = Just $ prettyPrintRowWith tro '(' ')' labels tail_ - match (PPBinaryNoParensType op l r) = - Just $ typeAsBox' l <> text " " <> typeAsBox' op <> text " " <> typeAsBox' r - match (PPTypeOp op) = Just $ text $ T.unpack $ showQualified runOpName op - match PPTruncated = Just $ text "..." - match _ = Nothing - -matchType :: TypeRenderOptions -> Pattern () PrettyPrintType Box -matchType tro = buildPrettyPrinter operators (matchTypeAtom tro) where - operators :: OperatorTable () PrettyPrintType Box - operators = - OperatorTable [ [ Wrap kindArg $ \_ ty -> text "@" <> ty ] - , [ AssocL typeApp $ \f x -> keepSingleLinesOr (moveRight 2) f x ] - , [ AssocR appliedFunction $ \arg ret -> keepSingleLinesOr id arg (text rightArrow <> " " <> ret) ] - , [ Wrap constrained $ \deps ty -> constraintsAsBox tro deps ty ] - , [ Wrap forall_ $ \idents ty -> keepSingleLinesOr (moveRight 2) (hsep 1 top (text forall' : fmap printMbKindedType idents) <> text ".") ty ] - , [ Wrap kinded $ \ty k -> keepSingleLinesOr (moveRight 2) (typeAsBox' ty) (text (doubleColon ++ " ") <> k) ] - , [ Wrap explicitParens $ \_ ty -> ty ] - ] - - rightArrow = if troUnicode tro then "→" else "->" - forall' = if troUnicode tro then "∀" else "forall" - doubleColon = if troUnicode tro then "∷" else "::" - - printMbKindedType (vis, v, Nothing) = text (T.unpack $ typeVarVisibilityPrefix vis) <> text v - printMbKindedType (vis, v, Just k) = text ("(" ++ T.unpack (typeVarVisibilityPrefix vis) ++ v ++ " " ++ doubleColon ++ " ") <> typeAsBox' k <> text ")" - - -- If both boxes span a single line, keep them on the same line, or else - -- use the specified function to modify the second box, then combine vertically. - keepSingleLinesOr :: (Box -> Box) -> Box -> Box -> Box - keepSingleLinesOr f b1 b2 - | rows b1 > 1 || rows b2 > 1 = vcat left [ b1, f b2 ] - | otherwise = hcat top [ b1, text " ", b2] - -forall_ :: Pattern () PrettyPrintType ([(TypeVarVisibility, String, Maybe PrettyPrintType)], PrettyPrintType) -forall_ = mkPattern match - where - match (PPForAll idents ty) = Just ((_2 %~ T.unpack) <$> idents, ty) - match _ = Nothing - -typeAtomAsBox' :: PrettyPrintType -> Box -typeAtomAsBox' - = fromMaybe (internalError "Incomplete pattern") - . PA.pattern_ (matchTypeAtom defaultOptions) () - -typeAtomAsBox :: Int -> Type a -> Box -typeAtomAsBox maxDepth = typeAtomAsBox' . convertPrettyPrintType maxDepth - --- | Generate a pretty-printed string representing a Type, as it should appear inside parentheses -prettyPrintTypeAtom :: Int -> Type a -> String -prettyPrintTypeAtom maxDepth = render . typeAtomAsBox maxDepth - -typeAsBox' :: PrettyPrintType -> Box -typeAsBox' = typeAsBoxImpl defaultOptions - -typeAsBox :: Int -> Type a -> Box -typeAsBox maxDepth = typeAsBox' . convertPrettyPrintType maxDepth - -typeDiffAsBox' :: PrettyPrintType -> Box -typeDiffAsBox' = typeAsBoxImpl diffOptions - -typeDiffAsBox :: Int -> Type a -> Box -typeDiffAsBox maxDepth = typeDiffAsBox' . convertPrettyPrintType maxDepth - -data TypeRenderOptions = TypeRenderOptions - { troSuggesting :: Bool - , troUnicode :: Bool - , troRowAsDiff :: Bool - } - -suggestingOptions :: TypeRenderOptions -suggestingOptions = TypeRenderOptions True False False - -defaultOptions :: TypeRenderOptions -defaultOptions = TypeRenderOptions False False False - -diffOptions :: TypeRenderOptions -diffOptions = TypeRenderOptions False False True - -unicodeOptions :: TypeRenderOptions -unicodeOptions = TypeRenderOptions False True False - -typeAsBoxImpl :: TypeRenderOptions -> PrettyPrintType -> Box -typeAsBoxImpl tro - = fromMaybe (internalError "Incomplete pattern") - . PA.pattern_ (matchType tro) () - --- | Generate a pretty-printed string representing a 'Type' -prettyPrintType :: Int -> Type a -> String -prettyPrintType = flip prettyPrintType' defaultOptions - --- | Generate a pretty-printed string representing a 'Type' using unicode --- symbols where applicable -prettyPrintTypeWithUnicode :: Int -> Type a -> String -prettyPrintTypeWithUnicode = flip prettyPrintType' unicodeOptions - --- | Generate a pretty-printed string representing a suggested 'Type' -prettyPrintSuggestedType :: Type a -> String -prettyPrintSuggestedType = prettyPrintType' maxBound suggestingOptions - -prettyPrintType' :: Int -> TypeRenderOptions -> Type a -> String -prettyPrintType' maxDepth tro = render . typeAsBoxImpl tro . convertPrettyPrintType maxDepth - -prettyPrintLabel :: Label -> Text -prettyPrintLabel (Label s) = - case decodeString s of - Just s' | not (objectKeyRequiresQuoting s') -> - s' - _ -> - prettyPrintString s - -prettyPrintObjectKey :: PSString -> Text -prettyPrintObjectKey = prettyPrintLabel . Label diff --git a/claude-help/original-compiler/src/Language/PureScript/Pretty/Values.hs b/claude-help/original-compiler/src/Language/PureScript/Pretty/Values.hs deleted file mode 100644 index 4d5a5ec6..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Pretty/Values.hs +++ /dev/null @@ -1,230 +0,0 @@ --- | --- Pretty printer for values --- -module Language.PureScript.Pretty.Values - ( prettyPrintValue - , prettyPrintBinder - , prettyPrintBinderAtom - ) where - -import Prelude hiding ((<>)) - -import Control.Arrow (second) - -import Data.Text (Text) -import Data.List.NonEmpty qualified as NEL -import Data.Monoid qualified as Monoid ((<>)) -import Data.Text qualified as T - -import Language.PureScript.AST (AssocList(..), Binder(..), CaseAlternative(..), Declaration(..), DoNotationElement(..), Expr(..), Guard(..), GuardedExpr(..), Literal(..), PathNode(..), PathTree(..), TypeDeclarationData(..), pattern ValueDecl, WhereProvenance(..)) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Names (OpName(..), ProperName(..), Qualified(..), disqualify, runModuleName, showIdent) -import Language.PureScript.Pretty.Common (before, beforeWithSpace, parensT) -import Language.PureScript.Pretty.Types (typeAsBox, typeAtomAsBox, prettyPrintObjectKey) -import Language.PureScript.Types (Constraint(..)) -import Language.PureScript.PSString (PSString, prettyPrintString) - -import Text.PrettyPrint.Boxes (Box, left, moveRight, text, vcat, vsep, (//), (<>)) - --- TODO(Christoph): remove T.unpack s - -textT :: Text -> Box -textT = text . T.unpack - --- | Render an aligned list of items separated with commas -list :: Char -> Char -> (a -> Box) -> [a] -> Box -list open close _ [] = text [open, close] -list open close f xs = vcat left (zipWith toLine [0 :: Int ..] xs ++ [ text [ close ] ]) - where - toLine i a = text [ if i == 0 then open else ',', ' ' ] <> f a - -ellipsis :: Box -ellipsis = text "..." - -prettyPrintObject :: Int -> [(PSString, Maybe Expr)] -> Box -prettyPrintObject d = list '{' '}' prettyPrintObjectProperty - where - prettyPrintObjectProperty :: (PSString, Maybe Expr) -> Box - prettyPrintObjectProperty (key, value) = textT (prettyPrintObjectKey key Monoid.<> ": ") <> maybe (text "_") (prettyPrintValue (d - 1)) value - -prettyPrintUpdateEntry :: Int -> PSString -> Expr -> Box -prettyPrintUpdateEntry d key val = textT (prettyPrintObjectKey key) <> text " = " <> prettyPrintValue (d - 1) val - --- | Pretty-print an expression -prettyPrintValue :: Int -> Expr -> Box -prettyPrintValue d _ | d < 0 = text "..." -prettyPrintValue d (IfThenElse cond th el) = - (text "if " <> prettyPrintValueAtom (d - 1) cond) - // moveRight 2 (vcat left [ text "then " <> prettyPrintValueAtom (d - 1) th - , text "else " <> prettyPrintValueAtom (d - 1) el - ]) -prettyPrintValue d (Accessor prop val) = prettyPrintValueAtom (d - 1) val `before` textT ("." Monoid.<> prettyPrintObjectKey prop) -prettyPrintValue d (ObjectUpdate o ps) = prettyPrintValueAtom (d - 1) o `beforeWithSpace` list '{' '}' (uncurry (prettyPrintUpdateEntry d)) ps -prettyPrintValue d (ObjectUpdateNested o ps) = prettyPrintValueAtom (d - 1) o `beforeWithSpace` prettyPrintUpdate ps - where - prettyPrintUpdate (PathTree tree) = list '{' '}' printNode (runAssocList tree) - printNode (key, Leaf val) = prettyPrintUpdateEntry d key val - printNode (key, Branch val) = textT (prettyPrintObjectKey key) `beforeWithSpace` prettyPrintUpdate val -prettyPrintValue d (App val arg) = prettyPrintValueAtom (d - 1) val `beforeWithSpace` prettyPrintValueAtom (d - 1) arg -prettyPrintValue d (VisibleTypeApp val _) = prettyPrintValueAtom (d - 1) val -prettyPrintValue d (Unused val) = prettyPrintValue d val -prettyPrintValue d (Abs arg val) = text ('\\' : T.unpack (prettyPrintBinder arg) ++ " -> ") // moveRight 2 (prettyPrintValue (d - 1) val) -prettyPrintValue d (Case values binders) = - (text "case " <> foldr beforeWithSpace (text "of") (map (prettyPrintValueAtom (d - 1)) values)) // - moveRight 2 (vcat left (map (prettyPrintCaseAlternative (d - 1)) binders)) -prettyPrintValue d (Let FromWhere ds val) = - prettyPrintValue (d - 1) val // - moveRight 2 (text "where" // - vcat left (map (prettyPrintDeclaration (d - 1)) ds)) -prettyPrintValue d (Let FromLet ds val) = - text "let" // - moveRight 2 (vcat left (map (prettyPrintDeclaration (d - 1)) ds)) // - (text "in " <> prettyPrintValue (d - 1) val) -prettyPrintValue d (Do m els) = - textT (maybe "" ((Monoid.<> ".") . runModuleName) m) <> text "do " <> vcat left (map (prettyPrintDoNotationElement (d - 1)) els) -prettyPrintValue d (Ado m els yield) = - textT (maybe "" ((Monoid.<> ".") . runModuleName) m) <> text "ado " <> vcat left (map (prettyPrintDoNotationElement (d - 1)) els) // - (text "in " <> prettyPrintValue (d - 1) yield) --- TODO: constraint kind args -prettyPrintValue d (TypeClassDictionary (Constraint _ name _ tys _) _ _) = foldl1 beforeWithSpace $ text ("#dict " ++ T.unpack (runProperName (disqualify name))) : map (typeAtomAsBox d) tys -prettyPrintValue _ (DeferredDictionary name _) = text $ "#dict " ++ T.unpack (runProperName (disqualify name)) -prettyPrintValue _ (DerivedInstancePlaceholder name _) = text $ "#derived " ++ T.unpack (runProperName (disqualify name)) -prettyPrintValue d (TypedValue _ val _) = prettyPrintValue d val -prettyPrintValue d (PositionedValue _ _ val) = prettyPrintValue d val -prettyPrintValue d (Literal _ l) = prettyPrintLiteralValue d l -prettyPrintValue _ (Hole name) = text "?" <> textT name -prettyPrintValue d expr@AnonymousArgument{} = prettyPrintValueAtom d expr -prettyPrintValue d expr@Constructor{} = prettyPrintValueAtom d expr -prettyPrintValue d expr@Var{} = prettyPrintValueAtom d expr -prettyPrintValue d expr@Op{} = prettyPrintValueAtom d expr -prettyPrintValue d expr@BinaryNoParens{} = prettyPrintValueAtom d expr -prettyPrintValue d expr@Parens{} = prettyPrintValueAtom d expr -prettyPrintValue d expr@UnaryMinus{} = prettyPrintValueAtom d expr - --- | Pretty-print an atomic expression, adding parentheses if necessary. -prettyPrintValueAtom :: Int -> Expr -> Box -prettyPrintValueAtom d (Literal _ l) = prettyPrintLiteralValue d l -prettyPrintValueAtom _ AnonymousArgument = text "_" -prettyPrintValueAtom _ (Constructor _ name) = text $ T.unpack $ runProperName (disqualify name) -prettyPrintValueAtom _ (Var _ ident) = text $ T.unpack $ showIdent (disqualify ident) -prettyPrintValueAtom d (BinaryNoParens op lhs rhs) = - prettyPrintValue (d - 1) lhs `beforeWithSpace` printOp op `beforeWithSpace` prettyPrintValue (d - 1) rhs - where - printOp (Op _ (Qualified _ name)) = text $ T.unpack $ runOpName name - printOp expr = text "`" <> prettyPrintValue (d - 1) expr `before` text "`" -prettyPrintValueAtom d (TypedValue _ val _) = prettyPrintValueAtom d val -prettyPrintValueAtom d (PositionedValue _ _ val) = prettyPrintValueAtom d val -prettyPrintValueAtom d (Parens expr) = (text "(" <> prettyPrintValue d expr) `before` text ")" -prettyPrintValueAtom d (UnaryMinus _ expr) = text "(-" <> prettyPrintValue d expr <> text ")" -prettyPrintValueAtom d expr = (text "(" <> prettyPrintValue d expr) `before` text ")" - -prettyPrintLiteralValue :: Int -> Literal Expr -> Box -prettyPrintLiteralValue _ (NumericLiteral n) = text $ either show show n -prettyPrintLiteralValue _ (StringLiteral s) = text $ T.unpack $ prettyPrintString s -prettyPrintLiteralValue _ (CharLiteral c) = text $ show c -prettyPrintLiteralValue _ (BooleanLiteral True) = text "true" -prettyPrintLiteralValue _ (BooleanLiteral False) = text "false" -prettyPrintLiteralValue d (ArrayLiteral xs) = list '[' ']' (prettyPrintValue (d - 1)) xs -prettyPrintLiteralValue d (ObjectLiteral ps) = prettyPrintObject (d - 1) $ second Just `map` ps - -prettyPrintDeclaration :: Int -> Declaration -> Box -prettyPrintDeclaration d _ | d < 0 = ellipsis -prettyPrintDeclaration d (TypeDeclaration td) = - text (T.unpack (showIdent (tydeclIdent td)) ++ " :: ") <> typeAsBox d (tydeclType td) -prettyPrintDeclaration d (ValueDecl _ ident _ [] [GuardedExpr [] val]) = - text (T.unpack (showIdent ident) ++ " = ") <> prettyPrintValue (d - 1) val -prettyPrintDeclaration d (BindingGroupDeclaration ds) = - vsep 1 left (NEL.toList (fmap (prettyPrintDeclaration (d - 1) . toDecl) ds)) - where - toDecl ((sa, nm), t, e) = ValueDecl sa nm t [] [GuardedExpr [] e] -prettyPrintDeclaration _ _ = internalError "Invalid argument to prettyPrintDeclaration" - -prettyPrintCaseAlternative :: Int -> CaseAlternative -> Box -prettyPrintCaseAlternative d _ | d < 0 = ellipsis -prettyPrintCaseAlternative d (CaseAlternative binders result) = - text (T.unpack (T.unwords (map prettyPrintBinderAtom binders))) <> prettyPrintResult result - where - prettyPrintResult :: [GuardedExpr] -> Box - prettyPrintResult [GuardedExpr [] v] = text " -> " <> prettyPrintValue (d - 1) v - prettyPrintResult gs = - vcat left (map (prettyPrintGuardedValueSep (text " | ")) gs) - - prettyPrintGuardedValueSep :: Box -> GuardedExpr -> Box - prettyPrintGuardedValueSep _ (GuardedExpr [] val) = - text " -> " <> prettyPrintValue (d - 1) val - - prettyPrintGuardedValueSep sep (GuardedExpr [guard] val) = - foldl1 before [ sep - , prettyPrintGuard guard - , prettyPrintGuardedValueSep sep (GuardedExpr [] val) - ] - - prettyPrintGuardedValueSep sep (GuardedExpr (guard : guards) val) = - vcat left [ foldl1 before - [ sep - , prettyPrintGuard guard - ] - , prettyPrintGuardedValueSep (text " , ") (GuardedExpr guards val) - ] - - prettyPrintGuard (ConditionGuard cond) = - prettyPrintValue (d - 1) cond - prettyPrintGuard (PatternGuard binder val) = - foldl1 before - [ text (T.unpack (prettyPrintBinder binder)) - , text " <- " - , prettyPrintValue (d - 1) val - ] - -prettyPrintDoNotationElement :: Int -> DoNotationElement -> Box -prettyPrintDoNotationElement d _ | d < 0 = ellipsis -prettyPrintDoNotationElement d (DoNotationValue val) = - prettyPrintValue d val -prettyPrintDoNotationElement d (DoNotationBind binder val) = - textT (prettyPrintBinder binder Monoid.<> " <- ") <> prettyPrintValue d val -prettyPrintDoNotationElement d (DoNotationLet ds) = - text "let" // - moveRight 2 (vcat left (map (prettyPrintDeclaration (d - 1)) ds)) -prettyPrintDoNotationElement d (PositionedDoNotationElement _ _ el) = prettyPrintDoNotationElement d el - -prettyPrintBinderAtom :: Binder -> Text -prettyPrintBinderAtom NullBinder = "_" -prettyPrintBinderAtom (LiteralBinder _ l) = prettyPrintLiteralBinder l -prettyPrintBinderAtom (VarBinder _ ident) = showIdent ident -prettyPrintBinderAtom (ConstructorBinder _ ctor []) = runProperName (disqualify ctor) -prettyPrintBinderAtom b@ConstructorBinder{} = parensT (prettyPrintBinder b) -prettyPrintBinderAtom (NamedBinder _ ident binder) = showIdent ident Monoid.<> "@" Monoid.<> prettyPrintBinder binder -prettyPrintBinderAtom (PositionedBinder _ _ binder) = prettyPrintBinderAtom binder -prettyPrintBinderAtom (TypedBinder _ binder) = prettyPrintBinderAtom binder -prettyPrintBinderAtom (OpBinder _ op) = runOpName (disqualify op) -prettyPrintBinderAtom (BinaryNoParensBinder op b1 b2) = - prettyPrintBinderAtom b1 Monoid.<> " " Monoid.<> prettyPrintBinderAtom op Monoid.<> " " Monoid.<> prettyPrintBinderAtom b2 -prettyPrintBinderAtom (ParensInBinder b) = parensT (prettyPrintBinder b) - -prettyPrintLiteralBinder :: Literal Binder -> Text -prettyPrintLiteralBinder (StringLiteral str) = prettyPrintString str -prettyPrintLiteralBinder (CharLiteral c) = T.pack (show c) -prettyPrintLiteralBinder (NumericLiteral num) = either (T.pack . show) (T.pack . show) num -prettyPrintLiteralBinder (BooleanLiteral True) = "true" -prettyPrintLiteralBinder (BooleanLiteral False) = "false" -prettyPrintLiteralBinder (ObjectLiteral bs) = - "{ " - Monoid.<> T.intercalate ", " (map prettyPrintObjectPropertyBinder bs) - Monoid.<> " }" - where - prettyPrintObjectPropertyBinder :: (PSString, Binder) -> Text - prettyPrintObjectPropertyBinder (key, binder) = prettyPrintObjectKey key Monoid.<> ": " Monoid.<> prettyPrintBinder binder -prettyPrintLiteralBinder (ArrayLiteral bs) = - "[ " - Monoid.<> T.intercalate ", " (map prettyPrintBinder bs) - Monoid.<> " ]" - --- | --- Generate a pretty-printed string representing a Binder --- -prettyPrintBinder :: Binder -> Text -prettyPrintBinder (ConstructorBinder _ ctor []) = runProperName (disqualify ctor) -prettyPrintBinder (ConstructorBinder _ ctor args) = runProperName (disqualify ctor) Monoid.<> " " Monoid.<> T.unwords (map prettyPrintBinderAtom args) -prettyPrintBinder (PositionedBinder _ _ binder) = prettyPrintBinder binder -prettyPrintBinder (TypedBinder _ binder) = prettyPrintBinder binder -prettyPrintBinder b = prettyPrintBinderAtom b diff --git a/claude-help/original-compiler/src/Language/PureScript/Publish.hs b/claude-help/original-compiler/src/Language/PureScript/Publish.hs deleted file mode 100644 index ed3dd4ab..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Publish.hs +++ /dev/null @@ -1,390 +0,0 @@ -module Language.PureScript.Publish - ( preparePackage - , preparePackage' - , unsafePreparePackage - , PrepareM() - , runPrepareM - , warn - , userError - , internalError - , otherError - , PublishOptions(..) - , defaultPublishOptions - , getGitWorkingTreeStatus - , checkCleanWorkingTree - , getVersionFromGitTag - , getManifestRepositoryInfo - , getModules - ) where - -import Protolude hiding (stdin, lines) - -import Control.Arrow ((***)) -import Control.Category ((>>>)) -import Control.Monad.Writer.Strict (MonadWriter, WriterT, runWriterT, tell) - -import Data.ByteString.Lazy qualified as BL -import Data.String (String, lines) -import Data.List (stripPrefix, (\\)) -import Data.Text qualified as T -import Data.Time.Clock (UTCTime) -import Data.Time.Clock.POSIX (posixSecondsToUTCTime) -import Data.Version (Version) -import Distribution.SPDX qualified as SPDX -import Distribution.Parsec qualified as CabalParsec - -import System.Directory (doesFileExist) -import System.FilePath.Glob (globDir1) -import System.Process (readProcess) - -import Web.Bower.PackageMeta (PackageMeta(..), PackageName, Repository(..)) -import Web.Bower.PackageMeta qualified as Bower - -import Language.PureScript.Publish.ErrorsWarnings (InternalError(..), OtherError(..), PackageError(..), PackageWarning(..), RepositoryFieldError(..), UserError(..), printError, printWarnings) -import Language.PureScript.Publish.Registry.Compat (asPursJson, toBowerPackage) -import Language.PureScript.Publish.Utils (globRelative, purescriptSourceFiles) -import Language.PureScript qualified as P (version, ModuleName) -import Language.PureScript.CoreFn.FromJSON qualified as P -import Language.PureScript.Docs qualified as D -import Data.Aeson.BetterErrors (Parse, withString, eachInObjectWithKey, asString, key, keyMay, parse, mapError) -import Language.PureScript.Docs.Types (ManifestError(BowerManifest, PursManifest)) - -data PublishOptions = PublishOptions - { -- | How to obtain the version tag and version that the data being - -- generated will refer to. - publishGetVersion :: PrepareM (Text, Version) - -- | How to obtain at what time the version was committed - , publishGetTagTime :: Text -> PrepareM UTCTime - , -- | What to do when the working tree is dirty - publishWorkingTreeDirty :: PrepareM () - , -- | Compiler output directory (which must include up-to-date docs.json - -- files for any modules we are producing docs for). - publishCompileOutputDir :: FilePath - , -- | Path to the manifest file; a JSON file including information about the - -- package, such as name, author, dependency version bounds. - publishManifestFile :: FilePath - , -- | Path to the resolutions file; a JSON file containing all of the - -- package's dependencies, their versions, and their paths on the disk. - publishResolutionsFile :: FilePath - } - -defaultPublishOptions :: PublishOptions -defaultPublishOptions = PublishOptions - { publishGetVersion = getVersionFromGitTag - , publishGetTagTime = getTagTime - , publishWorkingTreeDirty = userError DirtyWorkingTree - , publishCompileOutputDir = "output" - , publishManifestFile = "bower.json" - , publishResolutionsFile = "resolutions.json" - } - --- | Attempt to retrieve package metadata from the current directory. --- Calls exitFailure if no package metadata could be retrieved. -unsafePreparePackage :: PublishOptions -> IO D.UploadedPackage -unsafePreparePackage opts = - either (\e -> printError e >> exitFailure) pure - =<< preparePackage opts - --- | Attempt to retrieve package metadata from the current directory. --- Returns a PackageError on failure -preparePackage :: PublishOptions -> IO (Either PackageError D.UploadedPackage) -preparePackage opts = - runPrepareM (preparePackage' opts) - >>= either (pure . Left) (fmap Right . handleWarnings) - - where - handleWarnings (result, warns) = do - printWarnings warns - return result - -newtype PrepareM a = - PrepareM { unPrepareM :: WriterT [PackageWarning] (ExceptT PackageError IO) a } - deriving (Functor, Applicative, Monad, - MonadWriter [PackageWarning], - MonadError PackageError) - --- This MonadIO instance ensures that IO errors don't crash the program. -instance MonadIO PrepareM where - liftIO act = - lift' (try act) >>= either (otherError . IOExceptionThrown) return - where - lift' :: IO a -> PrepareM a - lift' = PrepareM . lift . lift - -runPrepareM :: PrepareM a -> IO (Either PackageError (a, [PackageWarning])) -runPrepareM = runExceptT . runWriterT . unPrepareM - -warn :: PackageWarning -> PrepareM () -warn w = tell [w] - -userError :: UserError -> PrepareM a -userError = throwError . UserError - -internalError :: InternalError -> PrepareM a -internalError = throwError . InternalError - -otherError :: OtherError -> PrepareM a -otherError = throwError . OtherError - -catchLeft :: Applicative f => Either a b -> (a -> f b) -> f b -catchLeft a f = either f pure a - -preparePackage' :: PublishOptions -> PrepareM D.UploadedPackage -preparePackage' opts = do - checkCleanWorkingTree opts - - let manifestPath = publishManifestFile opts - pkgMeta <- liftIO (try (BL.readFile manifestPath)) >>= \case - Left (_ :: IOException) -> - userError $ PackageManifestNotFound manifestPath - Right found -> do - -- We can determine the type of the manifest file based on the file path, - -- as both the PureScript and Bower registries require their manifest - -- files to have specific names. - let isPursJson = "purs.json" `T.isInfixOf` T.pack manifestPath - if isPursJson then do - pursJson <- catchLeft (parse (mapError PursManifest asPursJson) found) (userError . CouldntDecodePackageManifest) - catchLeft (toBowerPackage pursJson) (userError . CouldntConvertPackageManifest) - else - catchLeft (parse (mapError BowerManifest Bower.asPackageMeta) found) (userError . CouldntDecodePackageManifest) - - checkLicense pkgMeta - - (pkgVersionTag, pkgVersion) <- publishGetVersion opts - pkgTagTime <- Just <$> publishGetTagTime opts pkgVersionTag - pkgGithub <- getManifestRepositoryInfo pkgMeta - - resolvedDeps <- parseResolutionsFile (publishResolutionsFile opts) - - (pkgModules, pkgModuleMap) <- getModules opts (map (second fst) resolvedDeps) - - let declaredDeps = map fst $ Bower.bowerDependencies pkgMeta - pkgResolvedDependencies <- handleDeps declaredDeps (map (second snd) resolvedDeps) - - let pkgUploader = D.NotYetKnown - let pkgCompilerVersion = P.version - - return D.Package{..} - -getModules - :: PublishOptions - -> [(PackageName, FilePath)] - -> PrepareM ([D.Module], Map P.ModuleName PackageName) -getModules opts paths = do - (inputFiles, depsFiles) <- liftIO (getInputAndDepsFiles paths) - - (modules, moduleMap) <- - liftIO (runExceptT (D.collectDocs (publishCompileOutputDir opts) inputFiles depsFiles)) - >>= either (userError . CompileError) return - - pure (map snd modules, moduleMap) - -data TreeStatus = Clean | Dirty deriving (Show, Eq, Ord, Enum) - -getGitWorkingTreeStatus :: FilePath -> PrepareM TreeStatus -getGitWorkingTreeStatus manifestFilePath = do - output <- lines <$> readProcess' "git" ["status", "--porcelain"] "" - -- The PureScript registry generates purs.json files when publishing legacy - -- packages. To ensure these packages can also be published to Pursuit, we - -- include an exemption to the working tree status check that will ignore - -- untracked purs.json files. Note that _modified_ purs.json files will - -- still fail this check. - let untrackedPursJson = "?? " <> manifestFilePath - let filtered = filter (/= untrackedPursJson) output - return $ - if all null filtered - then Clean - else Dirty - -checkCleanWorkingTree :: PublishOptions -> PrepareM () -checkCleanWorkingTree opts = do - status <- getGitWorkingTreeStatus (publishManifestFile opts) - unless (status == Clean) $ - publishWorkingTreeDirty opts - -getVersionFromGitTag :: PrepareM (Text, Version) -getVersionFromGitTag = do - out <- readProcess' "git" ["tag", "--list", "--points-at", "HEAD"] "" - let vs = map trimWhitespace (lines out) - case mapMaybe parseMay vs of - [] -> userError TagMustBeCheckedOut - [x] -> return (first T.pack x) - xs -> userError (AmbiguousVersions (map snd xs)) - where - trimWhitespace = - dropWhile isSpace >>> reverse >>> dropWhile isSpace >>> reverse - parseMay str = do - digits <- stripPrefix "v" str - (str,) <$> P.parseVersion' digits - --- | Given a git tag, get the time it was created. -getTagTime :: Text -> PrepareM UTCTime -getTagTime tag = do - out <- readProcess' "git" ["log", "-1", "--format=%ct", T.unpack tag] "" - case mapMaybe readMaybe (lines out) of - [t] -> pure . posixSecondsToUTCTime . fromInteger $ t - _ -> internalError (CouldntParseGitTagDate tag) - -getManifestRepositoryInfo :: PackageMeta -> PrepareM (D.GithubUser, D.GithubRepo) -getManifestRepositoryInfo pkgMeta = - case bowerRepository pkgMeta of - Nothing -> do - giturl <- catchError (Just . T.strip . T.pack <$> readProcess' "git" ["config", "remote.origin.url"] "") - (const (return Nothing)) - userError (BadRepositoryField (RepositoryFieldMissing (giturl >>= extractGithub <&> format))) - Just Repository{..} -> do - unless (repositoryType == "git") - (userError (BadRepositoryField (BadRepositoryType repositoryType))) - maybe (userError (BadRepositoryField NotOnGithub)) return (extractGithub repositoryUrl) - - where - format :: (D.GithubUser, D.GithubRepo) -> Text - format (user, repo) = "https://github.com/" <> D.runGithubUser user <> "/" <> D.runGithubRepo repo <> ".git" - -checkLicense :: PackageMeta -> PrepareM () -checkLicense pkgMeta = - case bowerLicense pkgMeta of - [] -> - userError NoLicenseSpecified - ls -> - unless (any (isValidSPDX . T.unpack) ls) - (userError InvalidLicense) - --- | --- Check if a string is a valid SPDX license expression. --- -isValidSPDX :: String -> Bool -isValidSPDX input = case CabalParsec.simpleParsec input of - Nothing -> False - Just SPDX.NONE -> False - Just _ -> True - - -extractGithub :: Text -> Maybe (D.GithubUser, D.GithubRepo) -extractGithub = stripGitHubPrefixes - >>> fmap (T.splitOn "/") - >=> takeTwo - >>> fmap (D.GithubUser *** (D.GithubRepo . dropDotGit)) - - where - takeTwo :: [a] -> Maybe (a, a) - takeTwo [x, y] = Just (x, y) - takeTwo _ = Nothing - - stripGitHubPrefixes :: Text -> Maybe Text - stripGitHubPrefixes = stripPrefixes [ "git://github.com/" - , "https://github.com/" - , "git@github.com:" - ] - - stripPrefixes :: [Text] -> Text -> Maybe Text - stripPrefixes prefixes str = msum $ (`T.stripPrefix` str) <$> prefixes - - dropDotGit :: Text -> Text - dropDotGit str - | ".git" `T.isSuffixOf` str = T.take (T.length str - 4) str - | otherwise = str - -readProcess' :: String -> [String] -> String -> PrepareM String -readProcess' prog args stdin = do - out <- liftIO (catch (Right <$> readProcess prog args stdin) - (return . Left)) - either (otherError . ProcessFailed prog args) return out - -data DependencyStatus - = NoResolution - -- ^ In the resolutions file, there was no _resolution key. - | ResolvedOther Text - -- ^ Resolved, but to something other than a version. The Text argument - -- is the resolution type. The values it can take that I'm aware of are - -- "commit" and "branch". Note: this constructor is deprecated, and is only - -- used when parsing legacy resolutions files. - | ResolvedVersion Version - -- ^ Resolved to a version. - deriving (Show, Eq) - -parseResolutionsFile :: FilePath -> PrepareM [(PackageName, (FilePath, DependencyStatus))] -parseResolutionsFile resolutionsFile = do - unlessM (liftIO (doesFileExist resolutionsFile)) (userError ResolutionsFileNotFound) - depsBS <- liftIO (BL.readFile resolutionsFile) - - case parse asResolutions depsBS of - Right res -> - pure res - Left err -> - userError $ ResolutionsFileError resolutionsFile err - --- | Parser for resolutions files, which contain information about the packages --- which this package depends on. A resolutions file should look something like --- this: --- --- { --- "purescript-prelude": { --- "version": "4.0.0", --- "path": "bower_components/purescript-prelude" --- }, --- "purescript-lists": { --- "version": "6.0.0", --- "path": "bower_components/purescript-lists" --- }, --- ... --- } --- --- where the version is used for generating links between packages on Pursuit, --- and the path is used to obtain the source files while generating --- documentation: all files matching the glob "src/**/*.purs" relative to the --- `path` directory will be picked up. --- --- The "version" field is optional, but omitting it will mean that no links --- will be generated for any declarations from that package on Pursuit. The --- "path" field is required. -asResolutions :: Parse D.PackageError [(PackageName, (FilePath, DependencyStatus))] -asResolutions = - eachInObjectWithKey parsePackageName $ - (,) <$> key "path" asString - <*> (maybe NoResolution ResolvedVersion <$> keyMay "version" asVersion) - -asVersion :: Parse D.PackageError Version -asVersion = - withString (note D.InvalidVersion . P.parseVersion') - -parsePackageName :: Text -> Either D.PackageError PackageName -parsePackageName = first D.ErrorInPackageMeta . D.mapLeft BowerManifest . Bower.parsePackageName - -handleDeps - :: [PackageName] - -- ^ dependencies declared in package manifest file; we should emit - -- warnings for any package name in this list which is not in the - -- resolutions file. - -> [(PackageName, DependencyStatus)] - -- ^ Contents of resolutions file - -> PrepareM [(PackageName, Version)] -handleDeps declared resolutions = do - let missing = declared \\ map fst resolutions - case missing of - (x:xs) -> - userError (MissingDependencies (x :| xs)) - [] -> do - pkgs <- - for resolutions $ \(pkgName, status) -> - case status of - NoResolution -> do - warn (NoResolvedVersion pkgName) - pure Nothing - ResolvedOther other -> do - warn (UnacceptableVersion (pkgName, other)) - pure Nothing - ResolvedVersion version -> - pure (Just (pkgName, version)) - pure (catMaybes pkgs) - -getInputAndDepsFiles - :: [(PackageName, FilePath)] - -> IO ([FilePath], [(PackageName, FilePath)]) -getInputAndDepsFiles depPaths = do - inputFiles <- globRelative purescriptSourceFiles - let handleDep (pkgName, path) = do - depFiles <- globDir1 purescriptSourceFiles path - return (map (pkgName,) depFiles) - depFiles <- concat <$> traverse handleDep depPaths - return (inputFiles, depFiles) diff --git a/claude-help/original-compiler/src/Language/PureScript/Publish/BoxesHelpers.hs b/claude-help/original-compiler/src/Language/PureScript/Publish/BoxesHelpers.hs deleted file mode 100644 index 36d9a180..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Publish/BoxesHelpers.hs +++ /dev/null @@ -1,46 +0,0 @@ -module Language.PureScript.Publish.BoxesHelpers - ( Boxes.Box - , Boxes.nullBox - , module Language.PureScript.Publish.BoxesHelpers - ) where - -import Prelude - -import Data.Text (Text) -import Data.Text qualified as T -import System.IO (hPutStr, stderr) - -import Text.PrettyPrint.Boxes qualified as Boxes - -width :: Int -width = 79 - -indentWidth :: Int -indentWidth = 2 - -para :: String -> Boxes.Box -para = Boxes.para Boxes.left width - -indented :: Boxes.Box -> Boxes.Box -indented b = Boxes.hcat Boxes.left [Boxes.emptyBox 1 indentWidth, b] - -successivelyIndented :: [String] -> Boxes.Box -successivelyIndented [] = - Boxes.nullBox -successivelyIndented (x:xs) = - Boxes.vcat Boxes.left [para x, indented (successivelyIndented xs)] - -vcat :: [Boxes.Box] -> Boxes.Box -vcat = Boxes.vcat Boxes.left - -spacer :: Boxes.Box -spacer = Boxes.emptyBox 1 1 - -bulletedList :: (a -> String) -> [a] -> [Boxes.Box] -bulletedList f = map (indented . para . ("* " ++) . f) - -bulletedListT :: (a -> Text) -> [a] -> [Boxes.Box] -bulletedListT f = bulletedList (T.unpack . f) - -printToStderr :: Boxes.Box -> IO () -printToStderr = hPutStr stderr . Boxes.render diff --git a/claude-help/original-compiler/src/Language/PureScript/Publish/ErrorsWarnings.hs b/claude-help/original-compiler/src/Language/PureScript/Publish/ErrorsWarnings.hs deleted file mode 100644 index b855f68a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Publish/ErrorsWarnings.hs +++ /dev/null @@ -1,412 +0,0 @@ -module Language.PureScript.Publish.ErrorsWarnings - ( PackageError(..) - , PackageWarning(..) - , UserError(..) - , InternalError(..) - , OtherError(..) - , RepositoryFieldError(..) - , JSONSource(..) - , printError - , renderError - , printWarnings - , renderWarnings - ) where - -import Prelude - -import Control.Exception (IOException) - -import Data.Aeson.BetterErrors (ParseError, displayError) -import Data.List (intersperse) -import Data.List.NonEmpty (NonEmpty(..)) -import Data.Maybe (catMaybes, fromMaybe) -import Data.Monoid (Any(..)) -import Data.Version (Version, showVersion) -import Data.List.NonEmpty qualified as NonEmpty -import Data.Text (Text) -import Data.Text qualified as T - -import Language.PureScript.Docs.Types qualified as D -import Language.PureScript qualified as P -import Language.PureScript.Publish.BoxesHelpers (Box, bulletedList, bulletedListT, indented, nullBox, para, printToStderr, spacer, successivelyIndented, vcat) - -import Web.Bower.PackageMeta (PackageName, runPackageName, showBowerError) -import Web.Bower.PackageMeta qualified as Bower -import Language.PureScript.Docs.Types (showManifestError) - --- | An error which meant that it was not possible to retrieve metadata for a --- package. -data PackageError - = UserError UserError - | InternalError InternalError - | OtherError OtherError - deriving (Show) - -data PackageWarning - = NoResolvedVersion PackageName - | UnacceptableVersion (PackageName, Text) - | DirtyWorkingTreeWarn - deriving (Show) - --- | An error that should be fixed by the user. -data UserError - = PackageManifestNotFound FilePath - | ResolutionsFileNotFound - | CouldntConvertPackageManifest Bower.BowerError - | CouldntDecodePackageManifest (ParseError D.ManifestError) - | TagMustBeCheckedOut - | AmbiguousVersions [Version] -- Invariant: should contain at least two elements - | BadRepositoryField RepositoryFieldError - | NoLicenseSpecified - | InvalidLicense - | MissingDependencies (NonEmpty PackageName) - | CompileError P.MultipleErrors - | DirtyWorkingTree - | ResolutionsFileError FilePath (ParseError D.PackageError) - deriving (Show) - -data RepositoryFieldError - = RepositoryFieldMissing (Maybe Text) - | BadRepositoryType Text - | NotOnGithub - deriving (Show) - --- | An error that probably indicates a bug in this module. -data InternalError - = CouldntParseGitTagDate Text - deriving (Show) - -data JSONSource - = FromFile FilePath - | FromResolutions - deriving (Show) - -data OtherError - = ProcessFailed String [String] IOException - | IOExceptionThrown IOException - deriving (Show) - -printError :: PackageError -> IO () -printError = printToStderr . renderError - -renderError :: PackageError -> Box -renderError err = - case err of - UserError e -> - vcat - [ para ( - "There is a problem with your package, which meant that " ++ - "it could not be published." - ) - , para "Details:" - , indented (displayUserError e) - ] - InternalError e -> - vcat - [ para "Internal error: this is probably a bug. Please report it:" - , indented (para "https://github.com/purescript/purescript/issues/new") - , spacer - , para "Details:" - , successivelyIndented (displayInternalError e) - ] - OtherError e -> - vcat - [ para "An error occurred, and your package could not be published." - , para "Details:" - , indented (displayOtherError e) - ] - -displayUserError :: UserError -> Box -displayUserError e = case e of - PackageManifestNotFound path -> do - vcat - [ para "The package manifest file was not found:" - , indented (para path) - , spacer - , para "Please create either a bower.json or purs.json manifest file." - ] - ResolutionsFileNotFound -> - para "The resolutions file was not found." - CouldntConvertPackageManifest err -> - vcat - [ para "Unable to convert your package manifest file to the Bower format:" - , indented ((para . T.unpack) (showBowerError err)) - , spacer - , para "Please ensure that your package manifest file is valid." - ] - CouldntDecodePackageManifest err -> - vcat - [ para "There was a problem with your package manifest file:" - , indented (vcat (map (para . T.unpack) (displayError showManifestError err))) - , spacer - , para "Please ensure that your package manifest file is valid." - ] - TagMustBeCheckedOut -> - vcat - [ para (concat - [ "purs publish requires a tagged version to be checked out in " - , "order to build documentation, and no suitable tag was found. " - , "Please check out a previously tagged version, or tag a new " - , "version." - ]) - , spacer - , para "Note: tagged versions must be in the form" - , indented (para "v{MAJOR}.{MINOR}.{PATCH} (example: \"v1.6.2\")") - , spacer - , para (concat - [ "If the version you are publishing is not yet tagged, you might " - , "want to use the --dry-run flag instead, which removes this " - , "requirement. Run `purs publish --help` for more details." - ]) - ] - AmbiguousVersions vs -> - vcat $ - [ para (concat - [ "The currently checked out commit seems to have been tagged with " - , "more than 1 version, and I don't know which one should be used. " - , "Please either delete some of the tags, or create a new commit " - , "to tag the desired version with." - ]) - , spacer - , para "Tags for the currently checked out commit:" - ] ++ bulletedList showVersion vs - BadRepositoryField err -> - displayRepositoryError err - NoLicenseSpecified -> - vcat $ - [ para $ concat - [ "No license is specified in package manifest. Please add a " - , "\"license\" property with a SPDX license expression. For example, " - , "any of the following would be acceptable:" - ] - , spacer - ] ++ spdxExamples ++ - [ spacer - , para $ - "See https://spdx.org/licenses/ for a full list of licenses. For more " ++ - "information on SPDX license expressions, see https://spdx.org/ids-how" - , spacer - , para $ - "Note that distributing code without a license means that nobody will " ++ - "(legally) be able to use it." - , spacer - , para $ - "It is also recommended to add a LICENSE file to the repository, " ++ - "including your name and the current year, although this is not necessary." - ] - InvalidLicense -> - vcat $ - [ para $ concat - [ "The license specified in package manifest is not a valid SPDX " - , "license expression. Please update the \"license\" property so that " - , "it is a valid SPDX license expression. For example, any of the " - , "following would be acceptable:" - ] - , spacer - ] ++ - spdxExamples - MissingDependencies pkgs -> - let singular = NonEmpty.length pkgs == 1 - pl a b = if singular then b else a - do_ = pl "do" "does" - dependencies = pl "dependencies" "dependency" - in vcat $ - para (concat - [ "The following ", dependencies, " ", do_, " not appear to be " - , "installed:" - ]) : - bulletedListT runPackageName (NonEmpty.toList pkgs) - CompileError err -> - vcat - [ para "Compile error:" - , indented (vcat (P.prettyPrintMultipleErrorsBox P.defaultPPEOptions err)) - ] - DirtyWorkingTree -> - para ( - "Your git working tree is dirty. Please commit, discard, or stash " ++ - "your changes first." - ) - ResolutionsFileError path err -> - successivelyIndented $ - ("Error in resolutions file (" ++ path ++ "):") : - map T.unpack (displayError D.displayPackageError err) - -spdxExamples :: [Box] -spdxExamples = - map (indented . para) - [ "* \"MIT\"" - , "* \"Apache-2.0\"" - , "* \"BSD-2-Clause\"" - , "* \"GPL-2.0-or-later\"" - , "* \"(GPL-3.0-only OR MIT)\"" - ] - -displayRepositoryError :: RepositoryFieldError -> Box -displayRepositoryError err = case err of - RepositoryFieldMissing giturl -> - vcat - [ para (concat - [ "The 'repository' or 'location' field is not present in your package manifest file. " - , "Without this information, Pursuit would not be able to generate " - , "source links in your package's documentation. Please add one - like " - , "this, if you are using the bower.json format:" - ]) - , spacer - , indented (vcat - [ para "\"repository\": {" - , indented (para "\"type\": \"git\",") - , indented (para ("\"url\": \"" ++ T.unpack (fromMaybe "https://github.com/USER/REPO.git" giturl) ++ "\"")) - , para "}" - ] - ) - , para "or like this, if you are using the purs.json format:" - , spacer - , indented (vcat - [ para "\"location\": {" - , indented (para "\"githubOwner\": \"USER\",") - , indented (para "\"githubRepo\": \"REPO\",") - , para "}" - ] - ) - ] - BadRepositoryType ty -> - para (concat - [ "In your package manifest file, the repository type is currently listed as " - , "\"" ++ T.unpack ty ++ "\". Currently, only git repositories are supported. " - , "Please publish your code in a git repository, and then update the " - , "repository type in your package manifest file to \"git\"." - ]) - NotOnGithub -> - vcat - [ para (concat - [ "The repository url in your package manifest file does not point to a " - , "GitHub repository. Currently, Pursuit does not support packages " - , "which are not hosted on GitHub." - ]) - , spacer - , para (concat - [ "Please update your package manifest file to point to a GitHub repository. " - , "Alternatively, if you would prefer not to host your package on " - , "GitHub, please open an issue:" - ]) - , indented (para "https://github.com/purescript/purescript/issues/new") - ] - -displayInternalError :: InternalError -> [String] -displayInternalError e = case e of - CouldntParseGitTagDate tag -> - [ "Unable to parse the date for a git tag: " ++ T.unpack tag - ] - -displayOtherError :: OtherError -> Box -displayOtherError e = case e of - ProcessFailed prog args exc -> - successivelyIndented - [ "While running `" ++ prog ++ " " ++ unwords args ++ "`:" - , show exc - ] - IOExceptionThrown exc -> - successivelyIndented - [ "An IO exception occurred:", show exc ] - -data CollectedWarnings = CollectedWarnings - { noResolvedVersions :: [PackageName] - , unacceptableVersions :: [(PackageName, Text)] - , dirtyWorkingTree :: Any - } - deriving (Show, Eq, Ord) - -instance Semigroup CollectedWarnings where - (<>) (CollectedWarnings a b c) (CollectedWarnings a' b' c') = - CollectedWarnings (a <> a') (b <> b') (c <> c') - -instance Monoid CollectedWarnings where - mempty = CollectedWarnings mempty mempty mempty - -collectWarnings :: [PackageWarning] -> CollectedWarnings -collectWarnings = foldMap singular - where - singular w = case w of - NoResolvedVersion pn -> - mempty { noResolvedVersions = [pn] } - UnacceptableVersion t -> - mempty { unacceptableVersions = [t] } - DirtyWorkingTreeWarn -> - mempty { dirtyWorkingTree = Any True } - -renderWarnings :: [PackageWarning] -> Box -renderWarnings warns = - let CollectedWarnings{..} = collectWarnings warns - go toBox warns' = toBox <$> NonEmpty.nonEmpty warns' - mboxes = [ go warnNoResolvedVersions noResolvedVersions - , go warnUnacceptableVersions unacceptableVersions - , if getAny dirtyWorkingTree - then Just warnDirtyWorkingTree - else Nothing - ] - in case catMaybes mboxes of - [] -> nullBox - boxes -> vcat [ para "Warnings:" - , indented (vcat (intersperse spacer boxes)) - ] - -warnNoResolvedVersions :: NonEmpty PackageName -> Box -warnNoResolvedVersions pkgNames = - let singular = NonEmpty.length pkgNames == 1 - pl a b = if singular then b else a - - packages = pl "packages" "package" - anyOfThese = pl "any of these" "this" - these = pl "these" "this" - in vcat $ - [ para (concat - ["The following ", packages, " did not appear to have a resolved " - , "version:"]) - ] ++ - bulletedListT runPackageName (NonEmpty.toList pkgNames) - ++ - [ spacer - , para (concat - ["Links to types in ", anyOfThese, " ", packages, " will not work. In " - , "order to make links work, edit your package manifest to specify a version" - , " or a version range for ", these, " ", packages, "." - ]) - ] - -warnUnacceptableVersions :: NonEmpty (PackageName, Text) -> Box -warnUnacceptableVersions pkgs = - let singular = NonEmpty.length pkgs == 1 - pl a b = if singular then b else a - - packages' = pl "packages'" "package's" - packages = pl "packages" "package" - anyOfThese = pl "any of these" "this" - these = pl "these" "this" - versions = pl "versions" "version" - in vcat $ - [ para (concat - [ "The following installed ", packages', " ", versions, " could " - , "not be parsed:" - ]) - ] ++ - bulletedListT showTuple (NonEmpty.toList pkgs) - ++ - [ spacer - , para (concat - ["Links to types in ", anyOfThese, " ", packages, " will not work. In " - , "order to make links work, edit your package manifest to specify an " - , "acceptable version or version range for ", these, " ", packages, "." - ]) - ] - where - showTuple (pkgName, tag) = runPackageName pkgName <> "#" <> tag - -warnDirtyWorkingTree :: Box -warnDirtyWorkingTree = - para ( - "Your working tree is dirty. (Note: this would be an error if it " - ++ "were not a dry run)" - ) - -printWarnings :: [PackageWarning] -> IO () -printWarnings = printToStderr . renderWarnings diff --git a/claude-help/original-compiler/src/Language/PureScript/Publish/Registry/Compat.hs b/claude-help/original-compiler/src/Language/PureScript/Publish/Registry/Compat.hs deleted file mode 100644 index a1a01ed9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Publish/Registry/Compat.hs +++ /dev/null @@ -1,98 +0,0 @@ --- | A compatibility module that allows a restricted set of purs.json manifest --- | files to be used for publishing. The manifest must described a package --- | available on GitHub, and it must be convertable to a Bower manifest. --- | --- | Fully supporting the registry manifest format will require `purs publish` --- | and by extension Pursuit to relax the requirement that packages are hosted --- | on GitHub, because the registry does not have this requirement. -module Language.PureScript.Publish.Registry.Compat where - -import Protolude -import Data.Map qualified as Map -import Web.Bower.PackageMeta qualified as Bower -import Data.Bitraversable (Bitraversable(..)) -import Data.Aeson.BetterErrors (key, asText, keyMay, eachInObject, Parse, throwCustomError) - --- | Convert a valid purs.json manifest into a bower.json manifest -toBowerPackage :: PursJson -> Either Bower.BowerError Bower.PackageMeta -toBowerPackage PursJson{..} = do - bowerName <- Bower.parsePackageName ("purescript-" <> pursJsonName) - let - bowerDescription = pursJsonDescription - bowerMain = [] - bowerModuleType = [] - bowerLicense = [ pursJsonLicense ] - bowerIgnore = [] - bowerKeywords = [] - bowerAuthors = [] - bowerHomepage = Just pursJsonLocation - bowerRepository = Just $ Bower.Repository { repositoryUrl = pursJsonLocation, repositoryType = "git" } - bowerDevDependencies = [] - bowerResolutions = [] - bowerPrivate = False - - let parseDependencies = traverse (bitraverse (Bower.parsePackageName . ("purescript-" <>)) (pure . Bower.VersionRange)) - bowerDependencies <- parseDependencies $ Map.toAscList pursJsonDependencies - pure $ Bower.PackageMeta {..} - --- | A partial representation of the purs.json manifest format, including only --- | the fields required for publishing. --- | --- | https://github.com/purescript/registry/blob/master/v1/Manifest.dhall --- --- This type is intended for compatibility with the Bower publishing pipeline, --- and does not accurately reflect all possible purs.json manifests. However, --- supporting purs.json manifests properly introduces breaking changes to the --- compiler and to Pursuit. -data PursJson = PursJson - { -- | The name of the package - pursJsonName :: Text - -- | The SPDX identifier representing the package license - , pursJsonLicense :: Text - -- | The GitHub repository hosting the package - , pursJsonLocation :: Text - -- | An optional description of the package - , pursJsonDescription :: Maybe Text - -- | A map of dependencies, where keys are package names and values are - -- | dependency ranges of the form '>=X.Y.Z Text -showPursJsonError = \case - MalformedLocationField -> - "The 'location' field must be either '{ \"githubOwner\": OWNER, \"githubRepo\": REPO }' or '{ \"gitUrl\": URL }'." - -asPursJson :: Parse PursJsonError PursJson -asPursJson = do - pursJsonName <- key "name" asText - pursJsonDescription <- keyMay "description" asText - pursJsonLicense <- key "license" asText - pursJsonDependencies <- key "dependencies" (Map.fromAscList <$> eachInObject asText) - -- Packages are required to come from GitHub in PureScript 0.14.x, but the - -- PureScript registry does not require this, nor does it require that - -- packages are Git repositories. This restriction should be lifted when - -- we fully support purs.json manifests in the compiler and on Pursuit. - -- - -- For the time being, we only parse manifests that include a GitHub owner - -- and repo pair, or which specify a Git URL, which we use to try and get - -- the package from GitHub. - pursJsonLocation <- key "location" asOwnerRepoOrGitUrl - pure $ PursJson{..} - where - asOwnerRepoOrGitUrl = - catchError asOwnerRepo (\_ -> catchError asGitUrl (\_ -> throwCustomError MalformedLocationField)) - - asGitUrl = - key "gitUrl" asText - - asOwnerRepo = do - githubOwner <- key "githubOwner" asText - githubRepo <- key "githubRepo" asText - pure $ "https://github.com/" <> githubOwner <> "/" <> githubRepo <> ".git" diff --git a/claude-help/original-compiler/src/Language/PureScript/Publish/Utils.hs b/claude-help/original-compiler/src/Language/PureScript/Publish/Utils.hs deleted file mode 100644 index 37607295..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Publish/Utils.hs +++ /dev/null @@ -1,14 +0,0 @@ -module Language.PureScript.Publish.Utils where - -import Prelude - -import System.Directory (getCurrentDirectory) -import System.FilePath.Glob (Pattern, compile, globDir1) - --- | Glob relative to the current directory, and produce relative pathnames. -globRelative :: Pattern -> IO [FilePath] -globRelative pat = getCurrentDirectory >>= globDir1 pat - --- | Glob pattern for PureScript source files. -purescriptSourceFiles :: Pattern -purescriptSourceFiles = compile "src/**/*.purs" diff --git a/claude-help/original-compiler/src/Language/PureScript/Renamer.hs b/claude-help/original-compiler/src/Language/PureScript/Renamer.hs deleted file mode 100644 index aff42ca2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Renamer.hs +++ /dev/null @@ -1,216 +0,0 @@ --- | --- Renaming pass that prevents shadowing of local identifiers. --- -module Language.PureScript.Renamer (renameInModule) where - -import Prelude - -import Control.Monad.State (MonadState(..), State, gets, modify, runState) -import Control.Monad ((>=>)) - -import Data.Functor ((<&>)) -import Data.List (find) -import Data.Maybe (fromJust, fromMaybe) -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Text qualified as T - -import Language.PureScript.CoreFn (Ann, Bind(..), Binder(..), CaseAlternative(..), Expr(..), Literal(..), Module(..)) -import Language.PureScript.Names (Ident(..), Qualified(..), isBySourcePos, isPlainIdent, runIdent, showIdent) -import Language.PureScript.Traversals (eitherM, pairM, sndM) - --- | --- The state object used in this module --- -data RenameState = RenameState { - -- | - -- A map from names bound (in the input) to their names (in the output) - -- - rsBoundNames :: M.Map Ident Ident - -- | - -- The set of names which have been used and are in scope in the output - -- - , rsUsedNames :: S.Set Ident - } - -type Rename = State RenameState - -initState :: [Ident] -> RenameState -initState scope = RenameState (M.fromList (zip scope scope)) (S.fromList scope) - --- | --- Runs renaming starting with a list of idents for the initial scope. --- -runRename :: [Ident] -> Rename a -> (a, RenameState) -runRename scope = flip runState (initState scope) - --- | --- Creates a new renaming scope using the current as a basis. Used to backtrack --- when leaving an Abs. --- -newScope :: Rename a -> Rename a -newScope x = do - scope <- get - a <- x - put scope - return a - --- | --- Adds a new scope entry for an ident. If the ident is already present, a new --- unique name is generated and stored. --- -updateScope :: Ident -> Rename Ident -updateScope ident = - case ident of - GenIdent name _ -> go ident $ Ident (fromMaybe "v" name) - UnusedIdent -> return UnusedIdent - _ -> go ident ident - where - go :: Ident -> Ident -> Rename Ident - go keyName baseName = do - scope <- get - let usedNames = rsUsedNames scope - name' = - if baseName `S.member` usedNames - then getNewName usedNames baseName - else baseName - modify $ \s -> s { rsBoundNames = M.insert keyName name' (rsBoundNames s) - , rsUsedNames = S.insert name' (rsUsedNames s) - } - return name' - getNewName :: S.Set Ident -> Ident -> Ident - getNewName usedNames name = - fromJust $ find - (`S.notMember` usedNames) - [ Ident (runIdent name <> T.pack (show (i :: Int))) | i <- [1..] ] - --- | --- Finds the new name to use for an ident. --- -lookupIdent :: Ident -> Rename Ident -lookupIdent UnusedIdent = return UnusedIdent -lookupIdent name = do - name' <- gets $ M.lookup name . rsBoundNames - case name' of - Just name'' -> return name'' - Nothing -> error $ "Rename scope is missing ident '" ++ T.unpack (showIdent name) ++ "'" - - --- | --- Renames within each declaration in a module. Returns the map of renamed --- identifiers in the top-level scope, so that they can be renamed in the --- externs files as well. --- -renameInModule :: Module Ann -> (M.Map Ident Ident, Module Ann) -renameInModule m@(Module _ _ _ _ _ exports _ foreigns decls) = (rsBoundNames, m { moduleExports, moduleDecls }) - where - ((moduleDecls, moduleExports), RenameState{..}) = runRename foreigns $ - (,) <$> renameInDecls decls <*> traverse lookupIdent exports - --- | --- Renames within a list of declarations. The list is processed in three --- passes: --- --- 1) Declarations with user-provided names are added to the scope, renaming --- them only if necessary to prevent shadowing. --- 2) Declarations with compiler-provided names are added to the scope, --- renaming them to prevent shadowing or collision with a user-provided --- name. --- 3) The bodies of the declarations are processed recursively. --- --- The distinction between passes 1 and 2 is critical in the top-level module --- scope, where declarations can be exported and named declarations must not --- be renamed. Below the top level, this only matters for programmers looking --- at the generated code or using a debugger; we want them to see the names --- they used as much as possible. --- --- The distinction between the first two passes and pass 3 is important because --- a `GenIdent` can appear before its declaration in a depth-first traversal, --- and we need to visit the declaration first in order to rename all of its --- uses. Similarly, a plain `Ident` could shadow another declared in an outer --- scope but later in a depth-first traversal, and we need to visit the --- outer declaration first in order to know to rename the inner one. --- -renameInDecls :: [Bind Ann] -> Rename [Bind Ann] -renameInDecls = - traverse (renameDecl False) - >=> traverse (renameDecl True) - >=> traverse renameValuesInDecl - - where - - renameDecl :: Bool -> Bind Ann -> Rename (Bind Ann) - renameDecl isSecondPass = \case - NonRec a name val -> updateName name <&> \name' -> NonRec a name' val - Rec ds -> Rec <$> traverse updateNames ds - where - updateName :: Ident -> Rename Ident - updateName name = (if isSecondPass == isPlainIdent name then pure else updateScope) name - - updateNames :: ((Ann, Ident), Expr Ann) -> Rename ((Ann, Ident), Expr Ann) - updateNames ((a, name), val) = updateName name <&> \name' -> ((a, name'), val) - - renameValuesInDecl :: Bind Ann -> Rename (Bind Ann) - renameValuesInDecl = \case - NonRec a name val -> NonRec a name <$> renameInValue val - Rec ds -> Rec <$> traverse updateValues ds - where - updateValues :: ((Ann, Ident), Expr Ann) -> Rename ((Ann, Ident), Expr Ann) - updateValues (aname, val) = (aname, ) <$> renameInValue val - --- | --- Renames within a value. --- -renameInValue :: Expr Ann -> Rename (Expr Ann) -renameInValue (Literal ann l) = - Literal ann <$> renameInLiteral renameInValue l -renameInValue c@Constructor{} = return c -renameInValue (Accessor ann prop v) = - Accessor ann prop <$> renameInValue v -renameInValue (ObjectUpdate ann obj copy vs) = - (\obj' -> ObjectUpdate ann obj' copy) <$> renameInValue obj <*> traverse (\(name, v) -> (name, ) <$> renameInValue v) vs -renameInValue (Abs ann name v) = - newScope $ Abs ann <$> updateScope name <*> renameInValue v -renameInValue (App ann v1 v2) = - App ann <$> renameInValue v1 <*> renameInValue v2 -renameInValue (Var ann (Qualified qb name)) | isBySourcePos qb || not (isPlainIdent name) = - -- This should only rename identifiers local to the current module: either - -- they aren't qualified, or they are but they have a name that should not - -- have appeared in a module's externs, so they must be from this module's - -- top-level scope. - Var ann . Qualified qb <$> lookupIdent name -renameInValue v@Var{} = return v -renameInValue (Case ann vs alts) = - newScope $ Case ann <$> traverse renameInValue vs <*> traverse renameInCaseAlternative alts -renameInValue (Let ann ds v) = - newScope $ Let ann <$> renameInDecls ds <*> renameInValue v - --- | --- Renames within literals. --- -renameInLiteral :: (a -> Rename a) -> Literal a -> Rename (Literal a) -renameInLiteral rename (ArrayLiteral bs) = ArrayLiteral <$> traverse rename bs -renameInLiteral rename (ObjectLiteral bs) = ObjectLiteral <$> traverse (sndM rename) bs -renameInLiteral _ l = return l - --- | --- Renames within case alternatives. --- -renameInCaseAlternative :: CaseAlternative Ann -> Rename (CaseAlternative Ann) -renameInCaseAlternative (CaseAlternative bs v) = newScope $ - CaseAlternative <$> traverse renameInBinder bs - <*> eitherM (traverse (pairM renameInValue renameInValue)) renameInValue v - --- | --- Renames within binders. --- -renameInBinder :: Binder a -> Rename (Binder a) -renameInBinder n@NullBinder{} = return n -renameInBinder (LiteralBinder ann b) = - LiteralBinder ann <$> renameInLiteral renameInBinder b -renameInBinder (VarBinder ann name) = - VarBinder ann <$> updateScope name -renameInBinder (ConstructorBinder ann tctor dctor bs) = - ConstructorBinder ann tctor dctor <$> traverse renameInBinder bs -renameInBinder (NamedBinder ann name b) = - NamedBinder ann <$> updateScope name <*> renameInBinder b diff --git a/claude-help/original-compiler/src/Language/PureScript/Roles.hs b/claude-help/original-compiler/src/Language/PureScript/Roles.hs deleted file mode 100644 index 7a730629..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Roles.hs +++ /dev/null @@ -1,43 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - --- | --- Data types for roles. --- -module Language.PureScript.Roles - ( Role(..) - , displayRole - ) where - -import Prelude - -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) -import Data.Aeson qualified as A -import Data.Aeson.TH qualified as A -import Data.Text (Text) -import GHC.Generics (Generic) - --- | --- The role of a type constructor's parameter. -data Role - = Nominal - -- ^ This parameter's identity affects the representation of the type it is - -- parameterising. - | Representational - -- ^ This parameter's representation affects the representation of the type it - -- is parameterising. - | Phantom - -- ^ This parameter has no effect on the representation of the type it is - -- parameterising. - deriving (Show, Eq, Ord, Generic) - -instance NFData Role -instance Serialise Role - -$(A.deriveJSON A.defaultOptions ''Role) - -displayRole :: Role -> Text -displayRole r = case r of - Nominal -> "nominal" - Representational -> "representational" - Phantom -> "phantom" diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar.hs deleted file mode 100644 index f898bc6a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar.hs +++ /dev/null @@ -1,79 +0,0 @@ --- | --- Desugaring passes --- -module Language.PureScript.Sugar (desugar, module S) where - -import Prelude - -import Control.Category ((>>>)) -import Control.Monad ((>=>)) -import Control.Monad.Error.Class (MonadError) -import Control.Monad.Supply.Class (MonadSupply) -import Control.Monad.State.Class (MonadState) -import Control.Monad.Writer.Class (MonadWriter) - -import Language.PureScript.AST (Module) -import Language.PureScript.Errors (MultipleErrors) -import Language.PureScript.Externs (ExternsFile) -import Language.PureScript.Linter.Imports (UsedImports) -import Language.PureScript.Sugar.BindingGroups as S -import Language.PureScript.Sugar.CaseDeclarations as S -import Language.PureScript.Sugar.DoNotation as S -import Language.PureScript.Sugar.AdoNotation as S -import Language.PureScript.Sugar.LetPattern as S -import Language.PureScript.Sugar.Names as S -import Language.PureScript.Sugar.ObjectWildcards as S -import Language.PureScript.Sugar.Operators as S -import Language.PureScript.Sugar.TypeClasses as S -import Language.PureScript.Sugar.TypeClasses.Deriving as S -import Language.PureScript.Sugar.TypeDeclarations as S -import Language.PureScript.Sugar.Accessor as S - --- | --- The desugaring pipeline proceeds as follows: --- --- * Remove signed literals in favour of `negate` applications --- --- * Desugar object literals with wildcards into lambdas --- --- * Desugar operator sections --- --- * Desugar do-notation --- --- * Desugar ado-notation --- --- * Desugar top-level case declarations into explicit case expressions --- --- * Desugar type declarations into value declarations with explicit type annotations --- --- * Qualify any unqualified names and types --- --- * Rebracket user-defined binary operators --- --- * Introduce newtypes for type class dictionaries and value declarations for instances --- --- * Group mutually recursive value and data declarations into binding groups. --- -desugar - :: MonadSupply m - => MonadError MultipleErrors m - => MonadWriter MultipleErrors m - => MonadState (Env, UsedImports) m - => [ExternsFile] - -> Module - -> m Module -desugar externs = - desugarSignedLiterals - >>> desugarObjectConstructors - >>> fmap (desugarAccessorModule externs) - >=> desugarDoModule - >=> desugarAdoModule - >=> desugarLetPatternModule - >>> desugarCasesModule - >=> desugarTypeDeclarationsModule - >=> desugarImports - >=> rebracket externs - >=> checkFixityExports - >=> deriveInstances - >=> desugarTypeClasses externs - >=> createBindingGroupsModule diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Accessor.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Accessor.hs deleted file mode 100644 index 1aaa0107..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Accessor.hs +++ /dev/null @@ -1,51 +0,0 @@ --- | -module Language.PureScript.Sugar.Accessor - ( desugarAccessorModule - ) where - -import Prelude - -import Control.Monad.Writer - -import Data.Monoid (Any(..)) -import Language.PureScript.AST -import Language.PureScript.Constants.Libs qualified as C -import Language.PureScript.Externs -import Language.PureScript.Names -import Language.PureScript.Types - --- | Replace every @BoundValueDeclaration@ in @Let@ expressions with @Case@ --- expressions. -desugarAccessorModule :: [ExternsFile] -> Module -> Module -desugarAccessorModule externs m - | not (any (\e -> efModuleName e == ModuleName "Data.Record") externs) = m -desugarAccessorModule _externs (Module ss coms mn ds exts) = - let (ds', Any used) = runWriter $ traverse desugarAccessor ds - extraImports = if used - then addDefaultImport (Qualified (ByModuleName C.M_Data_Record) C.M_Data_Record) - . addDefaultImport (Qualified (ByModuleName C.M_Type_Proxy) C.M_Type_Proxy) - else id - in extraImports $ Module ss coms mn ds' exts - --- | Desugar a single let expression -desugarAccessor :: Declaration -> Writer Any Declaration -desugarAccessor decl = - let (f, _, _) = everywhereOnValuesM pure replace pure in f decl - where - replace :: Expr -> Writer Any Expr - replace (Accessor label e) = do - tell (Any True) - pure $ App - (App - (Var nullSourceSpan C.I_getField) - (TypedValue - False - (Constructor nullSourceSpan C.C_Proxy) - (TypeApp nullSourceAnn - (TypeConstructor nullSourceAnn C.Proxy) - (TypeLevelString nullSourceAnn label) - ) - ) - ) - e - replace other = pure other diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/AdoNotation.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/AdoNotation.hs deleted file mode 100644 index 3ac53736..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/AdoNotation.hs +++ /dev/null @@ -1,66 +0,0 @@ --- | This module implements the desugaring pass which replaces ado-notation statements with --- appropriate calls to pure and apply. - -module Language.PureScript.Sugar.AdoNotation (desugarAdoModule) where - -import Prelude hiding (abs) - -import Control.Monad (foldM) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Supply.Class (MonadSupply) -import Data.List (foldl') -import Language.PureScript.AST (Binder(..), CaseAlternative(..), Declaration, DoNotationElement(..), Expr(..), pattern MkUnguarded, Module(..), SourceSpan, WhereProvenance(..), declSourceSpan, everywhereOnValuesM) -import Language.PureScript.Errors (MultipleErrors, parU, rethrowWithPosition) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Qualified(..), byMaybeModuleName, freshIdent') -import Language.PureScript.Constants.Libs qualified as C - --- | Replace all @AdoNotationBind@ and @AdoNotationValue@ constructors with --- applications of the pure and apply functions in scope, and all @AdoNotationLet@ --- constructors with let expressions. -desugarAdoModule :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Module -> m Module -desugarAdoModule (Module ss coms mn ds exts) = Module ss coms mn <$> parU ds desugarAdo <*> pure exts - --- | Desugar a single ado statement -desugarAdo :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Declaration -> m Declaration -desugarAdo d = - let ss = declSourceSpan d - (f, _, _) = everywhereOnValuesM return (replace ss) return - in rethrowWithPosition ss $ f d - where - pure' :: SourceSpan -> Maybe ModuleName -> Expr - pure' ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_pure)) - - map' :: SourceSpan -> Maybe ModuleName -> Expr - map' ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_map)) - - apply :: SourceSpan -> Maybe ModuleName -> Expr - apply ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_apply)) - - replace :: SourceSpan -> Expr -> m Expr - replace pos (Ado m els yield) = do - (func, args) <- foldM (go pos) (yield, []) (reverse els) - return $ case args of - [] -> App (pure' pos m) func - hd : tl -> foldl' (\a b -> App (App (apply pos m) a) b) (App (App (map' pos m) func) hd) tl - replace _ (PositionedValue pos com v) = PositionedValue pos com <$> rethrowWithPosition pos (replace pos v) - replace _ other = return other - - go :: SourceSpan -> (Expr, [Expr]) -> DoNotationElement -> m (Expr, [Expr]) - go _ (yield, args) (DoNotationValue val) = - return (Abs NullBinder yield, val : args) - go _ (yield, args) (DoNotationBind (VarBinder ss ident) val) = - return (Abs (VarBinder ss ident) yield, val : args) - go ss (yield, args) (DoNotationBind binder val) = do - ident <- freshIdent' - let abs = Abs (VarBinder ss ident) - (Case [Var ss (Qualified ByNullSourcePos ident)] - [CaseAlternative [binder] [MkUnguarded yield]]) - return (abs, val : args) - go _ (yield, args) (DoNotationLet ds) = do - return (Let FromLet ds yield, args) - go _ acc (PositionedDoNotationElement pos com el) = - rethrowWithPosition pos $ do - (yield, args) <- go pos acc el - return $ case args of - [] -> (PositionedValue pos com yield, args) - (a : as) -> (yield, PositionedValue pos com a : as) diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/BindingGroups.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/BindingGroups.hs deleted file mode 100644 index 835e775f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/BindingGroups.hs +++ /dev/null @@ -1,305 +0,0 @@ --- | --- This module implements the desugaring pass which creates binding groups from sets of --- mutually-recursive value declarations and mutually-recursive type declarations. --- -module Language.PureScript.Sugar.BindingGroups - ( createBindingGroups - , createBindingGroupsModule - , collapseBindingGroups - ) where - -import Prelude -import Protolude (ordNub, swap) - -import Control.Monad ((<=<), guard) -import Control.Monad.Error.Class (MonadError(..)) - -import Data.Graph (SCC(..), stronglyConnComp, stronglyConnCompR) -import Data.List (intersect, (\\)) -import Data.List.NonEmpty (NonEmpty((:|)), nonEmpty) -import Data.Foldable (find) -import Data.Functor (($>)) -import Data.Maybe (isJust, mapMaybe) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.AST -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (NameKind) -import Language.PureScript.Errors (ErrorMessage(..), MultipleErrors(..), SimpleErrorMessage(..), errorMessage', parU, positionedError) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident, ModuleName, ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName) -import Language.PureScript.Types (Constraint(..), SourceConstraint, SourceType, Type(..), everythingOnTypes) - -data VertexType - = VertexDefinition - | VertexKindSignature - | VertexRoleDeclaration - deriving (Eq, Ord, Show) - --- | --- Replace all sets of mutually-recursive declarations in a module with binding groups --- -createBindingGroupsModule - :: (MonadError MultipleErrors m) - => Module - -> m Module -createBindingGroupsModule (Module ss coms name ds exps) = - Module ss coms name <$> createBindingGroups name ds <*> pure exps - -createBindingGroups - :: forall m - . (MonadError MultipleErrors m) - => ModuleName - -> [Declaration] - -> m [Declaration] -createBindingGroups moduleName = mapM f <=< handleDecls - - where - (f, _, _) = everywhereOnValuesTopDownM return handleExprs return - - handleExprs :: Expr -> m Expr - handleExprs (Let w ds val) = (\ds' -> Let w ds' val) <$> handleDecls ds - handleExprs other = return other - - -- Replace all sets of mutually-recursive declarations with binding groups - handleDecls :: [Declaration] -> m [Declaration] - handleDecls ds = do - let values = mapMaybe (fmap (fmap extractGuardedExpr) . getValueDeclaration) ds - kindDecls = (,VertexKindSignature) <$> filter isKindDecl ds - dataDecls = (,VertexDefinition) <$> filter (\a -> isDataDecl a || isExternDataDecl a || isTypeSynonymDecl a || isTypeClassDecl a) ds - roleDecls = (,VertexRoleDeclaration) <$> filter isRoleDecl ds - roleAnns = declTypeName . fst <$> roleDecls - kindSigs = declTypeName . fst <$> kindDecls - typeSyns = declTypeName <$> filter isTypeSynonymDecl ds - nonTypeSynKindSigs = kindSigs \\ typeSyns - allDecls = kindDecls ++ dataDecls ++ roleDecls - allProperNames = declTypeName . fst <$> allDecls - mkVert (d, vty) = - let names = usedTypeNames moduleName d `intersect` allProperNames - name = declTypeName d - -- If a dependency of a kind signature has a kind signature, than that's all we need to - -- depend on, except in the case that we are using a type synonym. In order to expand - -- the type synonym, we must depend on the synonym declaration itself. - -- - -- Arguably, type declarations (as opposed to just kind signatures) could also depend - -- on kind signatures when present. Attempting this caused one known issue (#4038); the - -- type checker might not expect type declarations not to be preceded or grouped by - -- their actual dependencies in all cases. But in principle, if done carefully, this - -- approach could be used to reduce the number or size of data binding group cycles. - -- (It's critical that kind signatures not appear in groups, which is why they get - -- special treatment.) - vtype n - | vty == VertexKindSignature && n `elem` nonTypeSynKindSigs = VertexKindSignature - | otherwise = VertexDefinition - deps = fmap (\n -> (n, vtype n)) names - self - | vty == VertexDefinition = - (guard (name `elem` kindSigs) $> (name, VertexKindSignature)) - ++ (guard (name `elem` roleAnns && not (isExternDataDecl d)) $> (name, VertexRoleDeclaration)) - | vty == VertexRoleDeclaration = [(name, VertexDefinition)] - | otherwise = [] - in (d, (name, vty), self ++ deps) - dataVerts = fmap mkVert allDecls - dataBindingGroupDecls <- parU (stronglyConnCompR dataVerts) toDataBindingGroup - let - -- #4437 - -- - -- The idea here is to create a `Graph` whose `key` is a tuple: `(Bool, Ident)`, - -- where the `Bool` encodes the absence of a type hole. This relies on an implementation - -- detail for `stronglyConnComp` which allows identifiers with no type holes to "float" - -- and get checked before those that do, while preserving reverse topological sorting. - makeValueDeclarationKey = (,) <$> exprHasNoTypeHole . valdeclExpression <*> valdeclIdent - valueDeclarationKeys = makeValueDeclarationKey <$> values - - valueDeclarationInfo = M.fromList $ swap <$> valueDeclarationKeys - findDeclarationInfo i = (M.findWithDefault False i valueDeclarationInfo, i) - computeValueDependencies = (`intersect` valueDeclarationKeys) . fmap findDeclarationInfo . usedIdents moduleName - - makeValueDeclarationVert = (,,) <$> id <*> makeValueDeclarationKey <*> computeValueDependencies - valueDeclarationVerts = makeValueDeclarationVert <$> values - - bindingGroupDecls <- parU (stronglyConnComp valueDeclarationVerts) (toBindingGroup moduleName) - return $ filter isImportDecl ds ++ - dataBindingGroupDecls ++ - filter isTypeClassInstanceDecl ds ++ - filter isFixityDecl ds ++ - filter isExternDecl ds ++ - bindingGroupDecls - where - extractGuardedExpr [MkUnguarded expr] = expr - extractGuardedExpr _ = internalError "Expected Guards to have been desugared in handleDecls." - - exprHasNoTypeHole :: Expr -> Bool - exprHasNoTypeHole = not . exprHasTypeHole - where - exprHasTypeHole :: Expr -> Bool - (_, exprHasTypeHole, _, _, _) = everythingOnValues (||) goDefault goExpr goDefault goDefault goDefault - where - goExpr :: Expr -> Bool - goExpr (Hole _) = True - goExpr _ = False - - goDefault :: forall a. a -> Bool - goDefault = const False - --- | --- Collapse all binding groups to individual declarations --- -collapseBindingGroups :: [Declaration] -> [Declaration] -collapseBindingGroups = - let (f, _, _) = everywhereOnValues id flattenBindingGroupsForValue id - in fmap f . flattenBindingGroups - -flattenBindingGroupsForValue :: Expr -> Expr -flattenBindingGroupsForValue (Let w ds val) = Let w (flattenBindingGroups ds) val -flattenBindingGroupsForValue other = other - -flattenBindingGroups :: [Declaration] -> [Declaration] -flattenBindingGroups = concatMap go - where - go (DataBindingGroupDeclaration ds) = NEL.toList ds - go (BindingGroupDeclaration ds) = - NEL.toList $ fmap (\((sa, ident), nameKind, val) -> - ValueDecl sa ident nameKind [] [MkUnguarded val]) ds - go other = [other] - -usedIdents :: ModuleName -> ValueDeclarationData Expr -> [Ident] -usedIdents moduleName = ordNub . usedIdents' S.empty . valdeclExpression - where - def _ _ = [] - - (_, usedIdents', _, _, _) = everythingWithScope def usedNamesE def def def - - usedNamesE :: S.Set ScopedIdent -> Expr -> [Ident] - usedNamesE scope (Var _ (Qualified (BySourcePos _) name)) - | LocalIdent name `S.notMember` scope = [name] - usedNamesE scope (Var _ (Qualified (ByModuleName moduleName') name)) - | moduleName == moduleName' && ToplevelIdent name `S.notMember` scope = [name] - usedNamesE _ _ = [] - -usedImmediateIdents :: ModuleName -> Declaration -> [Ident] -usedImmediateIdents moduleName = - let (f, _, _, _, _) = everythingWithContextOnValues True [] (++) def usedNamesE def def def - in ordNub . f - where - def s _ = (s, []) - - usedNamesE :: Bool -> Expr -> (Bool, [Ident]) - usedNamesE True (Var _ (Qualified (BySourcePos _) name)) = (True, [name]) - usedNamesE True (Var _ (Qualified (ByModuleName moduleName') name)) - | moduleName == moduleName' = (True, [name]) - usedNamesE True (Abs _ _) = (False, []) - usedNamesE scope _ = (scope, []) - -usedTypeNames :: ModuleName -> Declaration -> [ProperName 'TypeName] -usedTypeNames moduleName = go - where - (f, _, _, _, _) = accumTypes (everythingOnTypes (++) usedNames) - - go :: Declaration -> [ProperName 'TypeName] - go decl = ordNub (f decl <> usedNamesForTypeClassDeps decl) - - usedNames :: SourceType -> [ProperName 'TypeName] - usedNames (ConstrainedType _ con _) = usedConstraint con - usedNames (TypeConstructor _ (Qualified (ByModuleName moduleName') name)) - | moduleName == moduleName' = [name] - usedNames _ = [] - - usedConstraint :: SourceConstraint -> [ProperName 'TypeName] - usedConstraint (Constraint _ (Qualified (ByModuleName moduleName') name) _ _ _) - | moduleName == moduleName' = [coerceProperName name] - usedConstraint _ = [] - - usedNamesForTypeClassDeps :: Declaration -> [ProperName 'TypeName] - usedNamesForTypeClassDeps (TypeClassDeclaration _ _ _ deps _ _) = foldMap usedConstraint deps - usedNamesForTypeClassDeps _ = [] - -declTypeName :: Declaration -> ProperName 'TypeName -declTypeName (DataDeclaration _ _ pn _ _) = pn -declTypeName (ExternDataDeclaration _ pn _) = pn -declTypeName (TypeSynonymDeclaration _ pn _ _) = pn -declTypeName (TypeClassDeclaration _ pn _ _ _ _) = coerceProperName pn -declTypeName (KindDeclaration _ _ pn _) = pn -declTypeName (RoleDeclaration (RoleDeclarationData _ pn _)) = pn -declTypeName _ = internalError "Expected DataDeclaration" - --- | --- Convert a group of mutually-recursive dependencies into a BindingGroupDeclaration (or simple ValueDeclaration). --- --- -toBindingGroup - :: forall m - . (MonadError MultipleErrors m) - => ModuleName - -> SCC (ValueDeclarationData Expr) - -> m Declaration -toBindingGroup _ (AcyclicSCC d) = return (mkDeclaration d) -toBindingGroup moduleName (CyclicSCC ds') = do - -- Once we have a mutually-recursive group of declarations, we need to sort - -- them further by their immediate dependencies (those outside function - -- bodies). In particular, this is relevant for type instance dictionaries - -- whose members require other type instances (for example, functorEff - -- defines (<$>) = liftA1, which depends on applicativeEff). Note that - -- superclass references are still inside functions, so don't count here. - -- If we discover declarations that still contain mutually-recursive - -- immediate references, we're guaranteed to get an undefined reference at - -- runtime, so treat this as an error. See also github issue #365. - BindingGroupDeclaration . NEL.fromList <$> mapM toBinding (stronglyConnComp valueVerts) - where - idents :: [Ident] - idents = fmap (\(_, i, _) -> i) valueVerts - - valueVerts :: [(ValueDeclarationData Expr, Ident, [Ident])] - valueVerts = fmap (\d -> (d, valdeclIdent d, usedImmediateIdents moduleName (mkDeclaration d) `intersect` idents)) ds' - - toBinding :: SCC (ValueDeclarationData Expr) -> m ((SourceAnn, Ident), NameKind, Expr) - toBinding (AcyclicSCC d) = return $ fromValueDecl d - toBinding (CyclicSCC ds) = throwError $ foldMap cycleError ds - - cycleError :: ValueDeclarationData Expr -> MultipleErrors - cycleError (ValueDeclarationData (ss, _) n _ _ _) = errorMessage' ss $ CycleInDeclaration n - -toDataBindingGroup - :: MonadError MultipleErrors m - => Ord a - => SCC (Declaration, (ProperName 'TypeName, a), [(ProperName 'TypeName, a)]) - -> m Declaration -toDataBindingGroup (AcyclicSCC (d, _, _)) = return d -toDataBindingGroup (CyclicSCC ds') - | Just kds@((ss, _) :| _) <- nonEmpty $ concatMap (kindDecl . getDecl) ds' = throwError . errorMessage' ss . CycleInKindDeclaration $ fmap snd kds - | not (null typeSynonymCycles) = - throwError - . MultipleErrors - . fmap (\syns -> ErrorMessage [positionedError . declSourceSpan . getDecl $ NEL.head syns] . CycleInTypeSynonym $ fmap (fst . getName) syns) - $ typeSynonymCycles - | otherwise = return . DataBindingGroupDeclaration . NEL.fromList $ getDecl <$> ds' - where - kindDecl (KindDeclaration sa _ pn _) = [(fst sa, Qualified ByNullSourcePos pn)] - kindDecl (ExternDataDeclaration sa pn _) = [(fst sa, Qualified ByNullSourcePos pn)] - kindDecl _ = [] - - getDecl (decl, _, _) = decl - getName (_, name, _) = name - lookupVert name = find ((==) name . getName) ds' - - onlySynonyms (decl, name, deps) = do - guard . isJust $ isTypeSynonym decl - pure (decl, name, filter (maybe False (isJust . isTypeSynonym . getDecl) . lookupVert) deps) - - isCycle (CyclicSCC c) = nonEmpty c - isCycle _ = Nothing - - typeSynonymCycles = - mapMaybe isCycle . stronglyConnCompR . mapMaybe onlySynonyms $ ds' - -isTypeSynonym :: Declaration -> Maybe (ProperName 'TypeName) -isTypeSynonym (TypeSynonymDeclaration _ pn _ _) = Just pn -isTypeSynonym _ = Nothing - -mkDeclaration :: ValueDeclarationData Expr -> Declaration -mkDeclaration = ValueDeclaration . fmap (pure . MkUnguarded) - -fromValueDecl :: ValueDeclarationData Expr -> ((SourceAnn, Ident), NameKind, Expr) -fromValueDecl (ValueDeclarationData sa ident nameKind [] val) = ((sa, ident), nameKind, val) -fromValueDecl ValueDeclarationData{} = internalError "Binders should have been desugared" diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/CaseDeclarations.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/CaseDeclarations.hs deleted file mode 100644 index bcae7677..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/CaseDeclarations.hs +++ /dev/null @@ -1,419 +0,0 @@ --- | --- This module implements the desugaring pass which replaces top-level binders with --- case expressions. --- -module Language.PureScript.Sugar.CaseDeclarations - ( desugarCases - , desugarCasesModule - , desugarCaseGuards - ) where - -import Prelude -import Protolude (ordNub) - -import Data.List (groupBy, foldl1') -import Data.Maybe (catMaybes, mapMaybe) - -import Control.Monad ((<=<), forM, replicateM, join, unless) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Supply.Class (MonadSupply) - -import Language.PureScript.AST -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (NameKind(..)) -import Language.PureScript.Errors (ErrorMessage(..), MultipleErrors(..), SimpleErrorMessage(..), addHint, errorMessage', parU, rethrow, withPosition) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident, Qualified(..), freshIdent') -import Language.PureScript.TypeChecker.Monad (guardWith) - --- | --- Replace all top-level binders in a module with case expressions. --- -desugarCasesModule - :: (MonadSupply m, MonadError MultipleErrors m) - => Module - -> m Module -desugarCasesModule (Module ss coms name ds exps) = - rethrow (addHint (ErrorInModule name)) $ - Module ss coms name - <$> (desugarCases <=< desugarAbs <=< validateCases $ ds) - <*> pure exps - -desugarCaseGuards - :: forall m. (MonadSupply m, MonadError MultipleErrors m) - => [Declaration] - -> m [Declaration] -desugarCaseGuards declarations = parU declarations go - where - go d = - let (f, _, _) = everywhereOnValuesM return (desugarGuardedExprs (declSourceSpan d)) return - in f d - --- | --- Desugar case with pattern guards and pattern clauses to a --- series of nested case expressions. --- -desugarGuardedExprs - :: forall m. (MonadSupply m) - => SourceSpan - -> Expr - -> m Expr -desugarGuardedExprs ss (Case scrut alternatives) - | not $ all isTrivialExpr scrut = do - -- in case the scrutinee is non trivial (e.g. not a Var or Literal) - -- we may evaluate the scrutinee more than once when a guard occurs. - -- We bind the scrutinee to Vars here to mitigate this case. - (scrut', scrut_decls) <- unzip <$> forM scrut (\e -> do - scrut_id <- freshIdent' - pure ( Var ss (Qualified ByNullSourcePos scrut_id) - , ValueDecl (ss, []) scrut_id Private [] [MkUnguarded e] - ) - ) - Let FromLet scrut_decls <$> desugarGuardedExprs ss (Case scrut' alternatives) - where - isTrivialExpr (Var _ _) = True - isTrivialExpr (Literal _ _) = True - isTrivialExpr (Accessor _ e) = isTrivialExpr e - isTrivialExpr (Parens e) = isTrivialExpr e - isTrivialExpr (PositionedValue _ _ e) = isTrivialExpr e - isTrivialExpr (TypedValue _ e _) = isTrivialExpr e - isTrivialExpr _ = False - -desugarGuardedExprs ss (Case scrut alternatives) = - let - -- Alternatives which do not have guards are - -- left as-is. Alternatives which - -- - -- 1) have multiple clauses of the form - -- binder | g_1 - -- , g_2 - -- , ... - -- , g_n - -- -> expr - -- - -- 2) and/or contain pattern guards of the form - -- binder | pat_bind <- e - -- , ... - -- - -- are desugared to a sequence of nested case expressions. - -- - -- Consider an example case expression: - -- - -- case e of - -- (T s) | Just info <- Map.lookup s names - -- , is_used info - -- -> f info - -- - -- We desugar this to - -- - -- case e of - -- (T s) -> case Map.lookup s names of - -- Just info -> case is_used info of - -- True -> f info - -- (_ -> ) - -- (_ -> ) - -- - -- Note that if the original case is partial the desugared - -- case is also partial. - -- - -- Consider an exhaustive case expression: - -- - -- case e of - -- (T s) | Just info <- Map.lookup s names - -- , is_used info - -- -> f info - -- _ -> Nothing - -- - -- desugars to: - -- - -- case e of - -- _ -> let - -- v _ = Nothing - -- in - -- case e of - -- (T s) -> case Map.lookup s names of - -- Just info -> f info - -- _ -> v true - -- _ -> v true - -- - -- This might look strange but simplifies the algorithm a lot. - -- - desugarAlternatives :: [CaseAlternative] - -> m [CaseAlternative] - desugarAlternatives [] = pure [] - - -- the trivial case: no guards - desugarAlternatives (a@(CaseAlternative _ [MkUnguarded _]) : as) = - (a :) <$> desugarAlternatives as - - -- Special case: CoreFn understands single condition guards on - -- binders right hand side. - desugarAlternatives (CaseAlternative ab ge : as) - | not (null cond_guards) = - (CaseAlternative ab cond_guards :) - <$> desugarGuardedAlternative ab rest as - | otherwise = desugarGuardedAlternative ab ge as - where - (cond_guards, rest) = span isSingleCondGuard ge - - isSingleCondGuard (GuardedExpr [ConditionGuard _] _) = True - isSingleCondGuard _ = False - - desugarGuardedAlternative :: [Binder] - -> [GuardedExpr] - -> [CaseAlternative] - -> m [CaseAlternative] - desugarGuardedAlternative _vb [] rem_alts = - desugarAlternatives rem_alts - - desugarGuardedAlternative vb (GuardedExpr gs e : ge) rem_alts = do - rhs <- desugarAltOutOfLine vb ge rem_alts $ \alt_fail -> - let - -- if the binder is a var binder we must not add - -- the fail case as it results in unreachable - -- alternative - alt_fail' n | all isIrrefutable vb = [] - | otherwise = alt_fail n - - - -- we are here: - -- - -- case scrut of - -- ... - -- _ -> let - -- v _ = - -- in case scrut of -- we are here - -- ... - -- - in Case scrut - (CaseAlternative vb [MkUnguarded (desugarGuard gs e alt_fail)] - : alt_fail' (length scrut)) - - return [ CaseAlternative scrut_nullbinder [MkUnguarded rhs]] - - desugarGuard :: [Guard] -> Expr -> (Int ->[CaseAlternative]) -> Expr - desugarGuard [] e _ = e - desugarGuard (ConditionGuard c : gs) e match_failed - | isTrueExpr c = desugarGuard gs e match_failed - | otherwise = - Case [c] - (CaseAlternative [LiteralBinder ss (BooleanLiteral True)] - [MkUnguarded (desugarGuard gs e match_failed)] : match_failed 1) - - desugarGuard (PatternGuard vb g : gs) e match_failed = - Case [g] - (CaseAlternative [vb] [MkUnguarded (desugarGuard gs e match_failed)] - : match_failed') - where - -- don't consider match_failed case if the binder is irrefutable - match_failed' | isIrrefutable vb = [] - | otherwise = match_failed 1 - - -- we generate a let-binding for the remaining guards - -- and alternatives. A CaseAlternative is passed (or in - -- fact the original case is partial non is passed) to - -- mk_body which branches to the generated let-binding. - desugarAltOutOfLine :: [Binder] - -> [GuardedExpr] - -> [CaseAlternative] - -> ((Int -> [CaseAlternative]) -> Expr) - -> m Expr - desugarAltOutOfLine alt_binder rem_guarded rem_alts mk_body - | Just rem_case <- mkCaseOfRemainingGuardsAndAlts = do - - desugared <- desugarGuardedExprs ss rem_case - rem_case_id <- freshIdent' - unused_binder <- freshIdent' - - let - goto_rem_case :: Expr - goto_rem_case = Var ss (Qualified ByNullSourcePos rem_case_id) - `App` Literal ss (BooleanLiteral True) - alt_fail :: Int -> [CaseAlternative] - alt_fail n = [CaseAlternative (replicate n NullBinder) [MkUnguarded goto_rem_case]] - - pure $ Let FromLet [ - ValueDecl (ss, []) rem_case_id Private [] - [MkUnguarded (Abs (VarBinder ss unused_binder) desugared)] - ] (mk_body alt_fail) - - | otherwise - = pure $ mk_body (const []) - where - mkCaseOfRemainingGuardsAndAlts - | not (null rem_guarded) - = Just $ Case scrut (CaseAlternative alt_binder rem_guarded : rem_alts) - | not (null rem_alts) - = Just $ Case scrut rem_alts - | otherwise - = Nothing - - scrut_nullbinder :: [Binder] - scrut_nullbinder = replicate (length scrut) NullBinder - - -- case expressions with a single alternative which have - -- a NullBinder occur frequently after desugaring - -- complex guards. This function removes these superfluous - -- cases. - optimize :: Expr -> Expr - optimize (Case _ [CaseAlternative vb [MkUnguarded v]]) - | all isNullBinder vb = v - where - isNullBinder NullBinder = True - isNullBinder (PositionedBinder _ _ b) = isNullBinder b - isNullBinder (TypedBinder _ b) = isNullBinder b - isNullBinder _ = False - optimize e = e - in do - alts' <- desugarAlternatives alternatives - return $ optimize (Case scrut alts') - -desugarGuardedExprs ss (TypedValue inferred e ty) = - TypedValue inferred <$> desugarGuardedExprs ss e <*> pure ty - -desugarGuardedExprs _ (PositionedValue ss comms e) = - PositionedValue ss comms <$> desugarGuardedExprs ss e - -desugarGuardedExprs _ v = pure v - --- | --- Validates that case head and binder lengths match. --- -validateCases :: forall m. (MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration] -validateCases = flip parU f - where - (f, _, _) = everywhereOnValuesM return validate return - - validate :: Expr -> m Expr - validate c@(Case vs alts) = do - let l = length vs - alts' = filter ((l /=) . length . caseAlternativeBinders) alts - unless (null alts') $ - throwError . MultipleErrors $ fmap (altError l) (caseAlternativeBinders <$> alts') - return c - validate other = return other - - altError :: Int -> [Binder] -> ErrorMessage - altError l bs = withPosition pos $ ErrorMessage [] $ CaseBinderLengthDiffers l bs - where - pos = foldl1' widenSpan (mapMaybe positionedBinder bs) - - widenSpan (SourceSpan n start end) (SourceSpan _ start' end') = - SourceSpan n (min start start') (max end end') - - positionedBinder (PositionedBinder p _ _) = Just p - positionedBinder _ = Nothing - -desugarAbs :: forall m. (MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration] -desugarAbs = flip parU f - where - (f, _, _) = everywhereOnValuesM return replace return - - replace :: Expr -> m Expr - replace (Abs (stripPositioned -> (VarBinder ss i)) val) = - pure (Abs (VarBinder ss i) val) - replace (Abs binder val) = do - ident <- freshIdent' - return $ Abs (VarBinder nullSourceSpan ident) $ Case [Var nullSourceSpan (Qualified ByNullSourcePos ident)] [CaseAlternative [binder] [MkUnguarded val]] - replace other = return other - -stripPositioned :: Binder -> Binder -stripPositioned (PositionedBinder _ _ binder) = stripPositioned binder -stripPositioned binder = binder - --- | --- Replace all top-level binders with case expressions. --- -desugarCases :: forall m. (MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration] -desugarCases = desugarRest <=< fmap join . flip parU toDecls . groupBy inSameGroup - where - desugarRest :: [Declaration] -> m [Declaration] - desugarRest (TypeInstanceDeclaration sa na cd idx name constraints className tys ds : rest) = - (:) <$> (TypeInstanceDeclaration sa na cd idx name constraints className tys <$> traverseTypeInstanceBody desugarCases ds) <*> desugarRest rest - desugarRest (ValueDecl sa name nameKind bs result : rest) = - let (_, f, _) = everywhereOnValuesTopDownM return go return - f' = mapM (\(GuardedExpr gs e) -> GuardedExpr gs <$> f e) - in (:) <$> (ValueDecl sa name nameKind bs <$> f' result) <*> desugarRest rest - where - go (Let w ds val') = Let w <$> desugarCases ds <*> pure val' - go other = return other - desugarRest (d : ds) = (:) d <$> desugarRest ds - desugarRest [] = pure [] - -inSameGroup :: Declaration -> Declaration -> Bool -inSameGroup (ValueDeclaration vd1) (ValueDeclaration vd2) = valdeclIdent vd1 == valdeclIdent vd2 -inSameGroup _ _ = False - -toDecls :: forall m. (MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration] -toDecls [ValueDecl sa@(ss, _) ident nameKind bs [MkUnguarded val]] | all isIrrefutable bs = do - args <- mapM fromVarBinder bs - let body = foldr (Abs . VarBinder ss) val args - guardWith (errorMessage' ss (OverlappingArgNames (Just ident))) $ length (ordNub args) == length args - return [ValueDecl sa ident nameKind [] [MkUnguarded body]] - where - fromVarBinder :: Binder -> m Ident - fromVarBinder NullBinder = freshIdent' - fromVarBinder (VarBinder _ name) = return name - fromVarBinder (PositionedBinder _ _ b) = fromVarBinder b - fromVarBinder (TypedBinder _ b) = fromVarBinder b - fromVarBinder _ = internalError "fromVarBinder: Invalid argument" -toDecls ds@(ValueDecl (ss, _) ident _ bs (result : _) : _) = do - let tuples = map toTuple ds - - isGuarded (MkUnguarded _) = False - isGuarded _ = True - - unless (all ((== length bs) . length . fst) tuples) . - throwError . errorMessage' ss $ ArgListLengthsDiffer ident - unless (not (null bs) || isGuarded result) . - throwError . errorMessage' ss $ DuplicateValueDeclaration ident - caseDecl <- makeCaseDeclaration ss ident tuples - return [caseDecl] -toDecls ds = return ds - -toTuple :: Declaration -> ([Binder], [GuardedExpr]) -toTuple (ValueDecl _ _ _ bs result) = (bs, result) -toTuple _ = internalError "Not a value declaration" - -makeCaseDeclaration :: forall m. (MonadSupply m) => SourceSpan -> Ident -> [([Binder], [GuardedExpr])] -> m Declaration -makeCaseDeclaration ss ident alternatives = do - let namedArgs = map findName . fst <$> alternatives - argNames = foldl1 resolveNames namedArgs - args <- if allUnique (catMaybes argNames) - then mapM argName argNames - else replicateM (length argNames) ((nullSourceSpan, ) <$> freshIdent') - let vars = map (Var ss . Qualified ByNullSourcePos . snd) args - binders = [ CaseAlternative bs result | (bs, result) <- alternatives ] - let value = foldr (Abs . uncurry VarBinder) (Case vars binders) args - - return $ ValueDecl (ss, []) ident Public [] [MkUnguarded value] - where - -- We will construct a table of potential names. - -- VarBinders will become Just _ which is a potential name. - -- Everything else becomes Nothing, which indicates that we - -- have to generate a name. - findName :: Binder -> Maybe (SourceSpan, Ident) - findName (VarBinder ss' name) = Just (ss', name) - findName (PositionedBinder _ _ binder) = findName binder - findName _ = Nothing - - -- We still have to make sure the generated names are unique, or else - -- we will end up constructing an invalid function. - allUnique :: (Ord a) => [a] -> Bool - allUnique xs = length xs == length (ordNub xs) - - argName :: Maybe (SourceSpan, Ident) -> m (SourceSpan, Ident) - argName (Just (ss', name)) = return (ss', name) - argName _ = (nullSourceSpan, ) <$> freshIdent' - - -- Combine two lists of potential names from two case alternatives - -- by zipping corresponding columns. - resolveNames :: [Maybe (SourceSpan, Ident)] -> [Maybe (SourceSpan, Ident)] -> [Maybe (SourceSpan, Ident)] - resolveNames = zipWith resolveName - - -- Resolve a pair of names. VarBinder beats NullBinder, and everything - -- else results in Nothing. - resolveName :: Maybe (SourceSpan, Ident) -> Maybe (SourceSpan, Ident) -> Maybe (SourceSpan, Ident) - resolveName (Just a) (Just b) - | a == b = Just a - | otherwise = Nothing - resolveName _ _ = Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/DoNotation.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/DoNotation.hs deleted file mode 100644 index 8542a5a7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/DoNotation.hs +++ /dev/null @@ -1,83 +0,0 @@ --- | This module implements the desugaring pass which replaces do-notation statements with --- appropriate calls to bind. - -module Language.PureScript.Sugar.DoNotation (desugarDoModule) where - -import Prelude - -import Control.Applicative ((<|>)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Supply.Class (MonadSupply) -import Data.Maybe (fromMaybe) -import Data.Monoid (First(..)) -import Language.PureScript.AST (Binder(..), CaseAlternative(..), Declaration, DoNotationElement(..), Expr(..), pattern MkUnguarded, Module(..), SourceSpan, pattern ValueDecl, WhereProvenance(..), binderNames, declSourceSpan, everywhereOnValuesM) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage, errorMessage', parU, rethrowWithPosition) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Qualified(..), byMaybeModuleName, freshIdent') -import Language.PureScript.Constants.Libs qualified as C - --- | Replace all @DoNotationBind@ and @DoNotationValue@ constructors with --- applications of the bind function in scope, and all @DoNotationLet@ --- constructors with let expressions. -desugarDoModule :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Module -> m Module -desugarDoModule (Module ss coms mn ds exts) = Module ss coms mn <$> parU ds desugarDo <*> pure exts - --- | Desugar a single do statement -desugarDo :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Declaration -> m Declaration -desugarDo d = - let ss = declSourceSpan d - (f, _, _) = everywhereOnValuesM return (replace ss) return - in rethrowWithPosition ss $ f d - where - bind :: SourceSpan -> Maybe ModuleName -> Expr - bind ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_bind)) - - discard :: SourceSpan -> Maybe ModuleName -> Expr - discard ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_discard)) - - replace :: SourceSpan -> Expr -> m Expr - replace pos (Do m els) = go pos m els - replace _ (PositionedValue pos com v) = PositionedValue pos com <$> rethrowWithPosition pos (replace pos v) - replace _ other = return other - - stripPositionedBinder :: Binder -> (Maybe SourceSpan, Binder) - stripPositionedBinder (PositionedBinder ss _ b) = - let (ss', b') = stripPositionedBinder b - in (ss' <|> Just ss, b') - stripPositionedBinder b = - (Nothing, b) - - go :: SourceSpan -> Maybe ModuleName -> [DoNotationElement] -> m Expr - go _ _ [] = internalError "The impossible happened in desugarDo" - go _ _ [DoNotationValue val] = return val - go pos m (DoNotationValue val : rest) = do - rest' <- go pos m rest - return $ App (App (discard pos m) val) (Abs (VarBinder pos UnusedIdent) rest') - go _ _ [DoNotationBind _ _] = throwError . errorMessage $ InvalidDoBind - go _ _ (DoNotationBind b _ : _) | First (Just ident) <- foldMap fromIdent (binderNames b) = - throwError . errorMessage $ CannotUseBindWithDo (Ident ident) - where - fromIdent (Ident i) | i `elem` [ C.S_bind, C.S_discard ] = First (Just i) - fromIdent _ = mempty - go pos m (DoNotationBind binder val : rest) = do - rest' <- go pos m rest - let (mss, binder') = stripPositionedBinder binder - let ss = fromMaybe pos mss - case binder' of - NullBinder -> - return $ App (App (bind pos m) val) (Abs (VarBinder ss UnusedIdent) rest') - VarBinder _ ident -> - return $ App (App (bind pos m) val) (Abs (VarBinder ss ident) rest') - _ -> do - ident <- freshIdent' - return $ App (App (bind pos m) val) (Abs (VarBinder pos ident) (Case [Var pos (Qualified ByNullSourcePos ident)] [CaseAlternative [binder] [MkUnguarded rest']])) - go _ _ [DoNotationLet _] = throwError . errorMessage $ InvalidDoLet - go pos m (DoNotationLet ds : rest) = do - let checkBind :: Declaration -> m () - checkBind (ValueDecl (ss, _) i@(Ident name) _ _ _) - | name `elem` [ C.S_bind, C.S_discard ] = throwError . errorMessage' ss $ CannotUseBindWithDo i - checkBind _ = pure () - mapM_ checkBind ds - rest' <- go pos m rest - return $ Let FromLet ds rest' - go _ m (PositionedDoNotationElement pos com el : rest) = rethrowWithPosition pos $ PositionedValue pos com <$> go pos m (el : rest) diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/LetPattern.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/LetPattern.hs deleted file mode 100644 index 519487d9..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/LetPattern.hs +++ /dev/null @@ -1,54 +0,0 @@ --- | --- This module implements the desugaring pass which replaces patterns in let-in --- expressions with appropriate case expressions. --- -module Language.PureScript.Sugar.LetPattern (desugarLetPatternModule) where - -import Prelude - -import Data.List (groupBy) -import Data.Function (on) - -import Language.PureScript.AST (Binder, CaseAlternative(..), Declaration(..), Expr(..), pattern MkUnguarded, Module(..), SourceAnn, WhereProvenance, everywhereOnValues) -import Language.PureScript.Crash (internalError) - --- | Replace every @BoundValueDeclaration@ in @Let@ expressions with @Case@ --- expressions. -desugarLetPatternModule :: Module -> Module -desugarLetPatternModule (Module ss coms mn ds exts) = Module ss coms mn (map desugarLetPattern ds) exts - --- | Desugar a single let expression -desugarLetPattern :: Declaration -> Declaration -desugarLetPattern decl = - let (f, _, _) = everywhereOnValues id replace id - in f decl - where - replace :: Expr -> Expr - replace (Let w ds e) = go w (partitionDecls ds) e - replace other = other - - go :: WhereProvenance - -- Metadata about whether the let-in was a where clause - -> [Either [Declaration] (SourceAnn, Binder, Expr)] - -- Declarations to desugar - -> Expr - -- The original let-in result expression - -> Expr - go _ [] e = e - go w (Right ((pos, com), binder, boundE) : ds) e = - PositionedValue pos com $ Case [boundE] [CaseAlternative [binder] [MkUnguarded $ go w ds e]] - go w (Left ds:dss) e = Let w ds (go w dss e) - -partitionDecls :: [Declaration] -> [Either [Declaration] (SourceAnn, Binder, Expr)] -partitionDecls = concatMap f . groupBy ((==) `on` isBoundValueDeclaration) - where - f ds@(d:_) - | isBoundValueDeclaration d = map (Right . g) ds - f ds = [Left ds] - - g (BoundValueDeclaration sa binder expr) = (sa, binder, expr) - g _ = internalError "partitionDecls: the impossible happened." - -isBoundValueDeclaration :: Declaration -> Bool -isBoundValueDeclaration BoundValueDeclaration{} = True -isBoundValueDeclaration _ = False diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Names.hs deleted file mode 100644 index d081764d..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names.hs +++ /dev/null @@ -1,443 +0,0 @@ -module Language.PureScript.Sugar.Names - ( desugarImports - , Env - , externsEnv - , primEnv - , ImportRecord(..) - , ImportProvenance(..) - , Imports(..) - , Exports(..) - ) where - -import Prelude -import Protolude (sortOn, swap, foldl') - -import Control.Arrow (first, second, (&&&)) -import Control.Monad (foldM, when, (>=>)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State.Lazy (MonadState, StateT(..), gets, modify) -import Control.Monad.Writer (MonadWriter(..)) - -import Data.List.NonEmpty qualified as NEL -import Data.Maybe (fromMaybe, mapMaybe) -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.AST -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage, errorMessage'', nonEmpty, parU, warnAndRethrow, warnAndRethrowWithPosition) -import Language.PureScript.Externs (ExternsDeclaration(..), ExternsFile(..), ExternsImport(..)) -import Language.PureScript.Linter.Imports (Name(..), UsedImports) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident, OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..)) -import Language.PureScript.Sugar.Names.Env (Env, Exports(..), ImportProvenance(..), ImportRecord(..), Imports(..), checkImportConflicts, nullImports, primEnv) -import Language.PureScript.Sugar.Names.Exports (findExportable, resolveExports) -import Language.PureScript.Sugar.Names.Imports (resolveImports, resolveModuleImport) -import Language.PureScript.Traversals (defS, sndM) -import Language.PureScript.Types (Constraint(..), SourceConstraint, SourceType, Type(..), everywhereOnTypesM) - --- | --- Replaces all local names with qualified names. --- -desugarImports - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadState (Env, UsedImports) m) - => Module - -> m Module -desugarImports = updateEnv >=> renameInModule' - where - updateEnv :: Module -> m Module - updateEnv m@(Module ss _ mn _ refs) = do - members <- findExportable m - env' <- gets $ M.insert mn (ss, nullImports, members) . fst - (m', imps) <- resolveImports env' m - exps <- maybe (return members) (resolveExports env' ss mn imps members) refs - modify . first $ M.insert mn (ss, imps, exps) - return m' - - renameInModule' :: Module -> m Module - renameInModule' m@(Module _ _ mn _ _) = - warnAndRethrow (addHint (ErrorInModule mn)) $ do - env <- gets fst - let (_, imps, exps) = fromMaybe (internalError "Module is missing in renameInModule'") $ M.lookup mn env - (m', used) <- flip runStateT M.empty $ renameInModule imps m - modify . second $ M.unionWith (<>) used - return $ elaborateExports exps m' - --- | Create an environment from a collection of externs files -externsEnv - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => Env - -> ExternsFile - -> m Env -externsEnv env ExternsFile{..} = do - let members = Exports{..} - env' = M.insert efModuleName (efSourceSpan, nullImports, members) env - fromEFImport (ExternsImport mn mt qmn) = (mn, [(efSourceSpan, Just mt, qmn)]) - imps <- foldM (resolveModuleImport env') nullImports (map fromEFImport efImports) - exps <- resolveExports env' efSourceSpan efModuleName imps members efExports - return $ M.insert efModuleName (efSourceSpan, imps, exps) env - where - - -- An ExportSource for declarations local to the module which the given - -- ExternsFile corresponds to. - localExportSource = - ExportSource { exportSourceDefinedIn = efModuleName - , exportSourceImportedFrom = Nothing - } - - exportedTypes :: M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - exportedTypes = M.fromList $ mapMaybe toExportedType efExports - where - toExportedType (TypeRef _ tyCon dctors) = Just (tyCon, (fromMaybe (mapMaybe forTyCon efDeclarations) dctors, localExportSource)) - where - forTyCon :: ExternsDeclaration -> Maybe (ProperName 'ConstructorName) - forTyCon (EDDataConstructor pn _ tNm _ _) | tNm == tyCon = Just pn - forTyCon _ = Nothing - toExportedType _ = Nothing - - exportedTypeOps :: M.Map (OpName 'TypeOpName) ExportSource - exportedTypeOps = exportedRefs getTypeOpRef - - exportedTypeClasses :: M.Map (ProperName 'ClassName) ExportSource - exportedTypeClasses = exportedRefs getTypeClassRef - - exportedValues :: M.Map Ident ExportSource - exportedValues = exportedRefs getValueRef - - exportedValueOps :: M.Map (OpName 'ValueOpName) ExportSource - exportedValueOps = exportedRefs getValueOpRef - - exportedRefs :: Ord a => (DeclarationRef -> Maybe a) -> M.Map a ExportSource - exportedRefs f = - M.fromList $ (, localExportSource) <$> mapMaybe f efExports - --- | --- Make all exports for a module explicit. This may still affect modules that --- have an exports list, as it will also make all data constructor exports --- explicit. --- --- The exports will appear in the same order as they do in the existing exports --- list, or if there is no export list, declarations are order based on their --- order of appearance in the module. --- -elaborateExports :: Exports -> Module -> Module -elaborateExports exps (Module ss coms mn decls refs) = - Module ss coms mn decls $ Just $ reorderExports decls refs - $ elaboratedTypeRefs - ++ go (TypeOpRef ss) exportedTypeOps - ++ go (TypeClassRef ss) exportedTypeClasses - ++ go (ValueRef ss) exportedValues - ++ go (ValueOpRef ss) exportedValueOps - ++ maybe [] (filter isModuleRef) refs - where - - elaboratedTypeRefs :: [DeclarationRef] - elaboratedTypeRefs = - flip map (M.toList (exportedTypes exps)) $ \(tctor, (dctors, src)) -> - let ref = TypeRef ss tctor (Just dctors) - in if mn == exportSourceDefinedIn src then ref else ReExportRef ss src ref - - go :: (a -> DeclarationRef) -> (Exports -> M.Map a ExportSource) -> [DeclarationRef] - go toRef select = - flip map (M.toList (select exps)) $ \(export, src) -> - if mn == exportSourceDefinedIn src then toRef export else ReExportRef ss src (toRef export) - --- | --- Given a list of declarations, an original exports list, and an elaborated --- exports list, reorder the elaborated list so that it matches the original --- order. If there is no original exports list, reorder declarations based on --- their order in the source file. -reorderExports :: [Declaration] -> Maybe [DeclarationRef] -> [DeclarationRef] -> [DeclarationRef] -reorderExports decls originalRefs = - sortOn originalIndex - where - names = - maybe (mapMaybe declName decls) (map declRefName) originalRefs - namesMap = - M.fromList $ zip names [(0::Int)..] - originalIndex ref = - M.lookup (declRefName ref) namesMap - --- | --- Replaces all local names with qualified names within a module and checks that all existing --- qualified names are valid. --- -renameInModule - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadState UsedImports m) - => Imports - -> Module - -> m Module -renameInModule imports (Module modSS coms mn decls exps) = - Module modSS coms mn <$> parU decls go <*> pure exps - where - - (go, _, _, _, _, _) = - everywhereWithContextOnValuesM - (modSS, M.empty) - (\(_, bound) d -> (\(bound', d') -> ((declSourceSpan d', bound'), d')) <$> updateDecl bound d) - updateValue - updateBinder - updateCase - defS - updateGuard - - updateDecl - :: M.Map Ident SourcePos - -> Declaration - -> m (M.Map Ident SourcePos, Declaration) - updateDecl bound (DataDeclaration sa dtype name args dctors) = - fmap (bound,) $ - DataDeclaration sa dtype name - <$> updateTypeArguments args - <*> traverse (traverseDataCtorFields (traverse (sndM updateTypesEverywhere))) dctors - updateDecl bound (TypeSynonymDeclaration sa name ps ty) = - fmap (bound,) $ - TypeSynonymDeclaration sa name - <$> updateTypeArguments ps - <*> updateTypesEverywhere ty - updateDecl bound (TypeClassDeclaration sa className args implies deps ds) = - fmap (bound,) $ - TypeClassDeclaration sa className - <$> updateTypeArguments args - <*> updateConstraints implies - <*> pure deps - <*> pure ds - updateDecl bound (TypeInstanceDeclaration sa na@(ss, _) ch idx name cs cn ts ds) = - fmap (bound,) $ - TypeInstanceDeclaration sa na ch idx name - <$> updateConstraints cs - <*> updateClassName cn ss - <*> traverse updateTypesEverywhere ts - <*> pure ds - updateDecl bound (KindDeclaration sa kindFor name ty) = - fmap (bound,) $ - KindDeclaration sa kindFor name - <$> updateTypesEverywhere ty - updateDecl bound (TypeDeclaration (TypeDeclarationData sa name ty)) = - fmap (bound,) $ - TypeDeclaration . TypeDeclarationData sa name - <$> updateTypesEverywhere ty - updateDecl bound (ExternDeclaration sa name ty) = - fmap (M.insert name (spanStart $ fst sa) bound,) $ - ExternDeclaration sa name - <$> updateTypesEverywhere ty - updateDecl bound (ExternDataDeclaration sa name ki) = - fmap (bound,) $ - ExternDataDeclaration sa name - <$> updateTypesEverywhere ki - updateDecl bound (TypeFixityDeclaration sa@(ss, _) fixity alias op) = - fmap (bound,) $ - TypeFixityDeclaration sa fixity - <$> updateTypeName alias ss - <*> pure op - updateDecl bound (ValueFixityDeclaration sa@(ss, _) fixity (Qualified mn' (Left alias)) op) = - fmap (bound,) $ - ValueFixityDeclaration sa fixity . fmap Left - <$> updateValueName (Qualified mn' alias) ss - <*> pure op - updateDecl bound (ValueFixityDeclaration sa@(ss, _) fixity (Qualified mn' (Right alias)) op) = - fmap (bound,) $ - ValueFixityDeclaration sa fixity . fmap Right - <$> updateDataConstructorName (Qualified mn' alias) ss - <*> pure op - updateDecl b d = - return (b, d) - - updateValue - :: (SourceSpan, M.Map Ident SourcePos) - -> Expr - -> m ((SourceSpan, M.Map Ident SourcePos), Expr) - updateValue (_, bound) v@(PositionedValue pos' _ _) = - return ((pos', bound), v) - updateValue (pos, bound) (Abs (VarBinder ss arg) val') = - return ((pos, M.insert arg (spanStart ss) bound), Abs (VarBinder ss arg) val') - updateValue (pos, bound) (Let w ds val') = do - let - args = mapMaybe letBoundVariable ds - groupByFst = map (\ts -> (fst (NEL.head ts), snd <$> ts)) . NEL.groupAllWith fst - duplicateArgsErrs = foldMap mkArgError $ groupByFst args - mkArgError (ident, poses) - | NEL.length poses < 2 = mempty - | otherwise = errorMessage'' (NEL.reverse poses) (OverlappingNamesInLet ident) - when (nonEmpty duplicateArgsErrs) $ - throwError duplicateArgsErrs - return ((pos, declarationsToMap ds `M.union` bound), Let w ds val') - updateValue (_, bound) (Var ss name'@(Qualified qualifiedBy ident)) = - ((ss, bound), ) <$> case (M.lookup ident bound, qualifiedBy) of - -- bound idents that have yet to be locally qualified. - (Just sourcePos, ByNullSourcePos) -> - pure $ Var ss (Qualified (BySourcePos sourcePos) ident) - -- unbound idents are likely import unqualified imports, so we - -- handle them through updateValueName if they don't exist as a - -- local binding. - (Nothing, ByNullSourcePos) -> - Var ss <$> updateValueName name' ss - -- bound/unbound idents with explicit qualification is still - -- handled through updateValueName, as it fully resolves the - -- ModuleName. - (_, ByModuleName _) -> - Var ss <$> updateValueName name' ss - -- encountering non-null source spans may be a bug in previous - -- desugaring steps or with the AST traversals. - (_, BySourcePos _) -> - internalError "updateValue: ident is locally-qualified by a non-null source position" - updateValue (_, bound) (Op ss op) = - ((ss, bound), ) <$> (Op ss <$> updateValueOpName op ss) - updateValue (_, bound) (Constructor ss name) = - ((ss, bound), ) <$> (Constructor ss <$> updateDataConstructorName name ss) - updateValue s (TypedValue check val ty) = - (s, ) <$> (TypedValue check val <$> updateTypesEverywhere ty) - updateValue s (VisibleTypeApp val ty) = - (s, ) <$> VisibleTypeApp val <$> updateTypesEverywhere ty - updateValue s v = return (s, v) - - updateBinder - :: (SourceSpan, M.Map Ident SourcePos) - -> Binder - -> m ((SourceSpan, M.Map Ident SourcePos), Binder) - updateBinder (_, bound) v@(PositionedBinder pos _ _) = - return ((pos, bound), v) - updateBinder (_, bound) (ConstructorBinder ss name b) = - ((ss, bound), ) <$> (ConstructorBinder ss <$> updateDataConstructorName name ss <*> pure b) - updateBinder (_, bound) (OpBinder ss op) = - ((ss, bound), ) <$> (OpBinder ss <$> updateValueOpName op ss) - updateBinder s (TypedBinder t b) = do - t' <- updateTypesEverywhere t - return (s, TypedBinder t' b) - updateBinder s v = - return (s, v) - - updateCase - :: (SourceSpan, M.Map Ident SourcePos) - -> CaseAlternative - -> m ((SourceSpan, M.Map Ident SourcePos), CaseAlternative) - updateCase (pos, bound) c@(CaseAlternative bs _) = - return ((pos, rUnionMap binderNamesWithSpans' bs `M.union` bound), c) - where - rUnionMap f = foldl' (flip (M.union . f)) M.empty - - updateGuard - :: (SourceSpan, M.Map Ident SourcePos) - -> Guard - -> m ((SourceSpan, M.Map Ident SourcePos), Guard) - updateGuard (pos, bound) g@(ConditionGuard _) = - return ((pos, bound), g) - updateGuard (pos, bound) g@(PatternGuard b _) = - return ((pos, binderNamesWithSpans' b `M.union` bound), g) - - binderNamesWithSpans' :: Binder -> M.Map Ident SourcePos - binderNamesWithSpans' - = M.fromList - . fmap (second spanStart . swap) - . binderNamesWithSpans - - letBoundVariable :: Declaration -> Maybe (Ident, SourceSpan) - letBoundVariable = fmap (valdeclIdent &&& (fst . valdeclSourceAnn)) . getValueDeclaration - - declarationsToMap :: [Declaration] -> M.Map Ident SourcePos - declarationsToMap = foldl goDTM M.empty - where - goDTM a (ValueDeclaration ValueDeclarationData {..}) = - M.insert valdeclIdent (spanStart $ fst valdeclSourceAnn) a - goDTM a _ = - a - - updateTypeArguments - :: (Traversable f, Traversable g) - => f (a, g SourceType) -> m (f (a, g SourceType)) - updateTypeArguments = traverse (sndM (traverse updateTypesEverywhere)) - - updateTypesEverywhere :: SourceType -> m SourceType - updateTypesEverywhere = everywhereOnTypesM updateType - where - updateType :: SourceType -> m SourceType - updateType (TypeOp ann@(ss, _) name) = TypeOp ann <$> updateTypeOpName name ss - updateType (TypeConstructor ann@(ss, _) name) = TypeConstructor ann <$> updateTypeName name ss - updateType (ConstrainedType ann c t) = ConstrainedType ann <$> updateInConstraint c <*> pure t - updateType t = return t - updateInConstraint :: SourceConstraint -> m SourceConstraint - updateInConstraint (Constraint ann@(ss, _) name ks ts info) = - Constraint ann <$> updateClassName name ss <*> pure ks <*> pure ts <*> pure info - - updateConstraints :: [SourceConstraint] -> m [SourceConstraint] - updateConstraints = traverse $ \(Constraint ann@(pos, _) name ks ts info) -> - Constraint ann - <$> updateClassName name pos - <*> traverse updateTypesEverywhere ks - <*> traverse updateTypesEverywhere ts - <*> pure info - - updateTypeName - :: Qualified (ProperName 'TypeName) - -> SourceSpan - -> m (Qualified (ProperName 'TypeName)) - updateTypeName = update (importedTypes imports) TyName - - updateTypeOpName - :: Qualified (OpName 'TypeOpName) - -> SourceSpan - -> m (Qualified (OpName 'TypeOpName)) - updateTypeOpName = update (importedTypeOps imports) TyOpName - - updateDataConstructorName - :: Qualified (ProperName 'ConstructorName) - -> SourceSpan - -> m (Qualified (ProperName 'ConstructorName)) - updateDataConstructorName = update (importedDataConstructors imports) DctorName - - updateClassName - :: Qualified (ProperName 'ClassName) - -> SourceSpan - -> m (Qualified (ProperName 'ClassName)) - updateClassName = update (importedTypeClasses imports) TyClassName - - updateValueName :: Qualified Ident -> SourceSpan -> m (Qualified Ident) - updateValueName = update (importedValues imports) IdentName - - updateValueOpName - :: Qualified (OpName 'ValueOpName) - -> SourceSpan - -> m (Qualified (OpName 'ValueOpName)) - updateValueOpName = update (importedValueOps imports) ValOpName - - -- Update names so unqualified references become qualified, and locally - -- qualified references are replaced with their canonical qualified names - -- (e.g. M.Map -> Data.Map.Map). - update - :: (Ord a) - => M.Map (Qualified a) [ImportRecord a] - -> (a -> Name) - -> Qualified a - -> SourceSpan - -> m (Qualified a) - update imps toName qname@(Qualified mn' name) pos = warnAndRethrowWithPosition pos $ - case (M.lookup qname imps, mn') of - - -- We found the name in our imports, so we return the name for it, - -- qualifying with the name of the module it was originally defined in - -- rather than the module we're importing from, to handle the case of - -- re-exports. If there are multiple options for the name to resolve to - -- in scope, we throw an error. - (Just options, _) -> do - (mnNew, mnOrig) <- checkImportConflicts pos mn toName options - modify $ \usedImports -> - M.insertWith (++) mnNew [fmap toName qname] usedImports - return $ Qualified (ByModuleName mnOrig) name - - -- If the name wasn't found in our imports but was qualified then we need - -- to check whether it's a failed import from a "pseudo" module (created - -- by qualified importing). If that's not the case, then we just need to - -- check it refers to a symbol in another module. - (Nothing, ByModuleName mn'') -> - if mn'' `S.member` importedQualModules imports || mn'' `S.member` importedModules imports - then throwUnknown - else throwError . errorMessage . UnknownName . Qualified ByNullSourcePos $ ModName mn'' - - -- If neither of the above cases are true then it's an undefined or - -- unimported symbol. - _ -> throwUnknown - - where - throwUnknown = throwError . errorMessage . UnknownName . fmap toName $ qname diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Common.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Common.hs deleted file mode 100644 index 572d35eb..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Common.hs +++ /dev/null @@ -1,68 +0,0 @@ -module Language.PureScript.Sugar.Names.Common (warnDuplicateRefs) where - -import Prelude -import Protolude (ordNub) - -import Control.Monad.Writer (MonadWriter(..)) - -import Data.Foldable (for_) -import Data.List (group, sort, (\\)) -import Data.Maybe (mapMaybe) - -import Language.PureScript.AST (DeclarationRef(..), SourceSpan) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage, errorMessage, warnWithPosition) -import Language.PureScript.Names (Name(..)) - --- | --- Warns about duplicate values in a list of declaration refs. --- -warnDuplicateRefs - :: MonadWriter MultipleErrors m - => SourceSpan - -> (Name -> SimpleErrorMessage) - -> [DeclarationRef] - -> m () -warnDuplicateRefs pos toError refs = do - let withoutCtors = deleteCtors `map` refs - dupeRefs = mapMaybe (refToName pos) $ removeUnique withoutCtors - dupeCtors = concat $ mapMaybe (extractCtors pos) refs - - for_ (dupeRefs ++ dupeCtors) $ \(pos', name) -> - warnWithPosition pos' . tell . errorMessage $ toError name - - where - - -- Removes all unique elements from list - -- as well as one of each duplicate. - -- Example: - -- removeUnique [1,2,2,3,3,3,4] == [2,3,3] - -- Note that it may be more correct to keep ALL duplicates, - -- but that requires additional changes in how warnings are printed. - -- Example of keeping all duplicates (not what this code currently does): - -- removeUnique [1,2,2,3,3,3,4] == [2,2,3,3,3] - removeUnique :: Ord a => [a] -> [a] - removeUnique = concatMap (drop 1) . group . sort - - -- Deletes the constructor information from TypeRefs so that only the - -- referenced type is used in the duplicate check - constructors are handled - -- separately - deleteCtors :: DeclarationRef -> DeclarationRef - deleteCtors (TypeRef sa pn _) = TypeRef sa pn Nothing - deleteCtors other = other - - -- Extracts the names of duplicate constructor references from TypeRefs. - extractCtors :: SourceSpan -> DeclarationRef -> Maybe [(SourceSpan, Name)] - extractCtors pos' (TypeRef _ _ (Just dctors)) = - let dupes = dctors \\ ordNub dctors - in if null dupes then Nothing else Just $ (pos',) . DctorName <$> dupes - extractCtors _ _ = Nothing - - -- Converts a DeclarationRef into a name for an error message. - refToName :: SourceSpan -> DeclarationRef -> Maybe (SourceSpan, Name) - refToName pos' (TypeRef _ name _) = Just (pos', TyName name) - refToName pos' (TypeOpRef _ op) = Just (pos', TyOpName op) - refToName pos' (ValueRef _ name) = Just (pos', IdentName name) - refToName pos' (ValueOpRef _ op) = Just (pos', ValOpName op) - refToName pos' (TypeClassRef _ name) = Just (pos', TyClassName name) - refToName pos' (ModuleRef _ name) = Just (pos', ModName name) - refToName _ _ = Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Env.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Env.hs deleted file mode 100644 index 092b8e24..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Env.hs +++ /dev/null @@ -1,502 +0,0 @@ -module Language.PureScript.Sugar.Names.Env - ( ImportRecord(..) - , ImportProvenance(..) - , Imports(..) - , nullImports - , Exports(..) - , nullExports - , Env - , primEnv - , primExports - , envModuleExports - , ExportMode(..) - , exportType - , exportTypeOp - , exportTypeClass - , exportValue - , exportValueOp - , checkImportConflicts - ) where - -import Prelude - -import Control.Monad (forM_, when, (>=>)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Writer.Class (MonadWriter(..)) - -import Data.Function (on) -import Data.Foldable (find) -import Data.List (groupBy, sortOn, delete) -import Data.Maybe (mapMaybe) -import Safe (headMay, headDef) -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.AST (ExportSource(..), SourceSpan, internalModuleSourceSpan, nullSourceSpan) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage, errorMessage') -import Language.PureScript.Names (Ident, ModuleName, Name(..), OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, disqualify, getQual) - --- | --- The details for an import: the name of the thing that is being imported --- (`A.x` if importing from `A`), the module that the thing was originally --- defined in (for re-export resolution), and the import provenance (see below). --- -data ImportRecord a = - ImportRecord - { importName :: Qualified a - , importSourceModule :: ModuleName - , importSourceSpan :: SourceSpan - , importProvenance :: ImportProvenance - } - deriving (Eq, Ord, Show) - --- | --- Used to track how an import was introduced into scope. This allows us to --- handle the one-open-import special case that allows a name conflict to become --- a warning rather than being an unresolvable situation. --- -data ImportProvenance - = FromImplicit - | FromExplicit - | Local - | Prim - deriving (Eq, Ord, Show) - -type ImportMap a = M.Map (Qualified a) [ImportRecord a] - --- | --- The imported declarations for a module, including the module's own members. --- -data Imports = Imports - { - -- | - -- Local names for types within a module mapped to their qualified names - -- - importedTypes :: ImportMap (ProperName 'TypeName) - -- | - -- Local names for type operators within a module mapped to their qualified names - -- - , importedTypeOps :: ImportMap (OpName 'TypeOpName) - -- | - -- Local names for data constructors within a module mapped to their qualified names - -- - , importedDataConstructors :: ImportMap (ProperName 'ConstructorName) - -- | - -- Local names for classes within a module mapped to their qualified names - -- - , importedTypeClasses :: ImportMap (ProperName 'ClassName) - -- | - -- Local names for values within a module mapped to their qualified names - -- - , importedValues :: ImportMap Ident - -- | - -- Local names for value operators within a module mapped to their qualified names - -- - , importedValueOps :: ImportMap (OpName 'ValueOpName) - -- | - -- The name of modules that have been imported into the current scope that - -- can be re-exported. If a module is imported with `as` qualification, the - -- `as` name appears here, otherwise the original name. - -- - , importedModules :: S.Set ModuleName - -- | - -- The "as" names of modules that have been imported qualified. - -- - , importedQualModules :: S.Set ModuleName - -- | - -- Local names for kinds within a module mapped to their qualified names - -- - , importedKinds :: ImportMap (ProperName 'TypeName) - } deriving (Show) - -nullImports :: Imports -nullImports = Imports M.empty M.empty M.empty M.empty M.empty M.empty S.empty S.empty M.empty - --- | --- The exported declarations from a module. --- -data Exports = Exports - { - -- | - -- The exported types along with the module they originally came from. - -- - exportedTypes :: M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - -- | - -- The exported type operators along with the module they originally came - -- from. - -- - , exportedTypeOps :: M.Map (OpName 'TypeOpName) ExportSource - -- | - -- The exported classes along with the module they originally came from. - -- - , exportedTypeClasses :: M.Map (ProperName 'ClassName) ExportSource - -- | - -- The exported values along with the module they originally came from. - -- - , exportedValues :: M.Map Ident ExportSource - -- | - -- The exported value operators along with the module they originally came - -- from. - -- - , exportedValueOps :: M.Map (OpName 'ValueOpName) ExportSource - } deriving (Show) - --- | --- An empty 'Exports' value. --- -nullExports :: Exports -nullExports = Exports M.empty M.empty M.empty M.empty M.empty - --- | --- The imports and exports for a collection of modules. The 'SourceSpan' is used --- to store the source location of the module with a given name, used to provide --- useful information when there is a duplicate module definition. --- -type Env = M.Map ModuleName (SourceSpan, Imports, Exports) - --- | --- Extracts the 'Exports' from an 'Env' value. --- -envModuleExports :: (a, b, Exports) -> Exports -envModuleExports (_, _, exps) = exps - --- | --- The exported types from the @Prim@ module --- -primExports :: Exports -primExports = mkPrimExports primTypes primClasses - --- | --- The exported types from the @Prim.Boolean@ module --- -primBooleanExports :: Exports -primBooleanExports = mkPrimExports primBooleanTypes mempty - --- | --- The exported types from the @Prim.Coerce@ module --- -primCoerceExports :: Exports -primCoerceExports = mkPrimExports primCoerceTypes primCoerceClasses - --- | --- The exported types from the @Prim.Ordering@ module --- -primOrderingExports :: Exports -primOrderingExports = mkPrimExports primOrderingTypes mempty - --- | --- The exported types from the @Prim.Row@ module --- -primRowExports :: Exports -primRowExports = mkPrimExports primRowTypes primRowClasses - --- | --- The exported types from the @Prim.RowList@ module --- -primRowListExports :: Exports -primRowListExports = mkPrimExports primRowListTypes primRowListClasses - --- | --- The exported types from the @Prim.Symbol@ module --- -primSymbolExports :: Exports -primSymbolExports = mkPrimExports primSymbolTypes primSymbolClasses - --- | --- The exported types from the @Prim.Int@ module -primIntExports :: Exports -primIntExports = mkPrimExports primIntTypes primIntClasses - --- | --- The exported types from the @Prim.TypeError@ module --- -primTypeErrorExports :: Exports -primTypeErrorExports = mkPrimExports primTypeErrorTypes primTypeErrorClasses - --- | --- Create a set of exports for a Prim module. --- -mkPrimExports - :: M.Map (Qualified (ProperName 'TypeName)) a - -> M.Map (Qualified (ProperName 'ClassName)) b - -> Exports -mkPrimExports ts cs = - nullExports - { exportedTypes = M.fromList $ mkTypeEntry `map` M.keys ts - , exportedTypeClasses = M.fromList $ mkClassEntry `map` M.keys cs - } - where - mkTypeEntry (Qualified (ByModuleName mn) name) = (name, ([], primExportSource mn)) - mkTypeEntry _ = internalError - "mkPrimExports.mkTypeEntry: a name is qualified BySourcePos instead of ByModuleName" - - mkClassEntry (Qualified (ByModuleName mn) name) = (name, primExportSource mn) - mkClassEntry _ = internalError - "mkPrimExports.mkClassEntry: a name is qualified BySourcePos instead of ByModuleName" - - primExportSource mn = - ExportSource - { exportSourceImportedFrom = Nothing - , exportSourceDefinedIn = mn - } - --- | Environment which only contains the Prim modules. -primEnv :: Env -primEnv = M.fromList - [ ( C.M_Prim - , (internalModuleSourceSpan "", nullImports, primExports) - ) - , ( C.M_Prim_Boolean - , (internalModuleSourceSpan "", nullImports, primBooleanExports) - ) - , ( C.M_Prim_Coerce - , (internalModuleSourceSpan "", nullImports, primCoerceExports) - ) - , ( C.M_Prim_Ordering - , (internalModuleSourceSpan "", nullImports, primOrderingExports) - ) - , ( C.M_Prim_Row - , (internalModuleSourceSpan "", nullImports, primRowExports) - ) - , ( C.M_Prim_RowList - , (internalModuleSourceSpan "", nullImports, primRowListExports) - ) - , ( C.M_Prim_Symbol - , (internalModuleSourceSpan "", nullImports, primSymbolExports) - ) - , ( C.M_Prim_Int - , (internalModuleSourceSpan "", nullImports, primIntExports) - ) - , ( C.M_Prim_TypeError - , (internalModuleSourceSpan "", nullImports, primTypeErrorExports) - ) - ] - --- | --- When updating the `Exports` the behaviour is slightly different depending --- on whether we are exporting values defined within the module or elaborating --- re-exported values. This type is used to indicate which behaviour should be --- used. --- -data ExportMode = Internal | ReExport - deriving (Eq, Show) - --- | --- Safely adds a type and its data constructors to some exports, returning an --- error if a conflict occurs. --- -exportType - :: MonadError MultipleErrors m - => SourceSpan - -> ExportMode - -> Exports - -> ProperName 'TypeName - -> [ProperName 'ConstructorName] - -> ExportSource - -> m Exports -exportType ss exportMode exps name dctors src = do - let exTypes = exportedTypes exps - exClasses = exportedTypeClasses exps - dctorNameCounts :: [(ProperName 'ConstructorName, Int)] - dctorNameCounts = M.toList $ M.fromListWith (+) (map (,1) dctors) - forM_ dctorNameCounts $ \(dctorName, count) -> - when (count > 1) $ - throwDeclConflict (DctorName dctorName) (DctorName dctorName) - case exportMode of - Internal -> do - when (name `M.member` exTypes) $ - throwDeclConflict (TyName name) (TyName name) - when (coerceProperName name `M.member` exClasses) $ - throwDeclConflict (TyName name) (TyClassName (coerceProperName name)) - forM_ dctors $ \dctor -> do - when ((elem dctor . fst) `any` exTypes) $ - throwDeclConflict (DctorName dctor) (DctorName dctor) - when (coerceProperName dctor `M.member` exClasses) $ - throwDeclConflict (DctorName dctor) (TyClassName (coerceProperName dctor)) - ReExport -> do - let mn = exportSourceDefinedIn src - forM_ (coerceProperName name `M.lookup` exClasses) $ \src' -> - let mn' = exportSourceDefinedIn src' in - throwExportConflict' ss mn mn' (TyName name) (TyClassName (coerceProperName name)) - forM_ (name `M.lookup` exTypes) $ \(_, src') -> - let mn' = exportSourceDefinedIn src' in - when (mn /= mn') $ - throwExportConflict ss mn mn' (TyName name) - forM_ dctors $ \dctor -> - forM_ ((elem dctor . fst) `find` exTypes) $ \(_, src') -> - let mn' = exportSourceDefinedIn src' in - when (mn /= mn') $ - throwExportConflict ss mn mn' (DctorName dctor) - return $ exps { exportedTypes = M.alter updateOrInsert name exTypes } - where - updateOrInsert Nothing = Just (dctors, src) - updateOrInsert (Just (dctors', _)) = Just (dctors ++ dctors', src) - --- | --- Safely adds a type operator to some exports, returning an error if a --- conflict occurs. --- -exportTypeOp - :: MonadError MultipleErrors m - => SourceSpan - -> Exports - -> OpName 'TypeOpName - -> ExportSource - -> m Exports -exportTypeOp ss exps op src = do - typeOps <- addExport ss TyOpName op src (exportedTypeOps exps) - return $ exps { exportedTypeOps = typeOps } - --- | --- Safely adds a class to some exports, returning an error if a conflict occurs. --- -exportTypeClass - :: MonadError MultipleErrors m - => SourceSpan - -> ExportMode - -> Exports - -> ProperName 'ClassName - -> ExportSource - -> m Exports -exportTypeClass ss exportMode exps name src = do - let exTypes = exportedTypes exps - when (exportMode == Internal) $ do - when (coerceProperName name `M.member` exTypes) $ - throwDeclConflict (TyClassName name) (TyName (coerceProperName name)) - when ((elem (coerceProperName name) . fst) `any` exTypes) $ - throwDeclConflict (TyClassName name) (DctorName (coerceProperName name)) - classes <- addExport ss TyClassName name src (exportedTypeClasses exps) - return $ exps { exportedTypeClasses = classes } - --- | --- Safely adds a value to some exports, returning an error if a conflict occurs. --- -exportValue - :: MonadError MultipleErrors m - => SourceSpan - -> Exports - -> Ident - -> ExportSource - -> m Exports -exportValue ss exps name src = do - values <- addExport ss IdentName name src (exportedValues exps) - return $ exps { exportedValues = values } - --- | --- Safely adds a value operator to some exports, returning an error if a --- conflict occurs. --- -exportValueOp - :: MonadError MultipleErrors m - => SourceSpan - -> Exports - -> OpName 'ValueOpName - -> ExportSource - -> m Exports -exportValueOp ss exps op src = do - valueOps <- addExport ss ValOpName op src (exportedValueOps exps) - return $ exps { exportedValueOps = valueOps } - --- | --- Adds an entry to a list of exports unless it is already present, in which --- case an error is returned. --- -addExport - :: (MonadError MultipleErrors m, Ord a) - => SourceSpan - -> (a -> Name) - -> a - -> ExportSource - -> M.Map a ExportSource - -> m (M.Map a ExportSource) -addExport ss toName name src exports = - case M.lookup name exports of - Just src' -> - let - mn = exportSourceDefinedIn src - mn' = exportSourceDefinedIn src' - in - if mn == mn' - then return exports - else throwExportConflict ss mn mn' (toName name) - Nothing -> - return $ M.insert name src exports - --- | --- Raises an error for when there is more than one definition for something. --- -throwDeclConflict - :: MonadError MultipleErrors m - => Name - -> Name - -> m a -throwDeclConflict new existing = - throwError . errorMessage $ DeclConflict new existing - --- | --- Raises an error for when there are conflicting names in the exports. --- -throwExportConflict - :: MonadError MultipleErrors m - => SourceSpan - -> ModuleName - -> ModuleName - -> Name - -> m a -throwExportConflict ss new existing name = - throwExportConflict' ss new existing name name - --- | --- Raises an error for when there are conflicting names in the exports. Allows --- different categories of names. E.g. class and type names conflicting. --- -throwExportConflict' - :: MonadError MultipleErrors m - => SourceSpan - -> ModuleName - -> ModuleName - -> Name - -> Name - -> m a -throwExportConflict' ss new existing newName existingName = - throwError . errorMessage' ss $ - ExportConflict (Qualified (ByModuleName new) newName) (Qualified (ByModuleName existing) existingName) - --- | --- When reading a value from the imports, check that there are no conflicts in --- scope. --- -checkImportConflicts - :: forall m a - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => SourceSpan - -> ModuleName - -> (a -> Name) - -> [ImportRecord a] - -> m (ModuleName, ModuleName) -checkImportConflicts ss currentModule toName xs = - let - byOrig = sortOn importSourceModule xs - groups = groupBy ((==) `on` importSourceModule) byOrig - nonImplicit = filter ((/= FromImplicit) . importProvenance) xs - name = toName . disqualify . importName $ - headDef (internalError "checkImportConflicts: No imports found") xs - conflictModules = mapMaybe (headMay >=> (getQual . importName)) groups - in - if length groups > 1 - then case nonImplicit of - [ImportRecord (Qualified (ByModuleName mnNew) _) mnOrig _ _] -> do - let warningModule = if mnNew == currentModule then Nothing else Just mnNew - ss' = maybe nullSourceSpan importSourceSpan . headMay . filter ((== FromImplicit) . importProvenance) $ xs - tell . errorMessage' ss' $ ScopeShadowing name warningModule $ delete mnNew conflictModules - return (mnNew, mnOrig) - _ -> throwError . errorMessage' ss $ ScopeConflict name conflictModules - else - case headMay byOrig of - Just (ImportRecord (Qualified (ByModuleName mnNew) _) mnOrig _ _) -> - return (mnNew, mnOrig) - _ -> - internalError "checkImportConflicts: ImportRecord should be qualified" diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Exports.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Exports.hs deleted file mode 100644 index 67b1560a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Exports.hs +++ /dev/null @@ -1,306 +0,0 @@ -module Language.PureScript.Sugar.Names.Exports - ( findExportable - , resolveExports - ) where - -import Prelude -import Protolude (headDef) - -import Control.Monad (filterM, foldM, liftM2, unless, void, when) -import Control.Monad.Writer.Class (MonadWriter(..)) -import Control.Monad.Error.Class (MonadError(..)) - -import Data.Function (on) -import Data.Foldable (traverse_) -import Data.List (intersect, groupBy, sortOn) -import Data.Maybe (fromMaybe, mapMaybe) -import Data.Map qualified as M - -import Language.PureScript.AST -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage', rethrow, rethrowWithPosition, warnAndRethrow) -import Language.PureScript.Names (Ident, ModuleName, Name(..), OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), disqualifyFor, isQualifiedWith, isUnqualified) -import Language.PureScript.Sugar.Names.Env (Env, ExportMode(..), Exports(..), ImportRecord(..), Imports(..), checkImportConflicts, envModuleExports, exportType, exportTypeClass, exportTypeOp, exportValue, exportValueOp, nullExports) -import Language.PureScript.Sugar.Names.Common (warnDuplicateRefs) - --- | --- Finds all exportable members of a module, disregarding any explicit exports. --- -findExportable :: forall m. (MonadError MultipleErrors m) => Module -> m Exports -findExportable (Module _ _ mn ds _) = - rethrow (addHint (ErrorInModule mn)) $ foldM updateExports' nullExports ds - where - updateExports' :: Exports -> Declaration -> m Exports - updateExports' exps decl = rethrowWithPosition (declSourceSpan decl) $ updateExports exps decl - - source = - ExportSource - { exportSourceDefinedIn = mn - , exportSourceImportedFrom = Nothing - } - - updateExports :: Exports -> Declaration -> m Exports - updateExports exps (TypeClassDeclaration (ss, _) tcn _ _ _ ds') = do - exps' <- rethrowWithPosition ss $ exportTypeClass ss Internal exps tcn source - foldM go exps' ds' - where - go exps'' (TypeDeclaration (TypeDeclarationData (ss', _) name _)) = exportValue ss' exps'' name source - go _ _ = internalError "Invalid declaration in TypeClassDeclaration" - updateExports exps (DataDeclaration (ss, _) _ tn _ dcs) = - exportType ss Internal exps tn (map dataCtorName dcs) source - updateExports exps (TypeSynonymDeclaration (ss, _) tn _ _) = - exportType ss Internal exps tn [] source - updateExports exps (ExternDataDeclaration (ss, _) tn _) = - exportType ss Internal exps tn [] source - updateExports exps (ValueDeclaration vd) = - exportValue (fst (valdeclSourceAnn vd)) exps (valdeclIdent vd) source - updateExports exps (ValueFixityDeclaration (ss, _) _ _ op) = - exportValueOp ss exps op source - updateExports exps (TypeFixityDeclaration (ss, _) _ _ op) = - exportTypeOp ss exps op source - updateExports exps (ExternDeclaration (ss, _) name _) = - exportValue ss exps name source - updateExports exps _ = return exps - --- | --- Resolves the exports for a module, filtering out members that have not been --- exported and elaborating re-exports of other modules. --- -resolveExports - :: forall m - . (MonadError MultipleErrors m, MonadWriter MultipleErrors m) - => Env - -> SourceSpan - -> ModuleName - -> Imports - -> Exports - -> [DeclarationRef] - -> m Exports -resolveExports env ss mn imps exps refs = - warnAndRethrow (addHint (ErrorInModule mn)) $ do - filtered <- filterModule mn exps refs - exps' <- foldM elaborateModuleExports filtered refs - warnDuplicateRefs ss DuplicateExportRef refs - return exps' - - where - - -- Takes the current module's imports, the accumulated list of exports, and a - -- `DeclarationRef` for an explicit export. When the ref refers to another - -- module, export anything from the imports that matches for that module. - elaborateModuleExports :: Exports -> DeclarationRef -> m Exports - elaborateModuleExports result (ModuleRef _ name) | name == mn = do - let types' = exportedTypes result `M.union` exportedTypes exps - let typeOps' = exportedTypeOps result `M.union` exportedTypeOps exps - let classes' = exportedTypeClasses result `M.union` exportedTypeClasses exps - let values' = exportedValues result `M.union` exportedValues exps - let valueOps' = exportedValueOps result `M.union` exportedValueOps exps - return result - { exportedTypes = types' - , exportedTypeOps = typeOps' - , exportedTypeClasses = classes' - , exportedValues = values' - , exportedValueOps = valueOps' - } - elaborateModuleExports result (ModuleRef ss' name) = do - let isPseudo = isPseudoModule name - when (not isPseudo && not (isImportedModule name)) - . throwError . errorMessage' ss' . UnknownExport $ ModName name - reTypes <- extract ss' isPseudo name TyName (importedTypes imps) - reTypeOps <- extract ss' isPseudo name TyOpName (importedTypeOps imps) - reDctors <- extract ss' isPseudo name DctorName (importedDataConstructors imps) - reClasses <- extract ss' isPseudo name TyClassName (importedTypeClasses imps) - reValues <- extract ss' isPseudo name IdentName (importedValues imps) - reValueOps <- extract ss' isPseudo name ValOpName (importedValueOps imps) - foldM (\exps' ((tctor, dctors), src) -> exportType ss' ReExport exps' tctor dctors src) result (resolveTypeExports reTypes reDctors) - >>= flip (foldM (uncurry . exportTypeOp ss')) (map resolveTypeOp reTypeOps) - >>= flip (foldM (uncurry . exportTypeClass ss' ReExport)) (map resolveClass reClasses) - >>= flip (foldM (uncurry . exportValue ss')) (map resolveValue reValues) - >>= flip (foldM (uncurry . exportValueOp ss')) (map resolveValueOp reValueOps) - elaborateModuleExports result _ = return result - - -- Extracts a list of values for a module based on a lookup table. If the - -- boolean is true the values are filtered by the qualification - extract - :: SourceSpan - -> Bool - -> ModuleName - -> (a -> Name) - -> M.Map (Qualified a) [ImportRecord a] - -> m [Qualified a] - extract ss' useQual name toName = - fmap (map (importName . headDef (internalError "Missing value in extract") . snd)) . go . M.toList - where - go = filterM $ \(name', options) -> do - let isMatch = if useQual then isQualifiedWith name name' else any (checkUnqual name') options - when (isMatch && length options > 1) $ void $ checkImportConflicts ss' mn toName options - return isMatch - checkUnqual name' ir = isUnqualified name' && isQualifiedWith name (importName ir) - - -- Check whether a module name refers to a "pseudo module" that came into - -- existence in an import scope due to importing one or more modules as - -- qualified. - isPseudoModule :: ModuleName -> Bool - isPseudoModule = testQuals M.keys - where - -- Test for the presence of a `ModuleName` in a set of imports, using a - -- function to either extract the keys or values. We test the keys to see if a - -- value being re-exported belongs to a qualified module, and we test the - -- values if that fails to see whether the value has been imported at all. - testQuals :: (forall a b. M.Map (Qualified a) b -> [Qualified a]) -> ModuleName -> Bool - testQuals f mn' = any (isQualifiedWith mn') (f (importedTypes imps)) - || any (isQualifiedWith mn') (f (importedTypeOps imps)) - || any (isQualifiedWith mn') (f (importedDataConstructors imps)) - || any (isQualifiedWith mn') (f (importedTypeClasses imps)) - || any (isQualifiedWith mn') (f (importedValues imps)) - || any (isQualifiedWith mn') (f (importedValueOps imps)) - || any (isQualifiedWith mn') (f (importedKinds imps)) - - -- Check whether a module name refers to a module that has been imported - -- without qualification into an import scope. - isImportedModule :: ModuleName -> Bool - isImportedModule = flip elem (importedModules imps) - - -- Constructs a list of types with their data constructors and the original - -- module they were defined in from a list of type and data constructor names. - resolveTypeExports - :: [Qualified (ProperName 'TypeName)] - -> [Qualified (ProperName 'ConstructorName)] - -> [((ProperName 'TypeName, [ProperName 'ConstructorName]), ExportSource)] - resolveTypeExports tctors dctors = map go tctors - where - go - :: Qualified (ProperName 'TypeName) - -> ((ProperName 'TypeName, [ProperName 'ConstructorName]), ExportSource) - go (Qualified (ByModuleName mn'') name) = - fromMaybe (internalError "Missing value in resolveTypeExports") $ do - exps' <- envModuleExports <$> mn'' `M.lookup` env - (dctors', src) <- name `M.lookup` exportedTypes exps' - let relevantDctors = mapMaybe (disqualifyFor (Just mn'')) dctors - return - ( (name, relevantDctors `intersect` dctors') - , src { exportSourceImportedFrom = Just mn'' } - ) - go (Qualified _ _) = internalError "Unqualified value in resolveTypeExports" - - -- Looks up an imported type operator and re-qualifies it with the original - -- module it came from. - resolveTypeOp :: Qualified (OpName 'TypeOpName) -> (OpName 'TypeOpName, ExportSource) - resolveTypeOp op - = fromMaybe (internalError "Missing value in resolveValue") - $ resolve exportedTypeOps op - - -- Looks up an imported class and re-qualifies it with the original module it - -- came from. - resolveClass :: Qualified (ProperName 'ClassName) -> (ProperName 'ClassName, ExportSource) - resolveClass className - = fromMaybe (internalError "Missing value in resolveClass") - $ resolve exportedTypeClasses className - - -- Looks up an imported value and re-qualifies it with the original module it - -- came from. - resolveValue :: Qualified Ident -> (Ident, ExportSource) - resolveValue ident - = fromMaybe (internalError "Missing value in resolveValue") - $ resolve exportedValues ident - - -- Looks up an imported operator and re-qualifies it with the original - -- module it came from. - resolveValueOp :: Qualified (OpName 'ValueOpName) -> (OpName 'ValueOpName, ExportSource) - resolveValueOp op - = fromMaybe (internalError "Missing value in resolveValueOp") - $ resolve exportedValueOps op - - resolve - :: Ord a - => (Exports -> M.Map a ExportSource) - -> Qualified a - -> Maybe (a, ExportSource) - resolve f (Qualified (ByModuleName mn'') a) = do - exps' <- envModuleExports <$> mn'' `M.lookup` env - src <- a `M.lookup` f exps' - return (a, src { exportSourceImportedFrom = Just mn'' }) - resolve _ _ = internalError "Unqualified value in resolve" - --- | --- Filters the full list of exportable values, types, and classes for a module --- based on a list of export declaration references. --- -filterModule - :: forall m - . MonadError MultipleErrors m - => ModuleName - -> Exports - -> [DeclarationRef] - -> m Exports -filterModule mn exps refs = do - types <- foldM filterTypes M.empty (combineTypeRefs refs) - typeOps <- foldM (filterExport TyOpName getTypeOpRef exportedTypeOps) M.empty refs - classes <- foldM (filterExport TyClassName getTypeClassRef exportedTypeClasses) M.empty refs - values <- foldM (filterExport IdentName getValueRef exportedValues) M.empty refs - valueOps <- foldM (filterExport ValOpName getValueOpRef exportedValueOps) M.empty refs - return Exports - { exportedTypes = types - , exportedTypeOps = typeOps - , exportedTypeClasses = classes - , exportedValues = values - , exportedValueOps = valueOps - } - - where - - -- Takes the list of exported refs, filters out any non-TypeRefs, then - -- combines any duplicate type exports to ensure that all constructors - -- listed for the type are covered. Without this, only the data constructor - -- listing for the last ref would be used. - combineTypeRefs :: [DeclarationRef] -> [DeclarationRef] - combineTypeRefs - = fmap (\(ss', (tc, dcs)) -> TypeRef ss' tc dcs) - . fmap (foldr1 $ \(ss, (tc, dcs1)) (_, (_, dcs2)) -> (ss, (tc, liftM2 (++) dcs1 dcs2))) - . groupBy ((==) `on` (fst . snd)) - . sortOn (fst . snd) - . mapMaybe (\ref -> (declRefSourceSpan ref,) <$> getTypeRef ref) - - filterTypes - :: M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - -> DeclarationRef - -> m (M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource)) - filterTypes result (TypeRef ss name expDcons) = - case name `M.lookup` exportedTypes exps of - Nothing -> throwError . errorMessage' ss . UnknownExport $ TyName name - Just (dcons, src) -> do - let expDcons' = fromMaybe dcons expDcons - traverse_ (checkDcon name dcons) expDcons' - return $ M.insert name (expDcons', src) result - where - -- Ensures a data constructor is exportable for a given type. Takes a type - -- name, a list of exportable data constructors for the type, and the name of - -- the data constructor to check. - checkDcon - :: ProperName 'TypeName - -> [ProperName 'ConstructorName] - -> ProperName 'ConstructorName - -> m () - checkDcon tcon dcons dcon = - unless (dcon `elem` dcons) . - throwError . errorMessage' ss $ UnknownExportDataConstructor tcon dcon - filterTypes result _ = return result - - filterExport - :: Ord a - => (a -> Name) - -> (DeclarationRef -> Maybe a) - -> (Exports -> M.Map a ExportSource) - -> M.Map a ExportSource - -> DeclarationRef - -> m (M.Map a ExportSource) - filterExport toName get fromExps result ref - | Just name <- get ref = - case name `M.lookup` fromExps exps of - -- TODO: I'm not sure if we actually need to check that these modules - -- are the same here -gb - Just source' | mn == exportSourceDefinedIn source' -> - return $ M.insert name source' result - _ -> - throwError . errorMessage' (declRefSourceSpan ref) . UnknownExport $ toName name - filterExport _ _ _ result _ = return result diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Imports.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Imports.hs deleted file mode 100644 index 77c65ba3..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Names/Imports.hs +++ /dev/null @@ -1,229 +0,0 @@ -module Language.PureScript.Sugar.Names.Imports - ( ImportDef - , resolveImports - , resolveModuleImport - , findImports - ) where - -import Prelude - -import Control.Monad (foldM, when, unless) -import Control.Monad.Error.Class (MonadError(..)) - -import Data.Foldable (for_, traverse_) -import Data.Maybe (fromMaybe) -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.AST (Declaration(..), DeclarationRef(..), ErrorMessageHint(..), ExportSource(..), ImportDeclarationType(..), Module(..), SourceSpan, internalModuleSourceSpan) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage', rethrow) -import Language.PureScript.Names (pattern ByNullSourcePos, ModuleName, Name(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), byMaybeModuleName) -import Language.PureScript.Sugar.Names.Env (Env, Exports(..), ImportProvenance(..), ImportRecord(..), Imports(..), envModuleExports, nullImports) - -type ImportDef = (SourceSpan, ImportDeclarationType, Maybe ModuleName) - --- | --- Finds the imports within a module, mapping the imported module name to an optional set of --- explicitly imported declarations. --- -findImports - :: [Declaration] - -> M.Map ModuleName [ImportDef] -findImports = foldr go M.empty - where - go (ImportDeclaration (pos, _) mn typ qual) = - M.alter (return . ((pos, typ, qual) :) . fromMaybe []) mn - go _ = id - --- | --- Constructs a set of imports for a module. --- -resolveImports - :: forall m - . MonadError MultipleErrors m - => Env - -> Module - -> m (Module, Imports) -resolveImports env (Module ss coms currentModule decls exps) = - rethrow (addHint (ErrorInModule currentModule)) $ do - let imports = findImports decls - imports' = M.map (map (\(ss', dt, mmn) -> (ss', Just dt, mmn))) imports - scope = M.insert currentModule [(internalModuleSourceSpan "", Nothing, Nothing)] imports' - (Module ss coms currentModule decls exps,) <$> - foldM (resolveModuleImport env) nullImports (M.toList scope) - --- | Constructs a set of imports for a single module import. -resolveModuleImport - :: forall m - . MonadError MultipleErrors m - => Env - -> Imports - -> (ModuleName, [(SourceSpan, Maybe ImportDeclarationType, Maybe ModuleName)]) - -> m Imports -resolveModuleImport env ie (mn, imps) = foldM go ie imps - where - go :: Imports - -> (SourceSpan, Maybe ImportDeclarationType, Maybe ModuleName) - -> m Imports - go ie' (ss, typ, impQual) = do - modExports <- - maybe - (throwError . errorMessage' ss . UnknownName . Qualified ByNullSourcePos $ ModName mn) - (return . envModuleExports) - (mn `M.lookup` env) - let impModules = importedModules ie' - qualModules = importedQualModules ie' - ie'' = ie' { importedModules = maybe (S.insert mn impModules) (const impModules) impQual - , importedQualModules = maybe qualModules (`S.insert` qualModules) impQual - } - resolveImport mn modExports ie'' impQual ss typ - --- | --- Extends the local environment for a module by resolving an import of another module. --- -resolveImport - :: forall m - . MonadError MultipleErrors m - => ModuleName - -> Exports - -> Imports - -> Maybe ModuleName - -> SourceSpan - -> Maybe ImportDeclarationType - -> m Imports -resolveImport importModule exps imps impQual = resolveByType - where - - resolveByType :: SourceSpan -> Maybe ImportDeclarationType -> m Imports - resolveByType ss Nothing = - importAll ss (importRef Local) - resolveByType ss (Just Implicit) = - importAll ss (importRef FromImplicit) - resolveByType _ (Just (Explicit refs)) = - checkRefs False refs >> foldM (importRef FromExplicit) imps refs - resolveByType ss (Just (Hiding refs)) = - checkRefs True refs >> importAll ss (importNonHidden refs) - - -- Check that a 'DeclarationRef' refers to an importable symbol - checkRefs :: Bool -> [DeclarationRef] -> m () - checkRefs isHiding = traverse_ check - where - check (ValueRef ss name) = - checkImportExists ss IdentName (exportedValues exps) name - check (ValueOpRef ss op) = - checkImportExists ss ValOpName (exportedValueOps exps) op - check (TypeRef ss name dctors) = do - checkImportExists ss TyName (exportedTypes exps) name - let (allDctors, _) = allExportedDataConstructors name - for_ dctors $ traverse_ (checkDctorExists ss name allDctors) - check (TypeOpRef ss name) = - checkImportExists ss TyOpName (exportedTypeOps exps) name - check (TypeClassRef ss name) = - checkImportExists ss TyClassName (exportedTypeClasses exps) name - check (ModuleRef ss name) | isHiding = - throwError . errorMessage' ss $ ImportHidingModule name - check r = internalError $ "Invalid argument to checkRefs: " ++ show r - - -- Check that an explicitly imported item exists in the module it is being imported from - checkImportExists - :: Ord a - => SourceSpan - -> (a -> Name) - -> M.Map a b - -> a - -> m () - checkImportExists ss toName exports item - = when (item `M.notMember` exports) - . throwError . errorMessage' ss - $ UnknownImport importModule (toName item) - - -- Ensure that an explicitly imported data constructor exists for the type it is being imported - -- from - checkDctorExists - :: SourceSpan - -> ProperName 'TypeName - -> [ProperName 'ConstructorName] - -> ProperName 'ConstructorName - -> m () - checkDctorExists ss tcon exports dctor - = unless (dctor `elem` exports) - . throwError . errorMessage' ss - $ UnknownImportDataConstructor importModule tcon dctor - - importNonHidden :: [DeclarationRef] -> Imports -> DeclarationRef -> m Imports - importNonHidden hidden m ref | isHidden ref = return m - | otherwise = importRef FromImplicit m ref - where - -- TODO: rework this to be not confusing - isHidden :: DeclarationRef -> Bool - isHidden ref'@TypeRef{} = foldl (checkTypeRef ref') False hidden - isHidden ref' = ref' `elem` hidden - checkTypeRef :: DeclarationRef -> Bool -> DeclarationRef -> Bool - checkTypeRef _ True _ = True - checkTypeRef (TypeRef _ _ Nothing) acc (TypeRef _ _ (Just _)) = acc - checkTypeRef (TypeRef _ name (Just dctor)) _ (TypeRef _ name' (Just dctor')) = name == name' && dctor == dctor' - checkTypeRef (TypeRef _ name _) _ (TypeRef _ name' Nothing) = name == name' - checkTypeRef _ acc _ = acc - - -- Import all symbols - importAll :: SourceSpan -> (Imports -> DeclarationRef -> m Imports) -> m Imports - importAll ss importer = - foldM (\m (name, (dctors, _)) -> importer m (TypeRef ss name (Just dctors))) imps (M.toList (exportedTypes exps)) - >>= flip (foldM (\m (name, _) -> importer m (TypeOpRef ss name))) (M.toList (exportedTypeOps exps)) - >>= flip (foldM (\m (name, _) -> importer m (ValueRef ss name))) (M.toList (exportedValues exps)) - >>= flip (foldM (\m (name, _) -> importer m (ValueOpRef ss name))) (M.toList (exportedValueOps exps)) - >>= flip (foldM (\m (name, _) -> importer m (TypeClassRef ss name))) (M.toList (exportedTypeClasses exps)) - - importRef :: ImportProvenance -> Imports -> DeclarationRef -> m Imports - importRef prov imp (ValueRef ss name) = do - let values' = updateImports (importedValues imp) (exportedValues exps) id name ss prov - return $ imp { importedValues = values' } - importRef prov imp (ValueOpRef ss name) = do - let valueOps' = updateImports (importedValueOps imp) (exportedValueOps exps) id name ss prov - return $ imp { importedValueOps = valueOps' } - importRef prov imp (TypeRef ss name dctors) = do - let types' = updateImports (importedTypes imp) (exportedTypes exps) snd name ss prov - let (dctorNames, src) = allExportedDataConstructors name - dctorLookup :: M.Map (ProperName 'ConstructorName) ExportSource - dctorLookup = M.fromList $ map (, src) dctorNames - traverse_ (traverse_ $ checkDctorExists ss name dctorNames) dctors - let dctors' = foldl (\m d -> updateImports m dctorLookup id d ss prov) (importedDataConstructors imp) (fromMaybe dctorNames dctors) - return $ imp { importedTypes = types', importedDataConstructors = dctors' } - importRef prov imp (TypeOpRef ss name) = do - let ops' = updateImports (importedTypeOps imp) (exportedTypeOps exps) id name ss prov - return $ imp { importedTypeOps = ops' } - importRef prov imp (TypeClassRef ss name) = do - let typeClasses' = updateImports (importedTypeClasses imp) (exportedTypeClasses exps) id name ss prov - return $ imp { importedTypeClasses = typeClasses' } - importRef _ _ TypeInstanceRef{} = internalError "TypeInstanceRef in importRef" - importRef _ _ ModuleRef{} = internalError "ModuleRef in importRef" - importRef _ _ ReExportRef{} = internalError "ReExportRef in importRef" - - -- Find all exported data constructors for a given type - allExportedDataConstructors - :: ProperName 'TypeName - -> ([ProperName 'ConstructorName], ExportSource) - allExportedDataConstructors name = - fromMaybe (internalError "Invalid state in allExportedDataConstructors") - $ name `M.lookup` exportedTypes exps - - -- Add something to an import resolution list - updateImports - :: Ord a - => M.Map (Qualified a) [ImportRecord a] - -> M.Map a b - -> (b -> ExportSource) - -> a - -> SourceSpan - -> ImportProvenance - -> M.Map (Qualified a) [ImportRecord a] - updateImports imps' exps' expName name ss prov = - let - src = maybe (internalError "Invalid state in updateImports") expName (name `M.lookup` exps') - rec = ImportRecord (Qualified (ByModuleName importModule) name) (exportSourceDefinedIn src) ss prov - in - M.alter - (\currNames -> Just $ rec : fromMaybe [] currNames) - (Qualified (byMaybeModuleName impQual) name) - imps' diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/ObjectWildcards.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/ObjectWildcards.hs deleted file mode 100644 index 88b93b89..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/ObjectWildcards.hs +++ /dev/null @@ -1,101 +0,0 @@ -module Language.PureScript.Sugar.ObjectWildcards - ( desugarObjectConstructors - , desugarDecl - ) where - -import Prelude - -import Control.Monad (forM) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Supply.Class (MonadSupply) -import Data.Foldable (toList) -import Data.List (foldl') -import Data.Maybe (catMaybes) -import Language.PureScript.AST -import Language.PureScript.Environment (NameKind(..)) -import Language.PureScript.Errors (MultipleErrors, rethrowWithPosition) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident, Qualified(..), freshIdent') -import Language.PureScript.PSString (PSString) - - -desugarObjectConstructors - :: forall m - . (MonadSupply m, MonadError MultipleErrors m) - => Module - -> m Module -desugarObjectConstructors (Module ss coms mn ds exts) = Module ss coms mn <$> mapM desugarDecl ds <*> pure exts - -desugarDecl :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Declaration -> m Declaration -desugarDecl d = rethrowWithPosition (declSourceSpan d) $ fn d - where - (fn, _, _) = everywhereOnValuesTopDownM return desugarExpr return - - desugarExpr :: Expr -> m Expr - desugarExpr (Literal ss (ObjectLiteral ps)) = wrapLambdaAssoc (Literal ss . ObjectLiteral) ps - desugarExpr (ObjectUpdateNested obj ps) = transformNestedUpdate obj ps - desugarExpr (Accessor prop u) - | Just props <- peelAnonAccessorChain u = do - arg <- freshIdent' - return $ Abs (VarBinder nullSourceSpan arg) $ foldr Accessor (argToExpr arg) (prop:props) - desugarExpr (Case args cas) | any isAnonymousArgument args = do - argIdents <- forM args freshIfAnon - let args' = zipWith (`maybe` argToExpr) args argIdents - return $ foldr (Abs . VarBinder nullSourceSpan) (Case args' cas) (catMaybes argIdents) - desugarExpr (IfThenElse u t f) | any isAnonymousArgument [u, t, f] = do - u' <- freshIfAnon u - t' <- freshIfAnon t - f' <- freshIfAnon f - let if_ = IfThenElse (maybe u argToExpr u') (maybe t argToExpr t') (maybe f argToExpr f') - return $ foldr (Abs . VarBinder nullSourceSpan) if_ (catMaybes [u', t', f']) - desugarExpr e = return e - - transformNestedUpdate :: Expr -> PathTree Expr -> m Expr - transformNestedUpdate obj ps = do - -- If we don't have an anonymous argument then we need to generate a let wrapper - -- so that the object expression isn't re-evaluated for each nested update. - val <- freshIdent' - let valExpr = argToExpr val - if isAnonymousArgument obj - then Abs (VarBinder nullSourceSpan val) <$> wrapLambda (buildUpdates valExpr) ps - else wrapLambda (buildLet val . buildUpdates valExpr) ps - where - buildLet val = Let FromLet [ValueDecl (declSourceSpan d, []) val Public [] [MkUnguarded obj]] - - -- recursively build up the nested `ObjectUpdate` expressions - buildUpdates :: Expr -> PathTree Expr -> Expr - buildUpdates val (PathTree vs) = ObjectUpdate val (goLayer [] <$> runAssocList vs) where - goLayer :: [PSString] -> (PSString, PathNode Expr) -> (PSString, Expr) - goLayer _ (key, Leaf expr) = (key, expr) - goLayer path (key, Branch (PathTree branch)) = - let path' = path ++ [key] - updates = goLayer path' <$> runAssocList branch - accessor = foldl' (flip Accessor) val path' - objectUpdate = ObjectUpdate accessor updates - in (key, objectUpdate) - - wrapLambda :: forall t. Traversable t => (t Expr -> Expr) -> t Expr -> m Expr - wrapLambda mkVal ps = do - args <- traverse processExpr ps - return $ foldr (Abs . VarBinder nullSourceSpan) (mkVal (snd <$> args)) (catMaybes $ toList (fst <$> args)) - where - processExpr :: Expr -> m (Maybe Ident, Expr) - processExpr e = do - arg <- freshIfAnon e - return (arg, maybe e argToExpr arg) - - wrapLambdaAssoc :: ([(PSString, Expr)] -> Expr) -> [(PSString, Expr)] -> m Expr - wrapLambdaAssoc mkVal = wrapLambda (mkVal . runAssocList) . AssocList - - peelAnonAccessorChain :: Expr -> Maybe [PSString] - peelAnonAccessorChain (Accessor p e) = (p :) <$> peelAnonAccessorChain e - peelAnonAccessorChain (PositionedValue _ _ e) = peelAnonAccessorChain e - peelAnonAccessorChain AnonymousArgument = Just [] - peelAnonAccessorChain _ = Nothing - - freshIfAnon :: Expr -> m (Maybe Ident) - freshIfAnon u - | isAnonymousArgument u = Just <$> freshIdent' - | otherwise = return Nothing - - argToExpr :: Ident -> Expr - argToExpr = Var nullSourceSpan . Qualified ByNullSourcePos diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators.hs deleted file mode 100644 index 93028d7e..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators.hs +++ /dev/null @@ -1,496 +0,0 @@ --- | --- This module implements the desugaring pass which reapplies binary operators based --- on their fixity data and removes explicit parentheses. --- --- The value parser ignores fixity data when parsing binary operator applications, so --- it is necessary to reorder them here. --- -module Language.PureScript.Sugar.Operators - ( desugarSignedLiterals - , RebracketCaller(..) - , rebracket - , rebracketFiltered - , checkFixityExports - ) where - -import Prelude - -import Language.PureScript.AST -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage, errorMessage', parU, rethrow, rethrowWithPosition) -import Language.PureScript.Externs (ExternsFile(..), ExternsFixity(..), ExternsTypeFixity(..)) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), Name(..), OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), freshIdent') -import Language.PureScript.Sugar.Operators.Binders (matchBinderOperators) -import Language.PureScript.Sugar.Operators.Expr (matchExprOperators) -import Language.PureScript.Sugar.Operators.Types (matchTypeOperators) -import Language.PureScript.Traversals (defS, sndM) -import Language.PureScript.Types (Constraint(..), SourceType, Type(..), everywhereOnTypesTopDownM, overConstraintArgs) - -import Control.Monad (unless, (<=<)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Supply.Class (MonadSupply) - -import Data.Either (partitionEithers) -import Data.Foldable (for_, traverse_) -import Data.Function (on) -import Data.Functor (($>)) -import Data.Functor.Identity (Identity(..), runIdentity) -import Data.List (groupBy, sortOn) -import Data.Maybe (mapMaybe, listToMaybe) -import Data.Map qualified as M -import Data.Ord (Down(..)) - -import Language.PureScript.Constants.Libs qualified as C - --- | --- Removes unary negation operators and replaces them with calls to `negate`. --- -desugarSignedLiterals :: Module -> Module -desugarSignedLiterals (Module ss coms mn ds exts) = - Module ss coms mn (map f' ds) exts - where - (f', _, _) = everywhereOnValues id go id - go (UnaryMinus ss' val) = App (Var ss' (Qualified ByNullSourcePos (Ident C.S_negate))) val - go other = other - --- | --- An operator associated with its declaration position, fixity, and the name --- of the function or data constructor it is an alias for. --- -type FixityRecord op alias = (Qualified op, SourceSpan, Fixity, Qualified alias) -type ValueFixityRecord = FixityRecord (OpName 'ValueOpName) (Either Ident (ProperName 'ConstructorName)) -type TypeFixityRecord = FixityRecord (OpName 'TypeOpName) (ProperName 'TypeName) - --- | --- Remove explicit parentheses and reorder binary operator applications. --- --- This pass requires name desugaring and export elaboration to have run first. --- -rebracket - :: forall m - . MonadError MultipleErrors m - => MonadSupply m - => [ExternsFile] - -> Module - -> m Module -rebracket = - rebracketFiltered CalledByCompile (const True) - --- | --- A version of `rebracket` which allows you to choose which declarations --- should be affected. This is used in docs generation, where we want to --- desugar type operators in instance declarations to ensure that instances are --- paired up with their types correctly, but we don't want to desugar type --- operators in value declarations. --- -rebracketFiltered - :: forall m - . MonadError MultipleErrors m - => MonadSupply m - => RebracketCaller - -> (Declaration -> Bool) - -> [ExternsFile] - -> Module - -> m Module -rebracketFiltered !caller pred_ externs m = do - let (valueFixities, typeFixities) = - partitionEithers - $ concatMap externsFixities externs - ++ collectFixities m - - ensureNoDuplicates' MultipleValueOpFixities valueFixities - ensureNoDuplicates' MultipleTypeOpFixities typeFixities - - let valueOpTable = customOperatorTable' valueFixities - let valueAliased = M.fromList (map makeLookupEntry valueFixities) - let typeOpTable = customOperatorTable' typeFixities - let typeAliased = M.fromList (map makeLookupEntry typeFixities) - - rebracketModule caller pred_ valueOpTable typeOpTable m >>= - renameAliasedOperators valueAliased typeAliased - - where - - ensureNoDuplicates' - :: Ord op - => (op -> SimpleErrorMessage) - -> [FixityRecord op alias] - -> m () - ensureNoDuplicates' toError = - ensureNoDuplicates toError . map (\(i, pos, _, _) -> (i, pos)) - - customOperatorTable' - :: [FixityRecord op alias] - -> [[(Qualified op, Associativity)]] - customOperatorTable' = customOperatorTable . map (\(i, _, f, _) -> (i, f)) - - makeLookupEntry :: FixityRecord op alias -> (Qualified op, Qualified alias) - makeLookupEntry (qname, _, _, alias) = (qname, alias) - - renameAliasedOperators - :: M.Map (Qualified (OpName 'ValueOpName)) (Qualified (Either Ident (ProperName 'ConstructorName))) - -> M.Map (Qualified (OpName 'TypeOpName)) (Qualified (ProperName 'TypeName)) - -> Module - -> m Module - renameAliasedOperators valueAliased typeAliased (Module ss coms mn ds exts) = - Module ss coms mn <$> mapM (usingPredicate pred_ f') ds <*> pure exts - where - (goDecl', goExpr', goBinder') = updateTypes goType - (f', _, _, _, _, _) = - everywhereWithContextOnValuesM - ss - (\_ d -> (declSourceSpan d,) <$> goDecl' d) - (\pos -> uncurry goExpr <=< goExpr' pos) - (\pos -> uncurry goBinder <=< goBinder' pos) - defS - defS - defS - - goExpr :: SourceSpan -> Expr -> m (SourceSpan, Expr) - goExpr _ e@(PositionedValue pos _ _) = return (pos, e) - goExpr _ (Op pos op) = - (pos,) <$> case op `M.lookup` valueAliased of - Just (Qualified mn' (Left alias)) -> - return $ Var pos (Qualified mn' alias) - Just (Qualified mn' (Right alias)) -> - return $ Constructor pos (Qualified mn' alias) - Nothing -> - throwError . errorMessage' pos . UnknownName $ fmap ValOpName op - goExpr pos other = return (pos, other) - - goBinder :: SourceSpan -> Binder -> m (SourceSpan, Binder) - goBinder _ b@(PositionedBinder pos _ _) = return (pos, b) - goBinder _ (BinaryNoParensBinder (OpBinder pos op) lhs rhs) = - case op `M.lookup` valueAliased of - Just (Qualified mn' (Left alias)) -> - throwError . errorMessage' pos $ InvalidOperatorInBinder op (Qualified mn' alias) - Just (Qualified mn' (Right alias)) -> - return (pos, ConstructorBinder pos (Qualified mn' alias) [lhs, rhs]) - Nothing -> - throwError . errorMessage' pos . UnknownName $ fmap ValOpName op - goBinder _ BinaryNoParensBinder{} = - internalError "BinaryNoParensBinder has no OpBinder" - goBinder pos other = return (pos, other) - - goType :: SourceSpan -> SourceType -> m SourceType - goType pos (TypeOp ann2 op) = - case op `M.lookup` typeAliased of - Just alias -> - return $ TypeConstructor ann2 alias - Nothing -> - throwError . errorMessage' pos $ UnknownName $ fmap TyOpName op - goType _ other = return other - --- | Indicates whether the `rebracketModule` --- is being called with the full desugar pass --- run via `purs compile` or whether --- only the partial desugar pass is run --- via `purs docs`. --- This indication is needed to prevent --- a `purs docs` error when using --- `case _ of` syntax in a type class instance. -data RebracketCaller - = CalledByCompile - | CalledByDocs - deriving (Eq, Show) - -rebracketModule - :: forall m - . (MonadError MultipleErrors m) - => MonadSupply m - => RebracketCaller - -> (Declaration -> Bool) - -> [[(Qualified (OpName 'ValueOpName), Associativity)]] - -> [[(Qualified (OpName 'TypeOpName), Associativity)]] - -> Module - -> m Module -rebracketModule !caller pred_ valueOpTable typeOpTable (Module ss coms mn ds exts) = - Module ss coms mn <$> f' ds <*> pure exts - where - f' :: [Declaration] -> m [Declaration] - f' = - fmap (map (\d -> if pred_ d then removeParens d else d)) . - flip parU (usingPredicate pred_ h) - - -- The AST will run through all the desugar passes when compiling - -- and only some of the desugar passes when generating docs. - -- When generating docs, `case _ of` syntax used in an instance declaration - -- can trigger the `IncorrectAnonymousArgument` error because it does not - -- run the same passes that the compile desugaring does. Since `purs docs` - -- will only succeed once `purs compile` succeeds, we can ignore this check - -- when running `purs docs`. - -- See https://github.com/purescript/purescript/issues/4274#issuecomment-1087730651= - -- for more info. - h :: Declaration -> m Declaration - h = case caller of - CalledByDocs -> f - CalledByCompile -> g <=< f - - (f, _, _, _, _, _) = - everywhereWithContextOnValuesM - ss - (\_ d -> (declSourceSpan d,) <$> goDecl d) - (\pos -> wrap (matchExprOperators valueOpTable) <=< goExpr' pos) - (\pos -> wrap (matchBinderOperators valueOpTable) <=< goBinder' pos) - defS - defS - defS - - (g, _, _) = everywhereOnValuesTopDownM pure removeBinaryNoParens pure - - (goDecl, goExpr', goBinder') = updateTypes goType - - goType :: SourceSpan -> SourceType -> m SourceType - goType = flip matchTypeOperators typeOpTable - - wrap :: (a -> m a) -> (SourceSpan, a) -> m (SourceSpan, a) - wrap go (ss', a) = (ss',) <$> go a - -removeBinaryNoParens :: (MonadError MultipleErrors m, MonadSupply m) => Expr -> m Expr -removeBinaryNoParens u - | isAnonymousArgument u = case u of - PositionedValue p _ _ -> rethrowWithPosition p err - _ -> err - where err = throwError . errorMessage $ IncorrectAnonymousArgument -removeBinaryNoParens (Parens (stripPositionInfo -> BinaryNoParens op l r)) - | isAnonymousArgument r = do arg <- freshIdent' - return $ Abs (VarBinder nullSourceSpan arg) $ App (App op l) (Var nullSourceSpan (Qualified ByNullSourcePos arg)) - | isAnonymousArgument l = do arg <- freshIdent' - return $ Abs (VarBinder nullSourceSpan arg) $ App (App op (Var nullSourceSpan (Qualified ByNullSourcePos arg))) r -removeBinaryNoParens (BinaryNoParens op l r) = return $ App (App op l) r -removeBinaryNoParens e = return e - -stripPositionInfo :: Expr -> Expr -stripPositionInfo (PositionedValue _ _ e) = stripPositionInfo e -stripPositionInfo e = e - -removeParens :: Declaration -> Declaration -removeParens = f - where - (f, _, _) = - everywhereOnValues - (runIdentity . goDecl) - (goExpr . decontextify goExpr') - (goBinder . decontextify goBinder') - - (goDecl, goExpr', goBinder') = updateTypes (\_ -> return . goType) - - goExpr :: Expr -> Expr - goExpr (Parens val) = goExpr val - goExpr val = val - - goBinder :: Binder -> Binder - goBinder (ParensInBinder b) = goBinder b - goBinder b = b - - goType :: Type a -> Type a - goType (ParensInType _ t) = goType t - goType t = t - - decontextify - :: (SourceSpan -> a -> Identity (SourceSpan, a)) - -> a - -> a - decontextify ctxf = snd . runIdentity . ctxf (internalError "attempted to use SourceSpan in removeParens") - -externsFixities :: ExternsFile -> [Either ValueFixityRecord TypeFixityRecord] -externsFixities ExternsFile{..} = - map fromFixity efFixities ++ map fromTypeFixity efTypeFixities - where - - fromFixity - :: ExternsFixity - -> Either ValueFixityRecord TypeFixityRecord - fromFixity (ExternsFixity assoc prec op name) = - Left - ( Qualified (ByModuleName efModuleName) op - , internalModuleSourceSpan "" - , Fixity assoc prec - , name - ) - - fromTypeFixity - :: ExternsTypeFixity - -> Either ValueFixityRecord TypeFixityRecord - fromTypeFixity (ExternsTypeFixity assoc prec op name) = - Right - ( Qualified (ByModuleName efModuleName) op - , internalModuleSourceSpan "" - , Fixity assoc prec - , name - ) - -collectFixities :: Module -> [Either ValueFixityRecord TypeFixityRecord] -collectFixities (Module _ _ moduleName ds _) = concatMap collect ds - where - collect :: Declaration -> [Either ValueFixityRecord TypeFixityRecord] - collect (ValueFixityDeclaration (ss, _) fixity name op) = - [Left (Qualified (ByModuleName moduleName) op, ss, fixity, name)] - collect (TypeFixityDeclaration (ss, _) fixity name op) = - [Right (Qualified (ByModuleName moduleName) op, ss, fixity, name)] - collect _ = [] - -ensureNoDuplicates - :: (Ord a, MonadError MultipleErrors m) - => (a -> SimpleErrorMessage) - -> [(Qualified a, SourceSpan)] - -> m () -ensureNoDuplicates toError m = go $ sortOn fst m - where - go [] = return () - go [_] = return () - go ((x@(Qualified (ByModuleName mn) op), _) : (y, pos) : _) | x == y = - rethrow (addHint (ErrorInModule mn)) $ - rethrowWithPosition pos $ throwError . errorMessage $ toError op - go (_ : rest) = go rest - -customOperatorTable - :: [(Qualified op, Fixity)] - -> [[(Qualified op, Associativity)]] -customOperatorTable fixities = - let - userOps = map (\(name, Fixity a p) -> (name, p, a)) fixities - sorted = sortOn (Down . (\(_, p, _) -> p)) userOps - groups = groupBy ((==) `on` (\(_, p, _) -> p)) sorted - in - map (map (\(name, _, a) -> (name, a))) groups - -updateTypes - :: forall m - . Monad m - => (SourceSpan -> SourceType -> m SourceType) - -> ( Declaration -> m Declaration - , SourceSpan -> Expr -> m (SourceSpan, Expr) - , SourceSpan -> Binder -> m (SourceSpan, Binder) - ) -updateTypes goType = (goDecl, goExpr, goBinder) - where - - goType' :: SourceSpan -> SourceType -> m SourceType - goType' = everywhereOnTypesTopDownM . goType - - goDecl :: Declaration -> m Declaration - goDecl (DataDeclaration sa@(ss, _) ddt name args dctors) = - DataDeclaration sa ddt name - <$> traverse (traverse (traverse (goType' ss))) args - <*> traverse (traverseDataCtorFields (traverse (sndM (goType' ss)))) dctors - goDecl (ExternDeclaration sa@(ss, _) name ty) = - ExternDeclaration sa name <$> goType' ss ty - goDecl (TypeClassDeclaration sa@(ss, _) name args implies deps decls) = do - implies' <- traverse (overConstraintArgs (traverse (goType' ss))) implies - args' <- traverse (traverse (traverse (goType' ss))) args - return $ TypeClassDeclaration sa name args' implies' deps decls - goDecl (TypeInstanceDeclaration sa@(ss, _) na ch idx name cs className tys impls) = do - cs' <- traverse (overConstraintArgs (traverse (goType' ss))) cs - tys' <- traverse (goType' ss) tys - return $ TypeInstanceDeclaration sa na ch idx name cs' className tys' impls - goDecl (TypeSynonymDeclaration sa@(ss, _) name args ty) = - TypeSynonymDeclaration sa name - <$> traverse (traverse (traverse (goType' ss))) args - <*> goType' ss ty - goDecl (TypeDeclaration (TypeDeclarationData sa@(ss, _) expr ty)) = - TypeDeclaration . TypeDeclarationData sa expr <$> goType' ss ty - goDecl (KindDeclaration sa@(ss, _) sigFor name ty) = - KindDeclaration sa sigFor name <$> goType' ss ty - goDecl (ExternDataDeclaration sa@(ss, _) name ty) = - ExternDataDeclaration sa name <$> goType' ss ty - goDecl other = - return other - - goExpr :: SourceSpan -> Expr -> m (SourceSpan, Expr) - goExpr _ e@(PositionedValue pos _ _) = return (pos, e) - goExpr pos (TypeClassDictionary (Constraint ann name kinds tys info) dicts hints) = do - kinds' <- traverse (goType' pos) kinds - tys' <- traverse (goType' pos) tys - return (pos, TypeClassDictionary (Constraint ann name kinds' tys' info) dicts hints) - goExpr pos (DeferredDictionary cls tys) = do - tys' <- traverse (goType' pos) tys - return (pos, DeferredDictionary cls tys') - goExpr pos (TypedValue check v ty) = do - ty' <- goType' pos ty - return (pos, TypedValue check v ty') - goExpr pos (VisibleTypeApp v ty) = do - ty' <- goType' pos ty - return (pos, VisibleTypeApp v ty') - goExpr pos other = return (pos, other) - - goBinder :: SourceSpan -> Binder -> m (SourceSpan, Binder) - goBinder _ e@(PositionedBinder pos _ _) = return (pos, e) - goBinder pos (TypedBinder ty b) = do - ty' <- goType' pos ty - return (pos, TypedBinder ty' b) - goBinder pos other = return (pos, other) - --- | --- Checks all the fixity exports within a module to ensure that members aliased --- by the operators are also exported from the module. --- --- This pass requires name desugaring and export elaboration to have run first. --- -checkFixityExports - :: forall m - . MonadError MultipleErrors m - => Module - -> m Module -checkFixityExports (Module _ _ _ _ Nothing) = - internalError "exports should have been elaborated before checkFixityExports" -checkFixityExports m@(Module ss _ mn ds (Just exps)) = - rethrow (addHint (ErrorInModule mn)) - $ rethrowWithPosition ss (traverse_ checkRef exps) - $> m - where - - checkRef :: DeclarationRef -> m () - checkRef dr@(ValueOpRef ss' op) = - for_ (getValueOpAlias op) $ \case - Left ident -> - unless (ValueRef ss' ident `elem` exps) - . throwError . errorMessage' ss' - $ TransitiveExportError dr [ValueRef ss' ident] - Right ctor -> - unless (anyTypeRef (maybe False (elem ctor) . snd)) - . throwError . errorMessage' ss - $ TransitiveDctorExportError dr [ctor] - checkRef dr@(TypeOpRef ss' op) = - for_ (getTypeOpAlias op) $ \ty -> - unless (anyTypeRef ((== ty) . fst)) - . throwError . errorMessage' ss' - $ TransitiveExportError dr [TypeRef ss' ty Nothing] - checkRef _ = return () - - -- Finds the name associated with a type operator when that type is also - -- defined in the current module. - getTypeOpAlias :: OpName 'TypeOpName -> Maybe (ProperName 'TypeName) - getTypeOpAlias op = - listToMaybe (mapMaybe (either (const Nothing) go <=< getFixityDecl) ds) - where - go (TypeFixity _ (Qualified (ByModuleName mn') ident) op') - | mn == mn' && op == op' = Just ident - go _ = Nothing - - -- Finds the value or data constructor associated with an operator when that - -- declaration is also in the current module. - getValueOpAlias - :: OpName 'ValueOpName - -> Maybe (Either Ident (ProperName 'ConstructorName)) - getValueOpAlias op = - listToMaybe (mapMaybe (either go (const Nothing) <=< getFixityDecl) ds) - where - go (ValueFixity _ (Qualified (ByModuleName mn') ident) op') - | mn == mn' && op == op' = Just ident - go _ = Nothing - - -- Tests the exported `TypeRef` entries with a predicate. - anyTypeRef - :: ((ProperName 'TypeName, Maybe [ProperName 'ConstructorName]) -> Bool) - -> Bool - anyTypeRef f = any (maybe False f . getTypeRef) exps - -usingPredicate - :: forall f a - . Applicative f - => (a -> Bool) - -> (a -> f a) - -> (a -> f a) -usingPredicate p f x = - if p x then f x else pure x diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Binders.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Binders.hs deleted file mode 100644 index 29725c71..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Binders.hs +++ /dev/null @@ -1,33 +0,0 @@ -module Language.PureScript.Sugar.Operators.Binders where - -import Prelude - -import Control.Monad.Except (MonadError) - -import Language.PureScript.AST (Associativity, Binder(..), SourceSpan) -import Language.PureScript.Errors (MultipleErrors) -import Language.PureScript.Names (OpName(..), OpNameType(..), Qualified(..)) -import Language.PureScript.Sugar.Operators.Common (matchOperators) - -matchBinderOperators - :: MonadError MultipleErrors m - => [[(Qualified (OpName 'ValueOpName), Associativity)]] - -> Binder - -> m Binder -matchBinderOperators = matchOperators isBinOp extractOp fromOp reapply id - where - - isBinOp :: Binder -> Bool - isBinOp BinaryNoParensBinder{} = True - isBinOp _ = False - - extractOp :: Binder -> Maybe (Binder, Binder, Binder) - extractOp (BinaryNoParensBinder op l r) = Just (op, l, r) - extractOp _ = Nothing - - fromOp :: Binder -> Maybe (SourceSpan, Qualified (OpName 'ValueOpName)) - fromOp (OpBinder ss q@(Qualified _ (OpName _))) = Just (ss, q) - fromOp _ = Nothing - - reapply :: SourceSpan -> Qualified (OpName 'ValueOpName) -> Binder -> Binder -> Binder - reapply ss = BinaryNoParensBinder . OpBinder ss diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Common.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Common.hs deleted file mode 100644 index 7fd6df96..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Common.hs +++ /dev/null @@ -1,144 +0,0 @@ -module Language.PureScript.Sugar.Operators.Common where - -import Prelude - -import Control.Monad (guard, join) -import Control.Monad.Except (MonadError(..)) - -import Data.Either (rights) -import Data.Functor.Identity (Identity) -import Data.List (sortOn) -import Data.Maybe (mapMaybe, fromJust) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M - -import Text.Parsec qualified as P -import Text.Parsec.Pos qualified as P -import Text.Parsec.Expr qualified as P - -import Language.PureScript.AST (Associativity(..), ErrorMessageHint(..), SourceSpan) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (ErrorMessage(..), MultipleErrors(..), SimpleErrorMessage(..)) -import Language.PureScript.Names (OpName, Qualified, eraseOpName) - -type Chain a = [Either a a] - -type FromOp nameType a = a -> Maybe (SourceSpan, Qualified (OpName nameType)) -type Reapply nameType a = SourceSpan -> Qualified (OpName nameType) -> a -> a -> a - -toAssoc :: Associativity -> P.Assoc -toAssoc Infixl = P.AssocLeft -toAssoc Infixr = P.AssocRight -toAssoc Infix = P.AssocNone - -token :: (P.Stream s Identity t) => (t -> Maybe a) -> P.Parsec s u a -token = P.token (const "") (const (P.initialPos "")) - -parseValue :: P.Parsec (Chain a) () a -parseValue = token (either Just (const Nothing)) P. "expression" - -parseOp - :: FromOp nameType a - -> P.Parsec (Chain a) () (SourceSpan, Qualified (OpName nameType)) -parseOp fromOp = token (either (const Nothing) fromOp) P. "operator" - -matchOp - :: FromOp nameType a - -> Qualified (OpName nameType) - -> P.Parsec (Chain a) () SourceSpan -matchOp fromOp op = do - (ss, ident) <- parseOp fromOp - guard $ ident == op - pure ss - -opTable - :: [[(Qualified (OpName nameType), Associativity)]] - -> FromOp nameType a - -> Reapply nameType a - -> [[P.Operator (Chain a) () Identity a]] -opTable ops fromOp reapply = - map (map (\(name, a) -> P.Infix (P.try (matchOp fromOp name) >>= \ss -> return (reapply ss name)) (toAssoc a))) ops - -matchOperators - :: forall m a nameType - . Show a - => MonadError MultipleErrors m - => (a -> Bool) - -> (a -> Maybe (a, a, a)) - -> FromOp nameType a - -> Reapply nameType a - -> ([[P.Operator (Chain a) () Identity a]] -> P.OperatorTable (Chain a) () Identity a) - -> [[(Qualified (OpName nameType), Associativity)]] - -> a - -> m a -matchOperators isBinOp extractOp fromOp reapply modOpTable ops = parseChains - where - parseChains :: a -> m a - parseChains ty - | True <- isBinOp ty = bracketChain (extendChain ty) - | otherwise = pure ty - extendChain :: a -> Chain a - extendChain ty - | Just (op, l, r) <- extractOp ty = Left l : Right op : extendChain r - | otherwise = [Left ty] - bracketChain :: Chain a -> m a - bracketChain chain = - case P.parse opParser "operator expression" chain of - Right a -> pure a - Left _ -> throwError . MultipleErrors $ mkErrors chain - opParser :: P.Parsec (Chain a) () a - opParser = P.buildExpressionParser (modOpTable (opTable ops fromOp reapply)) parseValue <* P.eof - - -- Generating a good error message involves a bit of work here, as the parser - -- can't provide one for us. - -- - -- We examine the expression chain, plucking out the operators and then - -- grouping them by shared precedence, then if any of the following conditions - -- are met, we have something to report: - -- 1. any of the groups have mixed associativity - -- 2. there is more than one occurrence of a non-associative operator in a - -- precedence group - mkErrors :: Chain a -> [ErrorMessage] - mkErrors chain = - let - opInfo :: M.Map (Qualified (OpName nameType)) (Integer, Associativity) - opInfo = M.fromList $ concatMap (\(n, o) -> map (\(name, assoc) -> (name, (n, assoc))) o) (zip [0..] ops) - opPrec :: Qualified (OpName nameType) -> Integer - opPrec = fst . fromJust . flip M.lookup opInfo - opAssoc :: Qualified (OpName nameType) -> Associativity - opAssoc = snd . fromJust . flip M.lookup opInfo - chainOpSpans :: M.Map (Qualified (OpName nameType)) (NEL.NonEmpty SourceSpan) - chainOpSpans = foldr (\(ss, name) -> M.alter (Just . maybe (pure ss) (NEL.cons ss)) name) M.empty . mapMaybe fromOp $ rights chain - opUsages :: Qualified (OpName nameType) -> Int - opUsages = maybe 0 NEL.length . flip M.lookup chainOpSpans - precGrouped :: [NEL.NonEmpty (Qualified (OpName nameType))] - precGrouped = NEL.groupWith opPrec . sortOn opPrec $ M.keys chainOpSpans - assocGrouped :: [NEL.NonEmpty (NEL.NonEmpty (Qualified (OpName nameType)))] - assocGrouped = fmap (NEL.groupWith1 opAssoc . NEL.sortWith opAssoc) precGrouped - mixedAssoc :: [NEL.NonEmpty (Qualified (OpName nameType))] - mixedAssoc = fmap join . filter (\precGroup -> NEL.length precGroup > 1) $ assocGrouped - nonAssoc :: [NEL.NonEmpty (Qualified (OpName nameType))] - nonAssoc = NEL.filter (\assocGroup -> opAssoc (NEL.head assocGroup) == Infix && sum (fmap opUsages assocGroup) > 1) =<< assocGrouped - in - if null (nonAssoc ++ mixedAssoc) - then internalError "matchOperators: cannot reorder operators" - else - map - (\grp -> - mkPositionedError chainOpSpans grp - (MixedAssociativityError (fmap (\name -> (eraseOpName <$> name, opAssoc name)) grp))) - mixedAssoc - ++ map - (\grp -> - mkPositionedError chainOpSpans grp - (NonAssociativeError (fmap (fmap eraseOpName) grp))) - nonAssoc - - mkPositionedError - :: M.Map (Qualified (OpName nameType)) (NEL.NonEmpty SourceSpan) - -> NEL.NonEmpty (Qualified (OpName nameType)) - -> SimpleErrorMessage - -> ErrorMessage - mkPositionedError chainOpSpans grp = - ErrorMessage - [PositionedError (fromJust . flip M.lookup chainOpSpans =<< grp)] diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Expr.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Expr.hs deleted file mode 100644 index 0815eb16..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Expr.hs +++ /dev/null @@ -1,52 +0,0 @@ -module Language.PureScript.Sugar.Operators.Expr where - -import Prelude - -import Control.Monad.Except (MonadError) -import Data.Functor.Identity (Identity) - -import Text.Parsec qualified as P -import Text.Parsec.Expr qualified as P - -import Language.PureScript.AST (Associativity, Expr(..), SourceSpan) -import Language.PureScript.Names (OpName(..), OpNameType(..), Qualified(..)) -import Language.PureScript.Sugar.Operators.Common (Chain, matchOperators, token) -import Language.PureScript.Errors (MultipleErrors) - -matchExprOperators - :: MonadError MultipleErrors m - => [[(Qualified (OpName 'ValueOpName), Associativity)]] - -> Expr - -> m Expr -matchExprOperators = matchOperators isBinOp extractOp fromOp reapply modOpTable - where - - isBinOp :: Expr -> Bool - isBinOp BinaryNoParens{} = True - isBinOp _ = False - - extractOp :: Expr -> Maybe (Expr, Expr, Expr) - extractOp (BinaryNoParens op l r) - | PositionedValue _ _ op' <- op = Just (op', l, r) - | otherwise = Just (op, l, r) - extractOp _ = Nothing - - fromOp :: Expr -> Maybe (SourceSpan, Qualified (OpName 'ValueOpName)) - fromOp (Op ss q@(Qualified _ (OpName _))) = Just (ss, q) - fromOp _ = Nothing - - reapply :: SourceSpan -> Qualified (OpName 'ValueOpName) -> Expr -> Expr -> Expr - reapply ss = BinaryNoParens . Op ss - - modOpTable - :: [[P.Operator (Chain Expr) () Identity Expr]] - -> [[P.Operator (Chain Expr) () Identity Expr]] - modOpTable table = - [ P.Infix (P.try (BinaryNoParens <$> parseTicks)) P.AssocLeft ] - : table - - parseTicks :: P.Parsec (Chain Expr) () Expr - parseTicks = token (either (const Nothing) fromOther) P. "infix function" - where - fromOther (Op _ _) = Nothing - fromOther v = Just v diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Types.hs deleted file mode 100644 index 81001511..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/Operators/Types.hs +++ /dev/null @@ -1,34 +0,0 @@ -module Language.PureScript.Sugar.Operators.Types where - -import Prelude - -import Control.Monad.Except (MonadError) -import Language.PureScript.AST (Associativity, SourceSpan) -import Language.PureScript.Errors (MultipleErrors) -import Language.PureScript.Names (OpName(..), OpNameType(..), Qualified(..)) -import Language.PureScript.Sugar.Operators.Common (matchOperators) -import Language.PureScript.Types (SourceType, Type(..), srcTypeApp) - -matchTypeOperators - :: MonadError MultipleErrors m - => SourceSpan - -> [[(Qualified (OpName 'TypeOpName), Associativity)]] - -> SourceType - -> m SourceType -matchTypeOperators ss = matchOperators isBinOp extractOp fromOp reapply id - where - - isBinOp :: SourceType -> Bool - isBinOp BinaryNoParensType{} = True - isBinOp _ = False - - extractOp :: SourceType -> Maybe (SourceType, SourceType, SourceType) - extractOp (BinaryNoParensType _ op l r) = Just (op, l, r) - extractOp _ = Nothing - - fromOp :: SourceType -> Maybe (SourceSpan, Qualified (OpName 'TypeOpName)) - fromOp (TypeOp _ q@(Qualified _ (OpName _))) = Just (ss, q) - fromOp _ = Nothing - - reapply :: a -> Qualified (OpName 'TypeOpName) -> SourceType -> SourceType -> SourceType - reapply _ op = srcTypeApp . srcTypeApp (TypeOp (ss, []) op) diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeClasses.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeClasses.hs deleted file mode 100644 index d24485e0..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeClasses.hs +++ /dev/null @@ -1,392 +0,0 @@ --- | --- This module implements the desugaring pass which creates newtypes for type class dictionaries --- and value declarations for type class instances. --- -module Language.PureScript.Sugar.TypeClasses - ( desugarTypeClasses - , typeClassMemberName - , superClassDictionaryNames - ) where - -import Prelude - -import Control.Arrow (first, second) -import Control.Monad (unless) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State (MonadState(..), StateT, evalStateT, modify) -import Control.Monad.Supply.Class (MonadSupply) -import Data.Graph (SCC(..), stronglyConnComp) -import Data.List (find, partition) -import Data.List.NonEmpty (nonEmpty) -import Data.Map qualified as M -import Data.Maybe (catMaybes, mapMaybe, isJust) -import Data.List.NonEmpty qualified as NEL -import Data.Set qualified as S -import Data.Text (Text) -import Data.Traversable (for) -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType(..), NameKind(..), TypeClassData(..), dictTypeName, function, makeTypeClassData, primClasses, primCoerceClasses, primIntClasses, primRowClasses, primRowListClasses, primSymbolClasses, primTypeErrorClasses, tyRecord) -import Language.PureScript.Errors hiding (isExported, nonEmpty) -import Language.PureScript.Externs (ExternsDeclaration(..), ExternsFile(..)) -import Language.PureScript.Label (Label(..)) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Name(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, freshIdent, qualify, runIdent) -import Language.PureScript.PSString (mkString) -import Language.PureScript.Sugar.CaseDeclarations (desugarCases) -import Language.PureScript.TypeClassDictionaries (superclassName) -import Language.PureScript.Types - -type MemberMap = M.Map (ModuleName, ProperName 'ClassName) TypeClassData - -type Desugar = StateT MemberMap - --- | --- Add type synonym declarations for type class dictionary types, and value declarations for type class --- instance dictionary expressions. --- -desugarTypeClasses - :: (MonadSupply m, MonadError MultipleErrors m) - => [ExternsFile] - -> Module - -> m Module -desugarTypeClasses externs = flip evalStateT initialState . desugarModule - where - initialState :: MemberMap - initialState = - mconcat - [ M.mapKeys (qualify C.M_Prim) primClasses - , M.mapKeys (qualify C.M_Prim_Coerce) primCoerceClasses - , M.mapKeys (qualify C.M_Prim_Row) primRowClasses - , M.mapKeys (qualify C.M_Prim_RowList) primRowListClasses - , M.mapKeys (qualify C.M_Prim_Symbol) primSymbolClasses - , M.mapKeys (qualify C.M_Prim_Int) primIntClasses - , M.mapKeys (qualify C.M_Prim_TypeError) primTypeErrorClasses - , M.fromList (externs >>= \ExternsFile{..} -> mapMaybe (fromExternsDecl efModuleName) efDeclarations) - ] - - fromExternsDecl - :: ModuleName - -> ExternsDeclaration - -> Maybe ((ModuleName, ProperName 'ClassName), TypeClassData) - fromExternsDecl mn (EDClass name args members implies deps tcIsEmpty) = Just ((mn, name), typeClass) where - typeClass = makeTypeClassData args members implies deps tcIsEmpty - fromExternsDecl _ _ = Nothing - -desugarModule - :: (MonadSupply m, MonadError MultipleErrors m) - => Module - -> Desugar m Module -desugarModule (Module ss coms name decls (Just exps)) = do - let (classDecls, restDecls) = partition isTypeClassDecl decls - classVerts = fmap (\d -> (d, classDeclName d, superClassesNames d)) classDecls - (classNewExpss, classDeclss) <- unzip <$> parU (stronglyConnComp classVerts) (desugarClassDecl name exps) - (restNewExpss, restDeclss) <- unzip <$> parU restDecls (desugarDecl name exps) - return $ Module ss coms name (concat restDeclss ++ concat classDeclss) $ Just (exps ++ catMaybes restNewExpss ++ catMaybes classNewExpss) - where - desugarClassDecl :: (MonadSupply m, MonadError MultipleErrors m) - => ModuleName - -> [DeclarationRef] - -> SCC Declaration - -> Desugar m (Maybe DeclarationRef, [Declaration]) - desugarClassDecl name' exps' (AcyclicSCC d) = desugarDecl name' exps' d - desugarClassDecl _ _ (CyclicSCC ds') - | Just ds'' <- nonEmpty ds' = throwError . errorMessage' (declSourceSpan (NEL.head ds'')) $ CycleInTypeClassDeclaration (NEL.map classDeclName ds'') - | otherwise = internalError "desugarClassDecl: empty CyclicSCC" - - superClassesNames :: Declaration -> [Qualified (ProperName 'ClassName)] - superClassesNames (TypeClassDeclaration _ _ _ implies _ _) = fmap constraintName implies - superClassesNames _ = [] - - constraintName :: SourceConstraint -> Qualified (ProperName 'ClassName) - constraintName (Constraint _ cName _ _ _) = cName - - classDeclName :: Declaration -> Qualified (ProperName 'ClassName) - classDeclName (TypeClassDeclaration _ pn _ _ _ _) = Qualified (ByModuleName name) pn - classDeclName _ = internalError "Expected TypeClassDeclaration" - -desugarModule _ = internalError "Exports should have been elaborated in name desugaring" - -{- Desugar type class and type class instance declarations --- --- Type classes become newtypes for their dictionaries, and type instances become dictionary declarations. --- Additional values are generated to access individual members of a dictionary, with the appropriate type. --- --- E.g. the following --- --- module Test where --- --- class Foo a where --- foo :: a -> a --- --- instance fooString :: Foo String where --- foo s = s ++ s --- --- instance fooArray :: (Foo a) => Foo [a] where --- foo = map foo --- --- {- Superclasses -} --- --- class (Foo a) <= Sub a where --- sub :: a --- --- instance subString :: Sub String where --- sub = "" --- --- becomes: --- --- --- --- newtype Foo$Dict a = Foo$Dict { foo :: a -> a } --- --- -- this following type is marked as not needing to be checked so a new Abs --- -- is not introduced around the definition in type checking, but when --- -- called the dictionary value is still passed in for the `dict` argument --- foo :: forall a. (Foo$Dict a) => a -> a --- foo (Foo$Dict dict) = dict.foo --- --- fooString :: Foo$Dict String --- fooString = Foo$Dict { foo: \s -> s ++ s } --- --- fooArray :: forall a. (Foo$Dict a) => Foo$Dict [a] --- fooArray = Foo$Dict { foo: map foo } --- --- {- Superclasses -} --- --- --- --- newtype Sub$Dict a = Sub$Dict { sub :: a --- , "Foo0" :: {} -> Foo$Dict a --- } --- --- -- As with `foo` above, this type is unchecked at the declaration --- sub :: forall a. (Sub$Dict a) => a --- sub (Sub$Dict dict) = dict.sub --- --- subString :: Sub$Dict String --- subString = Sub$Dict { sub: "", --- , "Foo0": \_ -> --- } --- --- and finally as the generated javascript: --- --- var foo = function (dict) { --- return dict.foo; --- }; --- --- var fooString = { --- foo: function (s) { --- return s + s; --- } --- }; --- --- var fooArray = function (dictFoo) { --- return { --- foo: map(foo(dictFoo)) --- }; --- }; --- --- var sub = function (dict) { --- return dict.sub; --- }; --- --- var subString = { --- sub: "", --- Foo0: function () { --- return fooString; --- } --- }; --} -desugarDecl - :: (MonadSupply m, MonadError MultipleErrors m) - => ModuleName - -> [DeclarationRef] - -> Declaration - -> Desugar m (Maybe DeclarationRef, [Declaration]) -desugarDecl mn exps = go - where - go d@(TypeClassDeclaration sa name args implies deps members) = do - modify (M.insert (mn, name) (makeTypeClassData args (map memberToNameAndType members) implies deps False)) - return (Nothing, d : typeClassDictionaryDeclaration sa name args implies members : map (typeClassMemberToDictionaryAccessor mn name args) members) - go (TypeInstanceDeclaration sa na chainId idx name deps className tys body) = do - name' <- desugarInstName name - let d = TypeInstanceDeclaration sa na chainId idx (Right name') deps className tys body - let explicitOrNot = case body of - DerivedInstance -> Left $ DerivedInstancePlaceholder className KnownClassStrategy - NewtypeInstance -> Left $ DerivedInstancePlaceholder className NewtypeStrategy - ExplicitInstance members -> Right members - dictDecl <- case explicitOrNot of - Right members - | className == C.Coercible -> - throwError . errorMessage' (fst sa) $ InvalidCoercibleInstanceDeclaration tys - | otherwise -> do - desugared <- desugarCases members - typeInstanceDictionaryDeclaration sa name' mn deps className tys desugared - Left dict -> - let - dictTy = foldl srcTypeApp (srcTypeConstructor (fmap (coerceProperName . dictTypeName) className)) tys - constrainedTy = quantify (foldr srcConstrainedType dictTy deps) - in - return $ ValueDecl sa name' Private [] [MkUnguarded (TypedValue True dict constrainedTy)] - return (expRef name' className tys, [d, dictDecl]) - go other = return (Nothing, [other]) - - -- Completes the name generation for type class instances that do not have - -- a unique name defined in source code. - desugarInstName :: MonadSupply m => Either Text Ident -> Desugar m Ident - desugarInstName = either freshIdent pure - - expRef :: Ident -> Qualified (ProperName 'ClassName) -> [SourceType] -> Maybe DeclarationRef - expRef name className tys - | isExportedClass className && all (all isExportedType . getConstructors) tys = - Just $ TypeInstanceRef genSpan name UserNamed - | otherwise = Nothing - - isExportedClass :: Qualified (ProperName 'ClassName) -> Bool - isExportedClass = isExported (elem . TypeClassRef genSpan) - - isExportedType :: Qualified (ProperName 'TypeName) -> Bool - isExportedType = isExported $ \pn -> isJust . find (matchesTypeRef pn) - - isExported - :: (ProperName a -> [DeclarationRef] -> Bool) - -> Qualified (ProperName a) - -> Bool - isExported test (Qualified (ByModuleName mn') pn) = mn /= mn' || test pn exps - isExported _ _ = internalError "Names should have been qualified in name desugaring" - - matchesTypeRef :: ProperName 'TypeName -> DeclarationRef -> Bool - matchesTypeRef pn (TypeRef _ pn' _) = pn == pn' - matchesTypeRef _ _ = False - - getConstructors :: SourceType -> [Qualified (ProperName 'TypeName)] - getConstructors = everythingOnTypes (++) getConstructor - where - getConstructor (TypeConstructor _ tcname) = [tcname] - getConstructor _ = [] - - genSpan :: SourceSpan - genSpan = internalModuleSourceSpan "" - -memberToNameAndType :: Declaration -> (Ident, SourceType) -memberToNameAndType (TypeDeclaration td) = unwrapTypeDeclaration td -memberToNameAndType _ = internalError "Invalid declaration in type class definition" - -typeClassDictionaryDeclaration - :: SourceAnn - -> ProperName 'ClassName - -> [(Text, Maybe SourceType)] - -> [SourceConstraint] - -> [Declaration] - -> Declaration -typeClassDictionaryDeclaration sa name args implies members = - let superclassTypes = superClassDictionaryNames implies `zip` - [ function unit (foldl srcTypeApp (srcTypeConstructor (fmap (coerceProperName . dictTypeName) superclass)) tyArgs) - | (Constraint _ superclass _ tyArgs _) <- implies - ] - members' = map (first runIdent . memberToNameAndType) members - mtys = members' ++ superclassTypes - toRowListItem (l, t) = srcRowListItem (Label $ mkString l) t - ctor = DataConstructorDeclaration sa (coerceProperName $ dictTypeName name) - [(Ident "dict", srcTypeApp tyRecord $ rowFromList (map toRowListItem mtys, srcREmpty))] - in DataDeclaration sa Newtype (coerceProperName $ dictTypeName name) args [ctor] - -typeClassMemberToDictionaryAccessor - :: ModuleName - -> ProperName 'ClassName - -> [(Text, Maybe SourceType)] - -> Declaration - -> Declaration -typeClassMemberToDictionaryAccessor mn name args (TypeDeclaration (TypeDeclarationData sa@(ss, _) ident ty)) = - let className = Qualified (ByModuleName mn) name - dictIdent = Ident "dict" - dictObjIdent = Ident "v" - ctor = ConstructorBinder ss (coerceProperName . dictTypeName <$> className) [VarBinder ss dictObjIdent] - acsr = Accessor (mkString $ runIdent ident) (Var ss (Qualified ByNullSourcePos dictObjIdent)) - visibility = second (const TypeVarVisible) <$> args - in ValueDecl sa ident Private [] - [MkUnguarded ( - TypedValue False (Abs (VarBinder ss dictIdent) (Case [Var ss $ Qualified ByNullSourcePos dictIdent] [CaseAlternative [ctor] [MkUnguarded acsr]])) $ - addVisibility visibility (moveQuantifiersToFront NullSourceAnn (quantify (srcConstrainedType (srcConstraint className [] (map (srcTypeVar . fst) args) Nothing) ty))) - )] -typeClassMemberToDictionaryAccessor _ _ _ _ = internalError "Invalid declaration in type class definition" - -unit :: SourceType -unit = srcTypeApp tyRecord srcREmpty - -typeInstanceDictionaryDeclaration - :: forall m - . MonadError MultipleErrors m - => SourceAnn - -> Ident - -> ModuleName - -> [SourceConstraint] - -> Qualified (ProperName 'ClassName) - -> [SourceType] - -> [Declaration] - -> Desugar m Declaration -typeInstanceDictionaryDeclaration sa@(ss, _) name mn deps className tys decls = - rethrow (addHint (ErrorInInstance className tys)) $ do - m <- get - - -- Lookup the type arguments and member types for the type class - TypeClassData{..} <- - maybe (throwError . errorMessage' ss . UnknownName $ fmap TyClassName className) return $ - M.lookup (qualify mn className) m - - -- Replace the type arguments with the appropriate types in the member types - let memberTypes = map (second (replaceAllTypeVars (zip (map fst typeClassArguments) tys)) . tuple3To2) typeClassMembers - - let declaredMembers = S.fromList $ mapMaybe declIdent decls - - -- Instance declarations with a Fail constraint are unreachable code, so - -- we allow them to be empty. - let unreachable = any ((C.Fail ==) . constraintClass) deps && null decls - - unless unreachable $ - case filter (\(ident, _) -> not $ S.member ident declaredMembers) memberTypes of - hd : tl -> throwError . errorMessage' ss $ MissingClassMember (hd NEL.:| tl) - [] -> pure () - - -- Create values for the type instance members - members <- zip (map typeClassMemberName decls) <$> traverse (memberToValue memberTypes) decls - - -- Create the type of the dictionary - -- The type is a record type, but depending on type instance dependencies, may be constrained. - -- The dictionary itself is a record literal (unless unreachable, in which case it's undefined). - superclassesDicts <- for typeClassSuperclasses $ \(Constraint _ superclass _ suTyArgs _) -> do - let tyArgs = map (replaceAllTypeVars (zip (map fst typeClassArguments) tys)) suTyArgs - pure $ Abs (VarBinder ss UnusedIdent) (DeferredDictionary superclass tyArgs) - let superclasses = superClassDictionaryNames typeClassSuperclasses `zip` superclassesDicts - - let props = Literal ss $ ObjectLiteral $ map (first mkString) (members ++ superclasses) - dictTy = foldl srcTypeApp (srcTypeConstructor (fmap (coerceProperName . dictTypeName) className)) tys - constrainedTy = quantify (foldr srcConstrainedType dictTy deps) - dict = App (Constructor ss (fmap (coerceProperName . dictTypeName) className)) props - mkTV = if unreachable then TypedValue False (Var nullSourceSpan C.I_undefined) else TypedValue True dict - result = ValueDecl sa name Private [] [MkUnguarded (mkTV constrainedTy)] - return result - - where - - memberToValue :: [(Ident, SourceType)] -> Declaration -> Desugar m Expr - memberToValue tys' (ValueDecl (ss', _) ident _ [] [MkUnguarded val]) = do - _ <- maybe (throwError . errorMessage' ss' $ ExtraneousClassMember ident className) return $ lookup ident tys' - return val - memberToValue _ _ = internalError "Invalid declaration in type instance definition" - -declIdent :: Declaration -> Maybe Ident -declIdent (ValueDeclaration vd) = Just (valdeclIdent vd) -declIdent (TypeDeclaration td) = Just (tydeclIdent td) -declIdent _ = Nothing - -typeClassMemberName :: Declaration -> Text -typeClassMemberName = maybe (internalError "typeClassMemberName: Invalid declaration in type class definition") runIdent . declIdent - -superClassDictionaryNames :: [Constraint a] -> [Text] -superClassDictionaryNames supers = - [ superclassName pn index - | (index, Constraint _ pn _ _ _) <- zip [0..] supers - ] - -tuple3To2 :: (a, b, c) -> (a, b) -tuple3To2 (a, b, _) = (a, b) diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs deleted file mode 100755 index 3b4c0195..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs +++ /dev/null @@ -1,207 +0,0 @@ --- | This module implements the generic deriving elaboration that takes place during desugaring. -module Language.PureScript.Sugar.TypeClasses.Deriving (deriveInstances) where - -import Prelude -import Protolude (note) - -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.Supply.Class (MonadSupply) -import Data.List (foldl', find, unzip5) -import Language.PureScript.AST (Binder(..), CaseAlternative(..), DataConstructorDeclaration(..), Declaration(..), Expr(..), pattern MkUnguarded, Module(..), SourceSpan(..), TypeInstanceBody(..), pattern ValueDecl) -import Language.PureScript.AST.Utils (UnwrappedTypeConstructor(..), lamCase, unguarded, unwrapTypeConstructor) -import Language.PureScript.Constants.Libs qualified as Libs -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType(..), NameKind(..)) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage') -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), freshIdent) -import Language.PureScript.PSString (mkString) -import Language.PureScript.Types (SourceType, Type(..), WildcardData(..), replaceAllTypeVars, srcTypeApp, srcTypeConstructor, srcTypeLevelString) -import Language.PureScript.TypeChecker (checkNewtype) - --- | Elaborates deriving instance declarations by code generation. -deriveInstances - :: forall m - . (MonadError MultipleErrors m, MonadSupply m) - => Module - -> m Module -deriveInstances (Module ss coms mn ds exts) = - Module ss coms mn <$> mapM (deriveInstance mn ds) ds <*> pure exts - --- | Takes a declaration, and if the declaration is a deriving TypeInstanceDeclaration, --- elaborates that into an instance declaration via code generation. --- --- More instance deriving happens during type checking. The instances --- derived here are special for two reasons: --- * they depend only on the structure of the data, not types; and --- * they expect wildcard types from the user and generate type expressions --- to replace them. --- -deriveInstance - :: forall m - . (MonadError MultipleErrors m, MonadSupply m) - => ModuleName - -> [Declaration] - -> Declaration - -> m Declaration -deriveInstance mn ds decl = - case decl of - TypeInstanceDeclaration sa@(ss, _) na ch idx nm deps className tys DerivedInstance -> let - binaryWildcardClass :: (Declaration -> [SourceType] -> m ([Declaration], SourceType)) -> m Declaration - binaryWildcardClass f = case tys of - [ty1, ty2] -> case unwrapTypeConstructor ty1 of - Just UnwrappedTypeConstructor{..} | mn == utcModuleName -> do - checkIsWildcard ss utcTyCon ty2 - tyConDecl <- findTypeDecl ss utcTyCon ds - (members, ty2') <- f tyConDecl utcArgs - pure $ TypeInstanceDeclaration sa na ch idx nm deps className [ty1, ty2'] (ExplicitInstance members) - _ -> throwError . errorMessage' ss $ ExpectedTypeConstructor className tys ty1 - _ -> throwError . errorMessage' ss $ InvalidDerivedInstance className tys 2 - - in case className of - Libs.Generic -> binaryWildcardClass (deriveGenericRep ss mn) - Libs.Newtype -> binaryWildcardClass deriveNewtype - _ -> pure decl - _ -> pure decl - -deriveGenericRep - :: forall m - . (MonadError MultipleErrors m, MonadSupply m) - => SourceSpan - -> ModuleName - -> Declaration - -> [SourceType] - -> m ([Declaration], SourceType) -deriveGenericRep ss mn tyCon tyConArgs = - case tyCon of - DataDeclaration (ss', _) _ _ args dctors -> do - x <- freshIdent "x" - (reps, to, from) <- unzip3 <$> traverse makeInst dctors - let rep = toRepTy reps - inst | null reps = - -- If there are no cases, spin - [ ValueDecl (ss', []) (Ident "to") Public [] $ unguarded $ - lamCase x - [ CaseAlternative - [NullBinder] - (unguarded (App (Var ss Libs.I_to) (Var ss' (Qualified ByNullSourcePos x)))) - ] - , ValueDecl (ss', []) (Ident "from") Public [] $ unguarded $ - lamCase x - [ CaseAlternative - [NullBinder] - (unguarded (App (Var ss Libs.I_from) (Var ss' (Qualified ByNullSourcePos x)))) - ] - ] - | otherwise = - [ ValueDecl (ss', []) (Ident "to") Public [] $ unguarded $ - lamCase x (zipWith ($) (map underBinder (sumBinders (length dctors))) to) - , ValueDecl (ss', []) (Ident "from") Public [] $ unguarded $ - lamCase x (zipWith ($) (map underExpr (sumExprs (length dctors))) from) - ] - - subst = zipWith ((,) . fst) args tyConArgs - return (inst, replaceAllTypeVars subst rep) - _ -> internalError "deriveGenericRep: expected DataDeclaration" - - where - - select :: (a -> a) -> (a -> a) -> Int -> [a -> a] - select _ _ 0 = [] - select _ _ 1 = [id] - select l r n = take (n - 1) (iterate (r .) l) ++ [compN (n - 1) r] - - sumBinders :: Int -> [Binder -> Binder] - sumBinders = select (ConstructorBinder ss Libs.C_Inl . pure) - (ConstructorBinder ss Libs.C_Inr . pure) - - sumExprs :: Int -> [Expr -> Expr] - sumExprs = select (App (Constructor ss Libs.C_Inl)) - (App (Constructor ss Libs.C_Inr)) - - compN :: Int -> (a -> a) -> a -> a - compN 0 _ = id - compN n f = f . compN (n - 1) f - - makeInst - :: DataConstructorDeclaration - -> m (SourceType, CaseAlternative, CaseAlternative) - makeInst (DataConstructorDeclaration _ ctorName args) = do - let args' = map snd args - (ctorTy, matchProduct, ctorArgs, matchCtor, mkProduct) <- makeProduct args' - return ( srcTypeApp (srcTypeApp (srcTypeConstructor Libs.Constructor) - (srcTypeLevelString $ mkString (runProperName ctorName))) - ctorTy - , CaseAlternative [ ConstructorBinder ss Libs.C_Constructor [matchProduct] ] - (unguarded (foldl' App (Constructor ss (Qualified (ByModuleName mn) ctorName)) ctorArgs)) - , CaseAlternative [ ConstructorBinder ss (Qualified (ByModuleName mn) ctorName) matchCtor ] - (unguarded (App (Constructor ss Libs.C_Constructor) mkProduct)) - ) - - makeProduct - :: [SourceType] - -> m (SourceType, Binder, [Expr], [Binder], Expr) - makeProduct [] = - pure (srcTypeConstructor Libs.NoArguments, NullBinder, [], [], Constructor ss Libs.C_NoArguments) - makeProduct args = do - (tys, bs1, es1, bs2, es2) <- unzip5 <$> traverse makeArg args - pure ( foldr1 (\f -> srcTypeApp (srcTypeApp (srcTypeConstructor Libs.Product) f)) tys - , foldr1 (\b1 b2 -> ConstructorBinder ss Libs.C_Product [b1, b2]) bs1 - , es1 - , bs2 - , foldr1 (\e1 -> App (App (Constructor ss Libs.C_Product) e1)) es2 - ) - - makeArg :: SourceType -> m (SourceType, Binder, Expr, Binder, Expr) - makeArg arg = do - argName <- freshIdent "arg" - pure ( srcTypeApp (srcTypeConstructor Libs.Argument) arg - , ConstructorBinder ss Libs.C_Argument [ VarBinder ss argName ] - , Var ss (Qualified (BySourcePos $ spanStart ss) argName) - , VarBinder ss argName - , App (Constructor ss Libs.C_Argument) (Var ss (Qualified (BySourcePos $ spanStart ss) argName)) - ) - - underBinder :: (Binder -> Binder) -> CaseAlternative -> CaseAlternative - underBinder f (CaseAlternative bs e) = CaseAlternative (map f bs) e - - underExpr :: (Expr -> Expr) -> CaseAlternative -> CaseAlternative - underExpr f (CaseAlternative b [MkUnguarded e]) = CaseAlternative b (unguarded (f e)) - underExpr _ _ = internalError "underExpr: expected unguarded alternative" - - toRepTy :: [SourceType] -> SourceType - toRepTy [] = srcTypeConstructor Libs.NoConstructors - toRepTy [only] = only - toRepTy ctors = foldr1 (\f -> srcTypeApp (srcTypeApp (srcTypeConstructor Libs.Sum) f)) ctors - -checkIsWildcard :: MonadError MultipleErrors m => SourceSpan -> ProperName 'TypeName -> SourceType -> m () -checkIsWildcard _ _ (TypeWildcard _ UnnamedWildcard) = return () -checkIsWildcard ss tyConNm _ = - throwError . errorMessage' ss $ ExpectedWildcard tyConNm - -deriveNewtype - :: forall m - . MonadError MultipleErrors m - => Declaration - -> [SourceType] - -> m ([Declaration], SourceType) -deriveNewtype tyCon tyConArgs = - case tyCon of - DataDeclaration (ss', _) Data name _ _ -> - throwError . errorMessage' ss' $ CannotDeriveNewtypeForData name - DataDeclaration _ Newtype name args dctors -> do - (_, (_, ty)) <- checkNewtype name dctors - let subst = zipWith ((,) . fst) args tyConArgs - return ([], replaceAllTypeVars subst ty) - _ -> internalError "deriveNewtype: expected DataDeclaration" - -findTypeDecl - :: (MonadError MultipleErrors m) - => SourceSpan - -> ProperName 'TypeName - -> [Declaration] - -> m Declaration -findTypeDecl ss tyConNm = note (errorMessage' ss $ CannotFindDerivingType tyConNm) . find isTypeDecl - where - isTypeDecl :: Declaration -> Bool - isTypeDecl (DataDeclaration _ _ nm _ _) = nm == tyConNm - isTypeDecl _ = False diff --git a/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeDeclarations.hs b/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeDeclarations.hs deleted file mode 100644 index ef00748d..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Sugar/TypeDeclarations.hs +++ /dev/null @@ -1,97 +0,0 @@ --- | --- This module implements the desugaring pass which replaces top-level type --- declarations with type annotations on the corresponding expression. --- -module Language.PureScript.Sugar.TypeDeclarations - ( desugarTypeDeclarationsModule - ) where - -import Prelude - -import Control.Monad (unless) -import Control.Monad.Error.Class (MonadError(..)) - -import Language.PureScript.AST (Declaration(..), ErrorMessageHint(..), Expr(..), GuardedExpr(..), KindSignatureFor(..), pattern MkUnguarded, Module(..), RoleDeclarationData(..), TypeDeclarationData(..), TypeInstanceBody(..), pattern ValueDecl, declSourceSpan, everywhereOnValuesTopDownM) -import Language.PureScript.Names (Ident, coerceProperName) -import Language.PureScript.Environment (DataDeclType(..), NameKind) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), addHint, errorMessage', rethrow) - --- | --- Replace all top level type declarations in a module with type annotations --- -desugarTypeDeclarationsModule - :: forall m - . MonadError MultipleErrors m - => Module - -> m Module -desugarTypeDeclarationsModule (Module modSS coms name ds exps) = - rethrow (addHint (ErrorInModule name)) $ do - checkKindDeclarations ds - checkRoleDeclarations Nothing ds - Module modSS coms name <$> desugarTypeDeclarations ds <*> pure exps - where - - desugarTypeDeclarations :: [Declaration] -> m [Declaration] - desugarTypeDeclarations (TypeDeclaration (TypeDeclarationData sa name' ty) : d : rest) = do - (_, nameKind, val) <- fromValueDeclaration d - desugarTypeDeclarations (ValueDecl sa name' nameKind [] [MkUnguarded (TypedValue True val ty)] : rest) - where - fromValueDeclaration :: Declaration -> m (Ident, NameKind, Expr) - fromValueDeclaration (ValueDecl _ name'' nameKind [] [MkUnguarded val]) - | name' == name'' = return (name'', nameKind, val) - fromValueDeclaration d' = - throwError . errorMessage' (declSourceSpan d') $ OrphanTypeDeclaration name' - desugarTypeDeclarations [TypeDeclaration (TypeDeclarationData (ss, _) name' _)] = - throwError . errorMessage' ss $ OrphanTypeDeclaration name' - desugarTypeDeclarations (ValueDecl sa name' nameKind bs val : rest) = do - let (_, f, _) = everywhereOnValuesTopDownM return go return - f' = mapM (\(GuardedExpr g e) -> GuardedExpr g <$> f e) - (:) <$> (ValueDecl sa name' nameKind bs <$> f' val) - <*> desugarTypeDeclarations rest - where - go (Let w ds' val') = Let w <$> desugarTypeDeclarations ds' <*> pure val' - go other = return other - desugarTypeDeclarations (TypeInstanceDeclaration sa na ch idx nm deps cls args (ExplicitInstance ds') : rest) = - (:) <$> (TypeInstanceDeclaration sa na ch idx nm deps cls args . ExplicitInstance <$> desugarTypeDeclarations ds') - <*> desugarTypeDeclarations rest - desugarTypeDeclarations (d:rest) = (:) d <$> desugarTypeDeclarations rest - desugarTypeDeclarations [] = return [] - - checkKindDeclarations :: [Declaration] -> m () - checkKindDeclarations (KindDeclaration sa kindFor name' _ : d : rest) = do - unless (matchesDeclaration d) . throwError . errorMessage' (fst sa) $ OrphanKindDeclaration name' - checkKindDeclarations rest - where - matchesDeclaration :: Declaration -> Bool - matchesDeclaration (DataDeclaration _ Data name'' _ _) = kindFor == DataSig && name' == name'' - matchesDeclaration (DataDeclaration _ Newtype name'' _ _) = kindFor == NewtypeSig && name' == name'' - matchesDeclaration (TypeSynonymDeclaration _ name'' _ _) = kindFor == TypeSynonymSig && name' == name'' - matchesDeclaration (TypeClassDeclaration _ name'' _ _ _ _) = kindFor == ClassSig && name' == coerceProperName name'' - matchesDeclaration _ = False - checkKindDeclarations (KindDeclaration sa _ name' _ : _) = do - throwError . errorMessage' (fst sa) $ OrphanKindDeclaration name' - checkKindDeclarations (_ : rest) = checkKindDeclarations rest - checkKindDeclarations [] = return () - - checkRoleDeclarations :: Maybe Declaration -> [Declaration] -> m () - checkRoleDeclarations Nothing (RoleDeclaration RoleDeclarationData{..} : _) = - throwError . errorMessage' (fst rdeclSourceAnn) $ OrphanRoleDeclaration rdeclIdent - checkRoleDeclarations (Just (RoleDeclaration (RoleDeclarationData _ name' _))) ((RoleDeclaration RoleDeclarationData{..}) : _) | name' == rdeclIdent = - throwError . errorMessage' (fst rdeclSourceAnn) $ DuplicateRoleDeclaration rdeclIdent - checkRoleDeclarations (Just d) (rd@(RoleDeclaration RoleDeclarationData{..}) : rest) = do - unless (matchesDeclaration d) . throwError . errorMessage' (fst rdeclSourceAnn) $ OrphanRoleDeclaration rdeclIdent - unless (isSupported d) . throwError . errorMessage' (fst rdeclSourceAnn) $ UnsupportedRoleDeclaration - checkRoleDeclarations (Just rd) rest - where - isSupported :: Declaration -> Bool - isSupported DataDeclaration{} = True - isSupported ExternDataDeclaration{} = True - isSupported _ = False - matchesDeclaration :: Declaration -> Bool - matchesDeclaration (DataDeclaration _ _ name' _ _) = rdeclIdent == name' - matchesDeclaration (ExternDataDeclaration _ name' _) = rdeclIdent == name' - matchesDeclaration (TypeSynonymDeclaration _ name' _ _) = rdeclIdent == name' - matchesDeclaration (TypeClassDeclaration _ name' _ _ _ _) = rdeclIdent == coerceProperName name' - matchesDeclaration _ = False - checkRoleDeclarations _ (d : rest) = checkRoleDeclarations (Just d) rest - checkRoleDeclarations _ [] = return () diff --git a/claude-help/original-compiler/src/Language/PureScript/Traversals.hs b/claude-help/original-compiler/src/Language/PureScript/Traversals.hs deleted file mode 100644 index 1226342c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Traversals.hs +++ /dev/null @@ -1,23 +0,0 @@ --- | Common functions for implementing generic traversals -module Language.PureScript.Traversals where - -import Prelude - -sndM :: (Functor f) => (b -> f c) -> (a, b) -> f (a, c) -sndM f (a, b) = (a, ) <$> f b - -sndM' :: (Functor f) => (a -> b -> f c) -> (a, b) -> f (a, c) -sndM' f (a, b) = (a, ) <$> f a b - -thirdM :: (Functor f) => (c -> f d) -> (a, b, c) -> f (a, b, d) -thirdM f (a, b, c) = (a, b, ) <$> f c - -pairM :: (Applicative f) => (a -> f c) -> (b -> f d) -> (a, b) -> f (c, d) -pairM f g (a, b) = (,) <$> f a <*> g b - -eitherM :: (Applicative f) => (a -> f c) -> (b -> f d) -> Either a b -> f (Either c d) -eitherM f _ (Left a) = Left <$> f a -eitherM _ g (Right b) = Right <$> g b - -defS :: (Monad m) => st -> val -> m (st, val) -defS s val = return (s, val) diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker.hs deleted file mode 100644 index c0d92e0c..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker.hs +++ /dev/null @@ -1,780 +0,0 @@ --- | --- The top-level type checker, which checks all declarations in a module. --- -module Language.PureScript.TypeChecker - ( module T - , typeCheckModule - , checkNewtype - ) where - -import Prelude -import Protolude (headMay, maybeToLeft, ordNub, headDef) - -import Control.Lens ((^..), _2) -import Control.Monad (when, unless, void, forM, zipWithM_) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State.Class (modify, gets) -import Control.Monad.Writer.Class (tell) - -import Data.Foldable (for_, traverse_, toList) -import Data.List (nubBy, (\\), sort, group) -import Data.Maybe (fromMaybe, listToMaybe, mapMaybe) -import Data.Either (partitionEithers) -import Data.Text (Text) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M -import Data.IntMap.Lazy qualified as IM -import Data.Set qualified as S -import Data.Text qualified as T - -import Language.PureScript.AST -import Language.PureScript.AST.Declarations.ChainId (ChainId) -import Language.PureScript.Constants.Libs qualified as Libs -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType(..), Environment(..), FunctionalDependency, NameKind(..), NameVisibility(..), TypeClassData(..), TypeKind(..), isDictTypeName, kindArity, makeTypeClassData, nominalRolesForKind, tyFunction) -import Language.PureScript.Errors (SimpleErrorMessage(..), addHint, errorMessage, errorMessage', positionedError, rethrow, warnAndRethrow, MultipleErrors) -import Language.PureScript.Linter (checkExhaustiveExpr) -import Language.PureScript.Linter.Wildcards (ignoreWildcardsUnderCompleteTypeSignatures) -import Language.PureScript.Names (Ident, ModuleName, ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, disqualify, isPlainIdent, mkQualified) -import Language.PureScript.Roles (Role) -import Language.PureScript.Sugar.Names.Env (Exports(..)) -import Language.PureScript.TypeChecker.Kinds as T -import Language.PureScript.TypeChecker.Monad as T -import Language.PureScript.TypeChecker.Roles as T -import Language.PureScript.TypeChecker.Synonyms as T -import Language.PureScript.TypeChecker.Types as T -import Language.PureScript.TypeChecker.Unify (varIfUnknown) -import Language.PureScript.TypeClassDictionaries (NamedDict, TypeClassDictionaryInScope(..)) -import Language.PureScript.Types (Constraint(..), SourceConstraint, SourceType, Type(..), containsForAll, eqType, everythingOnTypes, overConstraintArgs, srcInstanceType, unapplyTypes) - -addDataType - :: ModuleName - -> DataDeclType - -> ProperName 'TypeName - -> [(Text, Maybe SourceType, Role)] - -> [(DataConstructorDeclaration, SourceType)] - -> SourceType - -> TypeCheckM () -addDataType moduleName dtype name args dctors ctorKind = do - env <- getEnv - let mapDataCtor (DataConstructorDeclaration _ ctorName vars) = (ctorName, snd <$> vars) - qualName = Qualified (ByModuleName moduleName) name - hasSig = qualName `M.member` types env - putEnv $ env { types = M.insert qualName (ctorKind, DataType dtype args (map (mapDataCtor . fst) dctors)) (types env) } - unless (hasSig || isDictTypeName name || not (containsForAll ctorKind)) $ do - tell . errorMessage $ MissingKindDeclaration (if dtype == Newtype then NewtypeSig else DataSig) name ctorKind - for_ dctors $ \(DataConstructorDeclaration _ dctor fields, polyType) -> - warnAndRethrow (addHint (ErrorInDataConstructor dctor)) $ - addDataConstructor moduleName dtype name dctor fields polyType - -addDataConstructor - :: ModuleName - -> DataDeclType - -> ProperName 'TypeName - -> ProperName 'ConstructorName - -> [(Ident, SourceType)] - -> SourceType - -> TypeCheckM () -addDataConstructor moduleName dtype name dctor dctorArgs polyType = do - let fields = fst <$> dctorArgs - env <- getEnv - checkTypeSynonyms polyType - putEnv $ env { dataConstructors = M.insert (Qualified (ByModuleName moduleName) dctor) (dtype, name, polyType, fields) (dataConstructors env) } - -checkRoleDeclaration - :: ModuleName - -> RoleDeclarationData - -> TypeCheckM () -checkRoleDeclaration moduleName (RoleDeclarationData (ss, _) name declaredRoles) = do - warnAndRethrow (addHint (ErrorInRoleDeclaration name) . addHint (positionedError ss)) $ do - env <- getEnv - let qualName = Qualified (ByModuleName moduleName) name - case M.lookup qualName (types env) of - Just (kind, DataType dtype args dctors) -> do - checkRoleDeclarationArity name declaredRoles (length args) - checkRoles args declaredRoles - let args' = zipWith (\(v, k, _) r -> (v, k, r)) args declaredRoles - putEnv $ env { types = M.insert qualName (kind, DataType dtype args' dctors) (types env) } - Just (kind, ExternData _) -> do - checkRoleDeclarationArity name declaredRoles (kindArity kind) - putEnv $ env { types = M.insert qualName (kind, ExternData declaredRoles) (types env) } - _ -> internalError "Unsupported role declaration" - -addTypeSynonym - :: ModuleName - -> ProperName 'TypeName - -> [(Text, Maybe SourceType)] - -> SourceType - -> SourceType - -> TypeCheckM () -addTypeSynonym moduleName name args ty kind = do - env <- getEnv - checkTypeSynonyms ty - let qualName = Qualified (ByModuleName moduleName) name - hasSig = qualName `M.member` types env - unless (hasSig || not (containsForAll kind)) $ do - tell . errorMessage $ MissingKindDeclaration TypeSynonymSig name kind - putEnv $ env { types = M.insert qualName (kind, TypeSynonym) (types env) - , typeSynonyms = M.insert qualName (args, ty) (typeSynonyms env) } - -valueIsNotDefined - :: ModuleName - -> Ident - -> TypeCheckM () -valueIsNotDefined moduleName name = do - env <- getEnv - case M.lookup (Qualified (ByModuleName moduleName) name) (names env) of - Just _ -> throwError . errorMessage $ RedefinedIdent name - Nothing -> return () - -addValue - :: ModuleName - -> Ident - -> SourceType - -> NameKind - -> TypeCheckM () -addValue moduleName name ty nameKind = do - env <- getEnv - putEnv (env { names = M.insert (Qualified (ByModuleName moduleName) name) (ty, nameKind, Defined) (names env) }) - -addTypeClass - :: ModuleName - -> Qualified (ProperName 'ClassName) - -> [(Text, Maybe SourceType)] - -> [SourceConstraint] - -> [FunctionalDependency] - -> [Declaration] - -> SourceType - -> TypeCheckM () -addTypeClass _ qualifiedClassName args implies dependencies ds kind = do - env <- getEnv - newClass <- mkNewClass - let qualName = fmap coerceProperName qualifiedClassName - hasSig = qualName `M.member` types env - unless (hasSig || not (containsForAll kind)) $ do - tell . errorMessage $ MissingKindDeclaration ClassSig (disqualify qualName) kind - putEnv $ env { types = M.insert qualName (kind, ExternData (nominalRolesForKind kind)) (types env) - , typeClasses = M.insert qualifiedClassName newClass (typeClasses env) } - where - classMembers :: [(Ident, SourceType)] - classMembers = map toPair ds - - mkNewClass :: TypeCheckM TypeClassData - mkNewClass = do - env <- getEnv - implies' <- (traverse . overConstraintArgs . traverse) replaceAllTypeSynonyms implies - let ctIsEmpty = null classMembers && all (typeClassIsEmpty . findSuperClass env) implies' - pure $ makeTypeClassData args classMembers implies' dependencies ctIsEmpty - where - findSuperClass env c = case M.lookup (constraintClass c) (typeClasses env) of - Just tcd -> tcd - Nothing -> internalError "Unknown super class in TypeClassDeclaration" - - toPair (TypeDeclaration (TypeDeclarationData _ ident ty)) = (ident, ty) - toPair _ = internalError "Invalid declaration in TypeClassDeclaration" - -addTypeClassDictionaries - :: QualifiedBy - -> M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)) - -> TypeCheckM () -addTypeClassDictionaries mn entries = - modify $ \st -> st { checkEnv = (checkEnv st) { typeClassDictionaries = insertState st } } - where insertState st = M.insertWith (M.unionWith (M.unionWith (<>))) mn entries (typeClassDictionaries . checkEnv $ st) - -checkDuplicateTypeArguments - :: [Text] - -> TypeCheckM () -checkDuplicateTypeArguments args = for_ firstDup $ \dup -> - throwError . errorMessage $ DuplicateTypeArgument dup - where - firstDup :: Maybe Text - firstDup = listToMaybe $ args \\ ordNub args - -checkTypeClassInstance - :: TypeClassData - -> Int -- ^ index of type class argument - -> SourceType - -> TypeCheckM () -checkTypeClassInstance cls i = check where - -- If the argument is determined via fundeps then we are less restrictive in - -- what type is allowed. This is because the type cannot be used to influence - -- which instance is selected. Currently the only weakened restriction is that - -- row types are allowed in determined type class arguments. - isFunDepDetermined = S.member i (typeClassDeterminedArguments cls) - check = \case - TypeVar _ _ -> return () - TypeLevelString _ _ -> return () - TypeLevelInt _ _ -> return () - TypeConstructor _ _ -> return () - TypeApp _ t1 t2 -> check t1 >> check t2 - KindApp _ t k -> check t >> check k - KindedType _ t _ -> check t - REmpty _ | isFunDepDetermined -> return () - RCons _ _ hd tl | isFunDepDetermined -> check hd >> check tl - ty -> throwError . errorMessage $ InvalidInstanceHead ty - --- | --- Check that type synonyms are fully-applied in a type --- -checkTypeSynonyms - :: SourceType - -> TypeCheckM () -checkTypeSynonyms = void . replaceAllTypeSynonyms - --- | --- Type check all declarations in a module --- --- At this point, many declarations will have been desugared, but it is still necessary to --- --- * Kind-check all types and add them to the @Environment@ --- --- * Type-check all values and add them to the @Environment@ --- --- * Infer all type roles and add them to the @Environment@ --- --- * Bring type class instances into scope --- --- * Process module imports --- -typeCheckAll - :: ModuleName - -> [Declaration] - -> TypeCheckM [Declaration] -typeCheckAll moduleName = traverse go - where - go :: Declaration -> TypeCheckM Declaration - go (DataDeclaration sa@(ss, _) dtype name args dctors) = do - warnAndRethrow (addHint (ErrorInTypeConstructor name) . addHint (positionedError ss)) $ do - when (dtype == Newtype) $ void $ checkNewtype name dctors - checkDuplicateTypeArguments $ map fst args - (dataCtors, ctorKind) <- kindOfData moduleName (sa, name, args, dctors) - let args' = args `withKinds` ctorKind - env <- getEnv - dctors' <- traverse (replaceTypeSynonymsInDataConstructor . fst) dataCtors - let args'' = args' `withRoles` inferRoles env moduleName name args' dctors' - addDataType moduleName dtype name args'' dataCtors ctorKind - return $ DataDeclaration sa dtype name args dctors - go d@(DataBindingGroupDeclaration tys) = do - let tysList = NEL.toList tys - syns = mapMaybe toTypeSynonym tysList - dataDecls = mapMaybe toDataDecl tysList - roleDecls = mapMaybe toRoleDecl tysList - clss = mapMaybe toClassDecl tysList - bindingGroupNames = ordNub ((syns ^.. traverse . _2) ++ (dataDecls ^.. traverse . _2 . _2) ++ fmap coerceProperName (clss ^.. traverse . _2 . _2)) - sss = fmap declSourceSpan tys - warnAndRethrow (addHint (ErrorInDataBindingGroup bindingGroupNames) . addHint (PositionedError sss)) $ do - env <- getEnv - (syn_ks, data_ks, cls_ks) <- kindsOfAll moduleName syns (fmap snd dataDecls) (fmap snd clss) - for_ (zip syns syn_ks) $ \((_, name, args, _), (elabTy, kind)) -> do - checkDuplicateTypeArguments $ map fst args - let args' = args `withKinds` kind - addTypeSynonym moduleName name args' elabTy kind - let dataDeclsWithKinds = zipWith (\(dtype, (_, name, args, _)) (dataCtors, ctorKind) -> - (dtype, name, args `withKinds` ctorKind, dataCtors, ctorKind)) dataDecls data_ks - inferRoles' <- fmap (inferDataBindingGroupRoles env moduleName roleDecls) . - forM dataDeclsWithKinds $ \(_, name, args, dataCtors, _) -> - (name, args,) <$> traverse (replaceTypeSynonymsInDataConstructor . fst) dataCtors - for_ dataDeclsWithKinds $ \(dtype, name, args', dataCtors, ctorKind) -> do - when (dtype == Newtype) $ void $ checkNewtype name (map fst dataCtors) - checkDuplicateTypeArguments $ map fst args' - let args'' = args' `withRoles` inferRoles' name args' - addDataType moduleName dtype name args'' dataCtors ctorKind - for_ roleDecls $ checkRoleDeclaration moduleName - for_ (zip clss cls_ks) $ \((deps, (sa, pn, _, _, _)), (args', implies', tys', kind)) -> do - let qualifiedClassName = Qualified (ByModuleName moduleName) pn - guardWith (errorMessage (DuplicateTypeClass pn (fst sa))) $ - not (M.member qualifiedClassName (typeClasses env)) - addTypeClass moduleName qualifiedClassName (fmap Just <$> args') implies' deps tys' kind - return d - where - toTypeSynonym (TypeSynonymDeclaration sa nm args ty) = Just (sa, nm, args, ty) - toTypeSynonym _ = Nothing - toDataDecl (DataDeclaration sa dtype nm args dctors) = Just (dtype, (sa, nm, args, dctors)) - toDataDecl _ = Nothing - toRoleDecl (RoleDeclaration rdd) = Just rdd - toRoleDecl _ = Nothing - toClassDecl (TypeClassDeclaration sa nm args implies deps decls) = Just (deps, (sa, nm, args, implies, decls)) - toClassDecl _ = Nothing - go (TypeSynonymDeclaration sa@(ss, _) name args ty) = do - warnAndRethrow (addHint (ErrorInTypeSynonym name) . addHint (positionedError ss) ) $ do - checkDuplicateTypeArguments $ map fst args - (elabTy, kind) <- kindOfTypeSynonym moduleName (sa, name, args, ty) - let args' = args `withKinds` kind - addTypeSynonym moduleName name args' elabTy kind - return $ TypeSynonymDeclaration sa name args ty - go (KindDeclaration sa@(ss, _) kindFor name ty) = do - warnAndRethrow (addHint (ErrorInKindDeclaration name) . addHint (positionedError ss)) $ do - elabTy <- withFreshSubstitution $ checkKindDeclaration moduleName ty - env <- getEnv - putEnv $ env { types = M.insert (Qualified (ByModuleName moduleName) name) (elabTy, LocalTypeVariable) (types env) } - return $ KindDeclaration sa kindFor name elabTy - go d@(RoleDeclaration rdd) = do - checkRoleDeclaration moduleName rdd - return d - go TypeDeclaration{} = - internalError "Type declarations should have been removed before typeCheckAlld" - go (ValueDecl sa@(ss, _) name nameKind [] [MkUnguarded val]) = do - env <- getEnv - let declHint = if isPlainIdent name then addHint (ErrorInValueDeclaration name) else id - warnAndRethrow (declHint . addHint (positionedError ss)) $ do - val' <- checkExhaustiveExpr ss env moduleName val - valueIsNotDefined moduleName name - typesOf NonRecursiveBindingGroup moduleName [((sa, name), val')] >>= \case - [(_, (val'', ty))] -> do - addValue moduleName name ty nameKind - return $ ValueDecl sa name nameKind [] [MkUnguarded val''] - _ -> internalError "typesOf did not return a singleton" - go ValueDeclaration{} = internalError "Binders were not desugared" - go BoundValueDeclaration{} = internalError "BoundValueDeclaration should be desugared" - go (BindingGroupDeclaration vals) = do - env <- getEnv - let sss = fmap (\(((ss, _), _), _, _) -> ss) vals - warnAndRethrow (addHint (ErrorInBindingGroup (fmap (\((_, ident), _, _) -> ident) vals)) . addHint (PositionedError sss)) $ do - for_ vals $ \((_, ident), _, _) -> valueIsNotDefined moduleName ident - vals' <- NEL.toList <$> traverse (\(sai@((ss, _), _), nk, expr) -> (sai, nk,) <$> checkExhaustiveExpr ss env moduleName expr) vals - tys <- typesOf RecursiveBindingGroup moduleName $ fmap (\(sai, _, ty) -> (sai, ty)) vals' - vals'' <- forM [ (sai, val, nameKind, ty) - | (sai@(_, name), nameKind, _) <- vals' - , ((_, name'), (val, ty)) <- tys - , name == name' - ] $ \(sai@(_, name), val, nameKind, ty) -> do - addValue moduleName name ty nameKind - return (sai, nameKind, val) - return . BindingGroupDeclaration $ NEL.fromList vals'' - go d@(ExternDataDeclaration (ss, _) name kind) = do - warnAndRethrow (addHint (ErrorInForeignImportData name) . addHint (positionedError ss)) $ do - elabKind <- withFreshSubstitution $ checkKindDeclaration moduleName kind - env <- getEnv - let qualName = Qualified (ByModuleName moduleName) name - roles = nominalRolesForKind elabKind - putEnv $ env { types = M.insert qualName (elabKind, ExternData roles) (types env) } - return d - go d@(ExternDeclaration (ss, _) name ty) = do - warnAndRethrow (addHint (ErrorInForeignImport name) . addHint (positionedError ss)) $ do - env <- getEnv - (elabTy, kind) <- withFreshSubstitution $ do - ((unks, ty'), kind) <- kindOfWithUnknowns ty - ty'' <- varIfUnknown unks ty' - pure (ty'', kind) - checkTypeKind elabTy kind - case M.lookup (Qualified (ByModuleName moduleName) name) (names env) of - Just _ -> throwError . errorMessage $ RedefinedIdent name - Nothing -> putEnv (env { names = M.insert (Qualified (ByModuleName moduleName) name) (elabTy, External, Defined) (names env) }) - return d - go d@FixityDeclaration{} = return d - go d@ImportDeclaration{} = return d - go d@(TypeClassDeclaration sa@(ss, _) pn args implies deps tys) = do - warnAndRethrow (addHint (ErrorInTypeClassDeclaration pn) . addHint (positionedError ss)) $ do - env <- getEnv - let qualifiedClassName = Qualified (ByModuleName moduleName) pn - guardWith (errorMessage (DuplicateTypeClass pn ss)) $ - not (M.member qualifiedClassName (typeClasses env)) - (args', implies', tys', kind) <- kindOfClass moduleName (sa, pn, args, implies, tys) - addTypeClass moduleName qualifiedClassName (fmap Just <$> args') implies' deps tys' kind - return d - go (TypeInstanceDeclaration _ _ _ _ (Left _) _ _ _ _) = internalError "typeCheckAll: type class instance generated name should have been desugared" - go d@(TypeInstanceDeclaration sa@(ss, _) _ ch idx (Right dictName) deps className tys body) = - rethrow (addHint (ErrorInInstance className tys) . addHint (positionedError ss)) $ do - env <- getEnv - let qualifiedDictName = Qualified (ByModuleName moduleName) dictName - flip (traverse_ . traverse_) (typeClassDictionaries env) $ \dictionaries -> - guardWith (errorMessage (DuplicateInstance dictName ss)) $ - not (M.member qualifiedDictName dictionaries) - case M.lookup className (typeClasses env) of - Nothing -> internalError "typeCheckAll: Encountered unknown type class in instance declaration" - Just typeClass -> do - checkInstanceArity dictName className typeClass tys - (deps', kinds', tys', vars) <- withFreshSubstitution $ checkInstanceDeclaration moduleName (sa, deps, className, tys) - tys'' <- traverse replaceAllTypeSynonyms tys' - zipWithM_ (checkTypeClassInstance typeClass) [0..] tys'' - let nonOrphanModules = findNonOrphanModules className typeClass tys'' - checkOrphanInstance dictName className tys'' nonOrphanModules - let chainId = Just ch - checkOverlappingInstance ss chainId dictName vars className typeClass tys'' nonOrphanModules - _ <- traverseTypeInstanceBody checkInstanceMembers body - deps'' <- (traverse . overConstraintArgs . traverse) replaceAllTypeSynonyms deps' - let dict = - TypeClassDictionaryInScope chainId idx qualifiedDictName [] className vars kinds' tys'' (Just deps'') $ - if isPlainIdent dictName then Nothing else Just $ srcInstanceType ss vars className tys'' - addTypeClassDictionaries (ByModuleName moduleName) . M.singleton className $ M.singleton (tcdValue dict) (pure dict) - return d - - checkInstanceArity :: Ident -> Qualified (ProperName 'ClassName) -> TypeClassData -> [SourceType] -> TypeCheckM () - checkInstanceArity dictName className typeClass tys = do - let typeClassArity = length (typeClassArguments typeClass) - instanceArity = length tys - when (typeClassArity /= instanceArity) $ - throwError . errorMessage $ ClassInstanceArityMismatch dictName className typeClassArity instanceArity - - checkInstanceMembers :: [Declaration] -> TypeCheckM [Declaration] - checkInstanceMembers instDecls = do - let idents = sort - . map (headDef $ internalError "checkInstanceMembers: Empty instance declaration list") - . group . map memberName $ instDecls - for_ (firstDuplicate idents) $ \ident -> - throwError . errorMessage $ DuplicateValueDeclaration ident - return instDecls - where - memberName :: Declaration -> Ident - memberName (ValueDeclaration vd) = valdeclIdent vd - memberName _ = internalError "checkInstanceMembers: Invalid declaration in type instance definition" - - firstDuplicate :: (Eq a) => [a] -> Maybe a - firstDuplicate (x : xs@(y : _)) - | x == y = Just x - | otherwise = firstDuplicate xs - firstDuplicate _ = Nothing - - findNonOrphanModules - :: Qualified (ProperName 'ClassName) - -> TypeClassData - -> [SourceType] - -> S.Set ModuleName - findNonOrphanModules (Qualified (ByModuleName mn') _) typeClass tys' = nonOrphanModules - where - nonOrphanModules :: S.Set ModuleName - nonOrphanModules = S.insert mn' nonOrphanModules' - - typeModule :: SourceType -> Maybe ModuleName - typeModule (TypeVar _ _) = Nothing - typeModule (TypeLevelString _ _) = Nothing - typeModule (TypeLevelInt _ _) = Nothing - typeModule (TypeConstructor _ (Qualified (ByModuleName mn'') _)) = Just mn'' - typeModule (TypeConstructor _ (Qualified (BySourcePos _) _)) = internalError "Unqualified type name in findNonOrphanModules" - typeModule (TypeApp _ t1 _) = typeModule t1 - typeModule (KindApp _ t1 _) = typeModule t1 - typeModule (KindedType _ t1 _) = typeModule t1 - typeModule _ = internalError "Invalid type in instance in findNonOrphanModules" - - modulesByTypeIndex :: IM.IntMap (Maybe ModuleName) - modulesByTypeIndex = IM.fromList (zip [0 ..] (typeModule <$> tys')) - - lookupModule :: Int -> S.Set ModuleName - lookupModule idx = case IM.lookup idx modulesByTypeIndex of - Just ms -> S.fromList (toList ms) - Nothing -> internalError "Unknown type index in findNonOrphanModules" - - -- If the instance is declared in a module that wouldn't be found based on a covering set - -- then it is considered an orphan - because we'd have a situation in which we expect an - -- instance but can't find it. So a valid module must be applicable across *all* covering - -- sets - therefore we take the intersection of covering set modules. - nonOrphanModules' :: S.Set ModuleName - nonOrphanModules' = foldl1 S.intersection (foldMap lookupModule `S.map` typeClassCoveringSets typeClass) - findNonOrphanModules _ _ _ = internalError "Unqualified class name in findNonOrphanModules" - - -- Check that the instance currently being declared doesn't overlap with any - -- other instance in any module that this instance wouldn't be considered an - -- orphan in. There are overlapping instance situations that won't be caught - -- by this, for example when combining multiparameter type classes with - -- flexible instances: the instances `Cls X y` and `Cls x Y` overlap and - -- could live in different modules but won't be caught here. - checkOverlappingInstance - :: SourceSpan - -> Maybe ChainId - -> Ident - -> [(Text, SourceType)] - -> Qualified (ProperName 'ClassName) - -> TypeClassData - -> [SourceType] - -> S.Set ModuleName - -> TypeCheckM () - checkOverlappingInstance ss ch dictName vars className typeClass tys' nonOrphanModules = do - for_ nonOrphanModules $ \m -> do - dicts <- M.toList <$> lookupTypeClassDictionariesForClass (ByModuleName m) className - - for_ dicts $ \(Qualified mn' ident, dictNel) -> do - for_ dictNel $ \dict -> do - -- ignore instances in the same instance chain - if ch == tcdChain dict || - instancesAreApart (typeClassCoveringSets typeClass) tys' (tcdInstanceTypes dict) - then return () - else do - let this = if isPlainIdent dictName then Right dictName else Left $ srcInstanceType ss vars className tys' - let that = Qualified mn' . maybeToLeft ident $ tcdDescription dict - throwError . errorMessage $ - OverlappingInstances className - tys' - [that, Qualified (ByModuleName moduleName) this] - - instancesAreApart - :: S.Set (S.Set Int) - -> [SourceType] - -> [SourceType] - -> Bool - instancesAreApart sets lhs rhs = all (any typesApart . S.toList) (S.toList sets) - where - typesApart :: Int -> Bool - typesApart i = typeHeadsApart (lhs !! i) (rhs !! i) - - -- Note: implementation doesn't need to care about all possible cases: - -- TUnknown, Skolem, etc. - typeHeadsApart :: SourceType -> SourceType -> Bool - typeHeadsApart l r | eqType l r = False - typeHeadsApart (TypeVar _ _) _ = False - typeHeadsApart _ (TypeVar _ _) = False - typeHeadsApart (KindedType _ t1 _) t2 = typeHeadsApart t1 t2 - typeHeadsApart t1 (KindedType _ t2 _) = typeHeadsApart t1 t2 - typeHeadsApart (TypeApp _ h1 t1) (TypeApp _ h2 t2) = typeHeadsApart h1 h2 || typeHeadsApart t1 t2 - typeHeadsApart _ _ = True - - checkOrphanInstance - :: Ident - -> Qualified (ProperName 'ClassName) - -> [SourceType] - -> S.Set ModuleName - -> TypeCheckM () - checkOrphanInstance dictName className tys' nonOrphanModules - | moduleName `S.member` nonOrphanModules = return () - | otherwise = throwError . errorMessage $ OrphanInstance dictName className nonOrphanModules tys' - - -- This function adds the argument kinds for a type constructor so that they may appear in the externs file, - -- extracted from the kind of the type constructor itself. - -- - withKinds :: [(Text, Maybe SourceType)] -> SourceType -> [(Text, Maybe SourceType)] - withKinds [] _ = [] - withKinds ss (ForAll _ _ _ _ k _) = withKinds ss k - withKinds (s@(_, Just _):ss) (TypeApp _ (TypeApp _ tyFn _) k2) | eqType tyFn tyFunction = s : withKinds ss k2 - withKinds ((s, Nothing):ss) (TypeApp _ (TypeApp _ tyFn k1) k2) | eqType tyFn tyFunction = (s, Just k1) : withKinds ss k2 - withKinds _ _ = internalError "Invalid arguments to withKinds" - - withRoles :: [(Text, Maybe SourceType)] -> [Role] -> [(Text, Maybe SourceType, Role)] - withRoles = zipWith $ \(v, k) r -> (v, k, r) - - replaceTypeSynonymsInDataConstructor :: DataConstructorDeclaration -> TypeCheckM DataConstructorDeclaration - replaceTypeSynonymsInDataConstructor DataConstructorDeclaration{..} = do - dataCtorFields' <- traverse (traverse replaceAllTypeSynonyms) dataCtorFields - return DataConstructorDeclaration - { dataCtorFields = dataCtorFields' - , .. - } - --- | Check that a newtype has just one data constructor with just one field, or --- throw an error. If the newtype is valid, this function returns the single --- data constructor declaration and the single field, as a 'proof' that the --- newtype was indeed a valid newtype. -checkNewtype - :: MonadError MultipleErrors m - => ProperName 'TypeName - -> [DataConstructorDeclaration] - -> m (DataConstructorDeclaration, (Ident, SourceType)) -checkNewtype _ [decl@(DataConstructorDeclaration _ _ [field])] = return (decl, field) -checkNewtype name _ = throwError . errorMessage $ InvalidNewtype name - --- | --- Type check an entire module and ensure all types and classes defined within the module that are --- required by exported members are also exported. --- -typeCheckModule - :: M.Map ModuleName Exports - -> Module - -> TypeCheckM Module -typeCheckModule _ (Module _ _ _ _ Nothing) = - internalError "exports should have been elaborated before typeCheckModule" -typeCheckModule modulesExports (Module ss coms mn decls (Just exps)) = - warnAndRethrow (addHint (ErrorInModule mn)) $ do - let (decls', imports) = partitionEithers $ fromImportDecl <$> decls - modify (\s -> s { checkCurrentModule = Just mn, checkCurrentModuleImports = imports }) - decls'' <- typeCheckAll mn $ ignoreWildcardsUnderCompleteTypeSignatures <$> decls' - checkSuperClassesAreExported <- getSuperClassExportCheck - for_ exps $ \e -> do - checkTypesAreExported e - checkClassMembersAreExported e - checkClassesAreExported e - checkSuperClassesAreExported e - checkDataConstructorsAreExported e - return $ Module ss coms mn (map toImportDecl imports ++ decls'') (Just exps) - where - - fromImportDecl - :: Declaration - -> Either Declaration - ( SourceAnn - , ModuleName - , ImportDeclarationType - , Maybe ModuleName - , M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - ) - fromImportDecl (ImportDeclaration sa moduleName importDeclarationType asModuleName) = - Right (sa, moduleName, importDeclarationType, asModuleName, foldMap exportedTypes $ M.lookup moduleName modulesExports) - fromImportDecl decl = Left decl - - toImportDecl - :: ( SourceAnn - , ModuleName - , ImportDeclarationType - , Maybe ModuleName - , M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - ) - -> Declaration - toImportDecl (sa, moduleName, importDeclarationType, asModuleName, _) = - ImportDeclaration sa moduleName importDeclarationType asModuleName - - qualify' :: a -> Qualified a - qualify' = Qualified (ByModuleName mn) - - getSuperClassExportCheck = do - classesToSuperClasses <- gets - ( M.map - ( S.fromList - . filter (\(Qualified mn' _) -> mn' == ByModuleName mn) - . fmap constraintClass - . typeClassSuperclasses - ) - . typeClasses - . checkEnv - ) - let - -- A function that, given a class name, returns the set of - -- transitive class dependencies that are defined in this - -- module. - transitiveSuperClassesFor - :: Qualified (ProperName 'ClassName) - -> S.Set (Qualified (ProperName 'ClassName)) - transitiveSuperClassesFor qname = - untilSame - (\s -> s <> foldMap (\n -> fromMaybe S.empty (M.lookup n classesToSuperClasses)) s) - (fromMaybe S.empty (M.lookup qname classesToSuperClasses)) - - superClassesFor qname = - fromMaybe S.empty (M.lookup qname classesToSuperClasses) - - pure $ checkSuperClassExport superClassesFor transitiveSuperClassesFor - moduleClassExports :: S.Set (Qualified (ProperName 'ClassName)) - moduleClassExports = S.fromList $ mapMaybe (\case - TypeClassRef _ name -> Just (qualify' name) - _ -> Nothing) exps - - untilSame :: Eq a => (a -> a) -> a -> a - untilSame f a = let a' = f a in if a == a' then a else untilSame f a' - - checkMemberExport :: (SourceType -> [DeclarationRef]) -> DeclarationRef -> TypeCheckM () - checkMemberExport extract dr@(TypeRef _ name dctors) = do - env <- getEnv - for_ (M.lookup (qualify' name) (types env)) $ \(k, _) -> do - -- TODO: remove? - -- let findModuleKinds = everythingOnTypes (++) $ \case - -- TypeConstructor _ (Qualified (ByModuleName mn') kindName) | mn' == mn -> [kindName] - -- _ -> [] - checkExport dr (extract k) - for_ (M.lookup (qualify' name) (typeSynonyms env)) $ \(_, ty) -> - checkExport dr (extract ty) - for_ dctors $ \dctors' -> - for_ dctors' $ \dctor -> - for_ (M.lookup (qualify' dctor) (dataConstructors env)) $ \(_, _, ty, _) -> - checkExport dr (extract ty) - checkMemberExport extract dr@(ValueRef _ name) = do - ty <- lookupVariable (qualify' name) - checkExport dr (extract ty) - checkMemberExport _ _ = return () - - checkSuperClassExport - :: (Qualified (ProperName 'ClassName) -> S.Set (Qualified (ProperName 'ClassName))) - -> (Qualified (ProperName 'ClassName) -> S.Set (Qualified (ProperName 'ClassName))) - -> DeclarationRef - -> TypeCheckM () - checkSuperClassExport superClassesFor transitiveSuperClassesFor dr@(TypeClassRef drss className) = do - let superClasses = superClassesFor (qualify' className) - -- thanks to laziness, the computation of the transitive - -- superclasses defined in-module will only occur if we actually - -- throw the error. Constructing the full set of transitive - -- superclasses is likely to be costly for every single term. - transitiveSuperClasses = transitiveSuperClassesFor (qualify' className) - unexported = S.difference superClasses moduleClassExports - unless (null unexported) - . throwError . errorMessage' drss - . TransitiveExportError dr - . map (TypeClassRef drss . disqualify) - $ toList transitiveSuperClasses - checkSuperClassExport _ _ _ = - return () - - checkExport :: DeclarationRef -> [DeclarationRef] -> TypeCheckM () - checkExport dr drs = case filter (not . exported) drs of - [] -> return () - hidden -> throwError . errorMessage' (declRefSourceSpan dr) $ TransitiveExportError dr (nubBy nubEq hidden) - where - exported e = any (exports e) exps - exports (TypeRef _ pn1 _) (TypeRef _ pn2 _) = pn1 == pn2 - exports (ValueRef _ id1) (ValueRef _ id2) = id1 == id2 - exports (TypeClassRef _ pn1) (TypeClassRef _ pn2) = pn1 == pn2 - exports _ _ = False - -- We avoid Eq for `nub`bing as the dctor part of `TypeRef` evaluates to - -- `error` for the values generated here (we don't need them anyway) - nubEq (TypeRef _ pn1 _) (TypeRef _ pn2 _) = pn1 == pn2 - nubEq r1 r2 = r1 == r2 - - - -- Check that all the type constructors defined in the current module that appear in member types - -- have also been exported from the module - checkTypesAreExported :: DeclarationRef -> TypeCheckM () - checkTypesAreExported ref = checkMemberExport findTcons ref - where - findTcons :: SourceType -> [DeclarationRef] - findTcons = everythingOnTypes (++) go - where - go (TypeConstructor _ (Qualified (ByModuleName mn') name)) | mn' == mn = - [TypeRef (declRefSourceSpan ref) name (internalError "Data constructors unused in checkTypesAreExported")] - go _ = [] - - -- Check that all the classes defined in the current module that appear in member types have also - -- been exported from the module - checkClassesAreExported :: DeclarationRef -> TypeCheckM () - checkClassesAreExported ref = checkMemberExport findClasses ref - where - findClasses :: SourceType -> [DeclarationRef] - findClasses = everythingOnTypes (++) go - where - go (ConstrainedType _ c _) = (fmap (TypeClassRef (declRefSourceSpan ref)) . extractCurrentModuleClass . constraintClass) c - go _ = [] - extractCurrentModuleClass :: Qualified (ProperName 'ClassName) -> [ProperName 'ClassName] - extractCurrentModuleClass (Qualified (ByModuleName mn') name) | mn == mn' = [name] - extractCurrentModuleClass _ = [] - - checkClassMembersAreExported :: DeclarationRef -> TypeCheckM () - checkClassMembersAreExported dr@(TypeClassRef ss' name) = do - let members = ValueRef ss' `map` - (headDef $ internalError "checkClassMembersAreExported: Empty class member list") - (mapMaybe findClassMembers decls) - let missingMembers = members \\ exps - unless (null missingMembers) . throwError . errorMessage' ss' $ TransitiveExportError dr missingMembers - where - findClassMembers :: Declaration -> Maybe [Ident] - findClassMembers (TypeClassDeclaration _ name' _ _ _ ds) | name == name' = Just $ map extractMemberName ds - findClassMembers (DataBindingGroupDeclaration decls') = headMay . mapMaybe findClassMembers $ NEL.toList decls' - findClassMembers _ = Nothing - extractMemberName :: Declaration -> Ident - extractMemberName (TypeDeclaration td) = tydeclIdent td - extractMemberName _ = internalError "Unexpected declaration in typeclass member list" - checkClassMembersAreExported _ = return () - - -- If a type is exported without data constructors, we warn on `Generic` or `Newtype` instances. - -- On the other hand if any data constructors are exported, we require all of them to be exported. - checkDataConstructorsAreExported :: DeclarationRef -> TypeCheckM () - checkDataConstructorsAreExported dr@(TypeRef ss' name (fromMaybe [] -> exportedDataConstructorsNames)) - | null exportedDataConstructorsNames = for_ - [ Libs.Generic - , Libs.Newtype - ] $ \className -> do - env <- getEnv - let dicts = foldMap (foldMap NEL.toList) $ - M.lookup (ByModuleName mn) (typeClassDictionaries env) >>= M.lookup className - when (any isDictOfTypeRef dicts) $ - tell . errorMessage' ss' $ HiddenConstructors dr className - | otherwise = do - env <- getEnv - let dataConstructorNames = fromMaybe [] $ - M.lookup (mkQualified name mn) (types env) >>= getDataConstructorNames . snd - missingDataConstructorsNames = dataConstructorNames \\ exportedDataConstructorsNames - unless (null missingDataConstructorsNames) $ - throwError . errorMessage' ss' $ TransitiveDctorExportError dr missingDataConstructorsNames - where - isDictOfTypeRef :: TypeClassDictionaryInScope a -> Bool - isDictOfTypeRef dict - | (TypeConstructor _ qualTyName, _, _) : _ <- unapplyTypes <$> tcdInstanceTypes dict - , qualTyName == Qualified (ByModuleName mn) name - = True - isDictOfTypeRef _ = False - getDataConstructorNames :: TypeKind -> Maybe [ProperName 'ConstructorName] - getDataConstructorNames (DataType _ _ constructors) = Just $ fst <$> constructors - getDataConstructorNames _ = Nothing - checkDataConstructorsAreExported _ = return () diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Deriving.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Deriving.hs deleted file mode 100644 index 067241a7..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Deriving.hs +++ /dev/null @@ -1,795 +0,0 @@ -{- HLINT ignore "Unused LANGUAGE pragma" -} -- HLint doesn't recognize that TypeApplications is used in a pattern -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeAbstractions #-} -module Language.PureScript.TypeChecker.Deriving (deriveInstance) where - -import Protolude hiding (Type) - -import Control.Lens (both, over) -import Control.Monad.Error.Class (liftEither) -import Control.Monad.Trans.Writer (Writer, WriterT, runWriter, runWriterT) -import Control.Monad.Writer.Class (MonadWriter(..)) -import Data.Align (align, unalign) -import Data.Foldable (foldl1, foldr1) -import Data.List (init, last, zipWith3, (!!)) -import Data.Map qualified as M -import Data.These (These(..), mergeTheseWith, these) - -import Language.PureScript.AST (Binder(..), CaseAlternative(..), ErrorMessageHint(..), Expr(..), InstanceDerivationStrategy(..), Literal(..), SourceSpan, nullSourceSpan) -import Language.PureScript.AST.Utils (UnwrappedTypeConstructor(..), lam, lamCase, lamCase2, mkBinder, mkCtor, mkCtorBinder, mkLit, mkRef, mkVar, unguarded, unwrapTypeConstructor, utcQTyCon) -import Language.PureScript.Constants.Libs qualified as Libs -import Language.PureScript.Constants.Prim qualified as Prim -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType(..), Environment(..), FunctionalDependency(..), TypeClassData(..), TypeKind(..), kindType, (-:>)) -import Language.PureScript.Errors (SimpleErrorMessage(..), addHint, errorMessage, internalCompilerError) -import Language.PureScript.Label (Label(..)) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName(..), Name(..), ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, freshIdent, qualify) -import Language.PureScript.PSString (PSString, mkString) -import Language.PureScript.Sugar.TypeClasses (superClassDictionaryNames) -import Language.PureScript.TypeChecker.Entailment (InstanceContext, findDicts) -import Language.PureScript.TypeChecker.Monad (getEnv, getTypeClassDictionaries, unsafeCheckCurrentModule, TypeCheckM) -import Language.PureScript.TypeChecker.Synonyms (replaceAllTypeSynonyms) -import Language.PureScript.TypeClassDictionaries (TypeClassDictionaryInScope(..)) -import Language.PureScript.Types (Constraint(..), pattern REmptyKinded, SourceType, Type(..), completeBinderList, eqType, everythingOnTypes, replaceAllTypeVars, srcTypeVar, usedTypeVariables) - --- | Extract the name of the newtype appearing in the last type argument of --- a derived newtype instance. --- --- Note: since newtypes in newtype instances can only be applied to type arguments --- (no flexible instances allowed), we don't need to bother with unification when --- looking for matching superclass instances, which saves us a lot of work. Instead, --- we just match the newtype name. -extractNewtypeName :: ModuleName -> [SourceType] -> Maybe (ModuleName, ProperName 'TypeName) -extractNewtypeName mn - = fmap (qualify mn . utcQTyCon) - . (unwrapTypeConstructor <=< lastMay) - -deriveInstance - :: SourceType - -> Qualified (ProperName 'ClassName) - -> InstanceDerivationStrategy - -> TypeCheckM Expr -deriveInstance instType className strategy = do - mn <- unsafeCheckCurrentModule - env <- getEnv - instUtc@UnwrappedTypeConstructor{ utcArgs = tys } <- maybe (internalCompilerError "invalid instance type") pure $ unwrapTypeConstructor instType - let ctorName = coerceProperName <$> utcQTyCon instUtc - - TypeClassData{..} <- - note (errorMessage . UnknownName $ fmap TyClassName className) $ - className `M.lookup` typeClasses env - - case strategy of - KnownClassStrategy -> let - unaryClass :: (UnwrappedTypeConstructor -> TypeCheckM [(PSString, Expr)]) -> TypeCheckM Expr - unaryClass f = case tys of - [ty] -> case unwrapTypeConstructor ty of - Just utc | mn == utcModuleName utc -> do - let superclassesDicts = flip map typeClassSuperclasses $ \(Constraint _ superclass _ suTyArgs _) -> - let tyArgs = map (replaceAllTypeVars (zip (map fst typeClassArguments) tys)) suTyArgs - in lam UnusedIdent (DeferredDictionary superclass tyArgs) - let superclasses = map mkString (superClassDictionaryNames typeClassSuperclasses) `zip` superclassesDicts - App (Constructor nullSourceSpan ctorName) . mkLit . ObjectLiteral . (++ superclasses) <$> f utc - _ -> throwError . errorMessage $ ExpectedTypeConstructor className tys ty - _ -> throwError . errorMessage $ InvalidDerivedInstance className tys 1 - - unaryClass' f = unaryClass (f className) - - in case className of - Libs.Bifoldable -> unaryClass' $ deriveFoldable True - Libs.Bifunctor -> unaryClass' $ deriveFunctor (Just False) False Libs.S_bimap - Libs.Bitraversable -> unaryClass' $ deriveTraversable True - Libs.Contravariant -> unaryClass' $ deriveFunctor Nothing True Libs.S_cmap - Libs.Eq -> unaryClass deriveEq - Libs.Eq1 -> unaryClass $ const deriveEq1 - Libs.Foldable -> unaryClass' $ deriveFoldable False - Libs.Functor -> unaryClass' $ deriveFunctor Nothing False Libs.S_map - Libs.Ord -> unaryClass deriveOrd - Libs.Ord1 -> unaryClass $ const deriveOrd1 - Libs.Profunctor -> unaryClass' $ deriveFunctor (Just True) False Libs.S_dimap - Libs.Traversable -> unaryClass' $ deriveTraversable False - -- See L.P.Sugar.TypeClasses.Deriving for the classes that can be - -- derived prior to type checking. - _ -> throwError . errorMessage $ CannotDerive className tys - - NewtypeStrategy -> - case tys of - _ : _ | Just utc <- unwrapTypeConstructor (last tys) - , mn == utcModuleName utc - -> deriveNewtypeInstance className tys utc - | otherwise -> throwError . errorMessage $ ExpectedTypeConstructor className tys (last tys) - _ -> throwError . errorMessage $ InvalidNewtypeInstance className tys - -deriveNewtypeInstance - :: Qualified (ProperName 'ClassName) - -> [SourceType] - -> UnwrappedTypeConstructor - -> TypeCheckM Expr -deriveNewtypeInstance className tys (UnwrappedTypeConstructor mn tyConNm dkargs dargs) = do - verifySuperclasses - (dtype, tyKindNames, tyArgNames, ctors) <- lookupTypeDecl mn tyConNm - go dtype tyKindNames tyArgNames ctors - where - go (Just Newtype) tyKindNames tyArgNames [(_, [wrapped])] = do - -- The newtype might not be applied to all type arguments. - -- This is okay as long as the newtype wraps something which ends with - -- sufficiently many type applications to variables. - -- For example, we can derive Functor for - -- - -- newtype MyArray a = MyArray (Array a) - -- - -- since Array a is a type application which uses the last - -- type argument - wrapped' <- replaceAllTypeSynonyms wrapped - case stripRight (takeReverse (length tyArgNames - length dargs) tyArgNames) wrapped' of - Just wrapped'' -> do - let subst = zipWith (\(name, _) t -> (name, t)) tyArgNames dargs <> zip tyKindNames dkargs - wrapped''' <- replaceAllTypeSynonyms $ replaceAllTypeVars subst wrapped'' - tys' <- mapM replaceAllTypeSynonyms tys - return (DeferredDictionary className (init tys' ++ [wrapped'''])) - Nothing -> throwError . errorMessage $ InvalidNewtypeInstance className tys - go _ _ _ _ = throwError . errorMessage $ InvalidNewtypeInstance className tys - - takeReverse :: Int -> [a] -> [a] - takeReverse n = take n . reverse - - stripRight :: [(Text, Maybe kind)] -> SourceType -> Maybe SourceType - stripRight [] ty = Just ty - stripRight ((arg, _) : args) (TypeApp _ t (TypeVar _ arg')) - | arg == arg' = stripRight args t - stripRight _ _ = Nothing - - verifySuperclasses :: TypeCheckM () - verifySuperclasses = do - env <- getEnv - for_ (M.lookup className (typeClasses env)) $ \TypeClassData{ typeClassArguments = args, typeClassSuperclasses = superclasses } -> - for_ superclasses $ \Constraint{..} -> do - let constraintClass' = qualify (internalError "verifySuperclasses: unknown class module") constraintClass - for_ (M.lookup constraintClass (typeClasses env)) $ \TypeClassData{ typeClassDependencies = deps } -> - -- We need to check whether the newtype is mentioned, because of classes like MonadWriter - -- with its Monoid superclass constraint. - when (not (null args) && any ((fst (last args) `elem`) . usedTypeVariables) constraintArgs) $ do - -- For now, we only verify superclasses where the newtype is the only argument, - -- or for which all other arguments are determined by functional dependencies. - -- Everything else raises a UnverifiableSuperclassInstance warning. - -- This covers pretty much all cases we're interested in, but later we might want to do - -- more work to extend this to other superclass relationships. - let determined = map (srcTypeVar . fst . (args !!)) . ordNub . concatMap fdDetermined . filter ((== [length args - 1]) . fdDeterminers) $ deps - if eqType (last constraintArgs) (srcTypeVar . fst $ last args) && all (`elem` determined) (init constraintArgs) - then do - -- Now make sure that a superclass instance was derived. Again, this is not a complete - -- check, since the superclass might have multiple type arguments, so overlaps might still - -- be possible, so we warn again. - for_ (extractNewtypeName mn tys) $ \nm -> do - unless (hasNewtypeSuperclassInstance constraintClass' nm (typeClassDictionaries env)) $ - tell . errorMessage $ MissingNewtypeSuperclassInstance constraintClass className tys - else tell . errorMessage $ UnverifiableSuperclassInstance constraintClass className tys - - -- Note that this check doesn't actually verify that the superclass is - -- newtype-derived; see #3168. The whole verifySuperclasses feature - -- is pretty sketchy, and could use a thorough review and probably rewrite. - hasNewtypeSuperclassInstance (suModule, suClass) nt@(newtypeModule, _) dicts = - let su = Qualified (ByModuleName suModule) suClass - lookIn mn' - = elem nt - . (toList . extractNewtypeName mn' . tcdInstanceTypes - <=< foldMap toList . M.elems - <=< toList . (M.lookup su <=< M.lookup (ByModuleName mn'))) - $ dicts - in lookIn suModule || lookIn newtypeModule - -data TypeInfo = TypeInfo - { tiTypeParams :: [Text] - , tiCtors :: [(ProperName 'ConstructorName, [SourceType])] - , tiArgSubst :: [(Text, SourceType)] - } - -lookupTypeInfo - :: UnwrappedTypeConstructor - -> TypeCheckM TypeInfo -lookupTypeInfo UnwrappedTypeConstructor{..} = do - (_, kindParams, map fst -> tiTypeParams, tiCtors) <- lookupTypeDecl utcModuleName utcTyCon - let tiArgSubst = zip tiTypeParams utcArgs <> zip kindParams utcKindArgs - pure TypeInfo{..} - -deriveEq - :: UnwrappedTypeConstructor - -> TypeCheckM [(PSString, Expr)] -deriveEq utc = do - TypeInfo{..} <- lookupTypeInfo utc - eqFun <- mkEqFunction tiCtors - pure [(Libs.S_eq, eqFun)] - where - mkEqFunction :: [(ProperName 'ConstructorName, [SourceType])] -> TypeCheckM Expr - mkEqFunction ctors = do - x <- freshIdent "x" - y <- freshIdent "y" - lamCase2 x y . addCatch <$> mapM mkCtorClause ctors - - preludeConj :: Expr -> Expr -> Expr - preludeConj = App . App (mkRef Libs.I_conj) - - preludeEq :: Expr -> Expr -> Expr - preludeEq = App . App (mkRef Libs.I_eq) - - preludeEq1 :: Expr -> Expr -> Expr - preludeEq1 = App . App (mkRef Libs.I_eq1) - - addCatch :: [CaseAlternative] -> [CaseAlternative] - addCatch xs - | length xs /= 1 = xs ++ [catchAll] - | otherwise = xs -- Avoid redundant case - where - catchAll = CaseAlternative [NullBinder, NullBinder] (unguarded (mkLit (BooleanLiteral False))) - - mkCtorClause :: (ProperName 'ConstructorName, [SourceType]) -> TypeCheckM CaseAlternative - mkCtorClause (ctorName, tys) = do - identsL <- replicateM (length tys) (freshIdent "l") - identsR <- replicateM (length tys) (freshIdent "r") - tys' <- mapM replaceAllTypeSynonyms tys - let tests = zipWith3 toEqTest (map mkVar identsL) (map mkVar identsR) tys' - return $ CaseAlternative [caseBinder identsL, caseBinder identsR] (unguarded (conjAll tests)) - where - caseBinder idents = mkCtorBinder (utcModuleName utc) ctorName $ map mkBinder idents - - conjAll :: [Expr] -> Expr - conjAll = \case - [] -> mkLit (BooleanLiteral True) - xs -> foldl1 preludeConj xs - - toEqTest :: Expr -> Expr -> SourceType -> Expr - toEqTest l r ty - | Just fields <- decomposeRec <=< objectType $ ty - = conjAll - . map (\(Label str, typ) -> toEqTest (Accessor str l) (Accessor str r) typ) - $ fields - | isAppliedVar ty = preludeEq1 l r - | otherwise = preludeEq l r - -deriveEq1 :: forall m. Applicative m => m [(PSString, Expr)] -deriveEq1 = pure [(Libs.S_eq1, mkRef Libs.I_eq)] - -deriveOrd - :: UnwrappedTypeConstructor - -> TypeCheckM [(PSString, Expr)] -deriveOrd utc = do - TypeInfo{..} <- lookupTypeInfo utc - compareFun <- mkCompareFunction tiCtors - pure [(Libs.S_compare, compareFun)] - where - mkCompareFunction :: [(ProperName 'ConstructorName, [SourceType])] -> TypeCheckM Expr - mkCompareFunction ctors = do - x <- freshIdent "x" - y <- freshIdent "y" - lamCase2 x y <$> (addCatch . concat <$> mapM mkCtorClauses (splitLast ctors)) - - splitLast :: [a] -> [(a, Bool)] - splitLast [] = [] - splitLast [x] = [(x, True)] - splitLast (x : xs) = (x, False) : splitLast xs - - addCatch :: [CaseAlternative] -> [CaseAlternative] - addCatch xs - | null xs = [catchAll] -- No type constructors - | otherwise = xs - where - catchAll = CaseAlternative [NullBinder, NullBinder] (unguarded (orderingCtor "EQ")) - - orderingMod :: ModuleName - orderingMod = ModuleName "Data.Ordering" - - orderingCtor :: Text -> Expr - orderingCtor = mkCtor orderingMod . ProperName - - orderingBinder :: Text -> Binder - orderingBinder name = mkCtorBinder orderingMod (ProperName name) [] - - ordCompare :: Expr -> Expr -> Expr - ordCompare = App . App (mkRef Libs.I_compare) - - ordCompare1 :: Expr -> Expr -> Expr - ordCompare1 = App . App (mkRef Libs.I_compare1) - - mkCtorClauses :: ((ProperName 'ConstructorName, [SourceType]), Bool) -> TypeCheckM [CaseAlternative] - mkCtorClauses ((ctorName, tys), isLast) = do - identsL <- replicateM (length tys) (freshIdent "l") - identsR <- replicateM (length tys) (freshIdent "r") - tys' <- mapM replaceAllTypeSynonyms tys - let tests = zipWith3 toOrdering (map mkVar identsL) (map mkVar identsR) tys' - extras | not isLast = [ CaseAlternative [nullCaseBinder, NullBinder] (unguarded (orderingCtor "LT")) - , CaseAlternative [NullBinder, nullCaseBinder] (unguarded (orderingCtor "GT")) - ] - | otherwise = [] - return $ CaseAlternative [ caseBinder identsL - , caseBinder identsR - ] - (unguarded (appendAll tests)) - : extras - - where - mn = utcModuleName utc - caseBinder idents = mkCtorBinder mn ctorName $ map mkBinder idents - nullCaseBinder = mkCtorBinder mn ctorName $ replicate (length tys) NullBinder - - appendAll :: [Expr] -> Expr - appendAll = \case - [] -> orderingCtor "EQ" - [x] -> x - (x : xs) -> Case [x] [ CaseAlternative [orderingBinder "LT"] (unguarded (orderingCtor "LT")) - , CaseAlternative [orderingBinder "GT"] (unguarded (orderingCtor "GT")) - , CaseAlternative [NullBinder] (unguarded (appendAll xs)) - ] - - toOrdering :: Expr -> Expr -> SourceType -> Expr - toOrdering l r ty - | Just fields <- decomposeRec <=< objectType $ ty - = appendAll - . map (\(Label str, typ) -> toOrdering (Accessor str l) (Accessor str r) typ) - $ fields - | isAppliedVar ty = ordCompare1 l r - | otherwise = ordCompare l r - -deriveOrd1 :: forall m. Applicative m => m [(PSString, Expr)] -deriveOrd1 = pure [(Libs.S_compare1, mkRef Libs.I_compare)] - -lookupTypeDecl - :: ModuleName - -> ProperName 'TypeName - -> TypeCheckM (Maybe DataDeclType, [Text], [(Text, Maybe SourceType)], [(ProperName 'ConstructorName, [SourceType])]) -lookupTypeDecl mn typeName = do - env <- getEnv - note (errorMessage $ CannotFindDerivingType typeName) $ do - (kind, DataType _ args dctors) <- Qualified (ByModuleName mn) typeName `M.lookup` types env - (kargs, _) <- completeBinderList kind - let dtype = do - (ctorName, _) <- headMay dctors - (a, _, _, _) <- Qualified (ByModuleName mn) ctorName `M.lookup` dataConstructors env - pure a - pure (dtype, fst . snd <$> kargs, map (\(v, k, _) -> (v, k)) args, dctors) - -isAppliedVar :: Type a -> Bool -isAppliedVar (TypeApp _ (TypeVar _ _) _) = True -isAppliedVar _ = False - -objectType :: Type a -> Maybe (Type a) -objectType (TypeApp _ (TypeConstructor _ Prim.Record) rec) = Just rec -objectType _ = Nothing - -decomposeRec :: SourceType -> Maybe [(Label, SourceType)] -decomposeRec = fmap (sortOn fst) . go - where go (RCons _ str typ typs) = fmap ((str, typ) :) (go typs) - go (REmptyKinded _ _) = Just [] - go _ = Nothing - -decomposeRec' :: SourceType -> [(Label, SourceType)] -decomposeRec' = sortOn fst . go - where go (RCons _ str typ typs) = (str, typ) : go typs - go _ = [] - --- | The parameter `c` is used to allow or forbid contravariance for different --- type classes. When deriving a type class that is a variation on Functor, a --- witness for `c` will be provided; when deriving a type class that is a --- variation on Foldable or Traversable, `c` will be Void and the contravariant --- ParamUsage constructor can be skipped in pattern matching. -data ParamUsage c - = IsParam - | IsLParam - -- ^ enables biparametric classes (of any variance) to be derived - | MentionsParam (ParamUsage c) - -- ^ enables monoparametric classes to be used in a derivation - | MentionsParamBi (These (ParamUsage c) (ParamUsage c)) - -- ^ enables biparametric classes to be used in a derivation - | MentionsParamContravariantly !c (ContravariantParamUsage c) - -- ^ enables contravariant classes (of either parametricity) to be used in a derivation - | IsRecord (NonEmpty (PSString, ParamUsage c)) - -data ContravariantParamUsage c - = MentionsParamContra (ParamUsage c) - -- ^ enables Contravariant to be used in a derivation - | MentionsParamPro (These (ParamUsage c) (ParamUsage c)) - -- ^ enables Profunctor to be used in a derivation - -data CovariantClasses = CovariantClasses - { monoClass :: Qualified (ProperName 'ClassName) - , biClass :: Qualified (ProperName 'ClassName) - } - -data ContravariantClasses = ContravariantClasses - { contraClass :: Qualified (ProperName 'ClassName) - , proClass :: Qualified (ProperName 'ClassName) - } - -data ContravarianceSupport c = ContravarianceSupport - { contravarianceWitness :: c - , paramIsContravariant :: Bool - , lparamIsContravariant :: Bool - , contravariantClasses :: ContravariantClasses - } - --- | Return, if possible, a These the contents of which each satisfy the --- predicate. -filterThese :: forall a. (a -> Bool) -> These a a -> Maybe (These a a) -filterThese p = uncurry align . over both (mfilter p) . unalign . Just - -validateParamsInTypeConstructors - :: forall c - . Qualified (ProperName 'ClassName) - -> UnwrappedTypeConstructor - -> Bool - -> CovariantClasses - -> Maybe (ContravarianceSupport c) - -> TypeCheckM [(ProperName 'ConstructorName, [Maybe (ParamUsage c)])] -validateParamsInTypeConstructors derivingClass utc isBi CovariantClasses{..} contravarianceSupport = do - TypeInfo{..} <- lookupTypeInfo utc - (mbLParam, param) <- liftEither . first (errorMessage . flip KindsDoNotUnify kindType . (kindType -:>)) $ - case (isBi, reverse tiTypeParams) of - (False, x : _) -> Right (Nothing, x) - (False, _) -> Left kindType - (True, y : x : _) -> Right (Just x, y) - (True, _ : _) -> Left kindType - (True, _) -> Left $ kindType -:> kindType - ctors <- traverse (traverse $ traverse replaceAllTypeSynonyms) tiCtors - tcds <- getTypeClassDictionaries - let (ctorUsages, problemSpans) = runWriter $ traverse (traverse . traverse $ typeToUsageOf tcds tiArgSubst (maybe That These mbLParam param) False) ctors - let relatedClasses = [monoClass, biClass] ++ ([contraClass, proClass] <*> (contravariantClasses <$> toList contravarianceSupport)) - for_ (nonEmpty $ ordNub problemSpans) $ \sss -> - throwError . addHint (RelatedPositions sss) . errorMessage $ CannotDeriveInvalidConstructorArg derivingClass relatedClasses (isJust contravarianceSupport) - pure ctorUsages - - where - typeToUsageOf :: InstanceContext -> [(Text, SourceType)] -> These Text Text -> Bool -> SourceType -> Writer [SourceSpan] (Maybe (ParamUsage c)) - typeToUsageOf tcds subst = fix $ \go params isNegative -> let - goCo = go params isNegative - goContra = go params $ not isNegative - - assertNoParamUsedIn :: SourceType -> Writer [SourceSpan] () - assertNoParamUsedIn ty = void $ both (flip assertParamNotUsedIn ty) params - - assertParamNotUsedIn :: Text -> SourceType -> Writer [SourceSpan] () - assertParamNotUsedIn param = everythingOnTypes (*>) $ \case - TypeVar (ss, _) name | name == param -> tell [ss] - _ -> pure () - - tryBiClasses ht tyLArg tyArg - | hasInstance tcds ht biClass - = goCo tyLArg >>= preferMonoClass MentionsParamBi - | Just (ContravarianceSupport c _ _ ContravariantClasses{..}) <- contravarianceSupport, hasInstance tcds ht proClass - = goContra tyLArg >>= preferMonoClass (MentionsParamContravariantly c . MentionsParamPro) - | otherwise - = assertNoParamUsedIn tyLArg *> tryMonoClasses ht tyArg - where - preferMonoClass f lUsage = - (if isNothing lUsage && hasInstance tcds ht monoClass then fmap MentionsParam else fmap f . align lUsage) <$> goCo tyArg - - tryMonoClasses ht tyArg - | hasInstance tcds ht monoClass - = fmap MentionsParam <$> goCo tyArg - | Just (ContravarianceSupport c _ _ ContravariantClasses{..}) <- contravarianceSupport, hasInstance tcds ht contraClass - = fmap (MentionsParamContravariantly c . MentionsParamContra) <$> goContra tyArg - | otherwise - = assertNoParamUsedIn tyArg $> Nothing - - headOfTypeWithSubst :: SourceType -> Qualified (Either Text (ProperName 'TypeName)) - headOfTypeWithSubst = headOfType . replaceAllTypeVars subst - - in \case - ForAll _ _ name _ ty _ -> - fmap join . traverse (\params' -> go params' isNegative ty) $ filterThese (/= name) params - - ConstrainedType _ _ ty -> - goCo ty - - TypeApp _ (TypeConstructor _ Prim.Record) row -> - fmap (fmap IsRecord . nonEmpty . catMaybes) . for (decomposeRec' row) $ \(Label lbl, ty) -> - fmap (lbl, ) <$> goCo ty - - TypeApp _ (TypeApp _ tyFn tyLArg) tyArg -> - assertNoParamUsedIn tyFn *> tryBiClasses (headOfTypeWithSubst tyFn) tyLArg tyArg - - TypeApp _ tyFn tyArg -> - assertNoParamUsedIn tyFn *> tryMonoClasses (headOfTypeWithSubst tyFn) tyArg - - TypeVar (ss, _) name -> mergeTheseWith (checkName lparamIsContra IsLParam) (checkName paramIsContra IsParam) (liftA2 (<|>)) params - where - checkName thisParamIsContra usage param - | name == param = when (thisParamIsContra /= isNegative) (tell [ss]) $> Just usage - | otherwise = pure Nothing - - ty -> - assertNoParamUsedIn ty $> Nothing - - paramIsContra = any paramIsContravariant contravarianceSupport - lparamIsContra = any lparamIsContravariant contravarianceSupport - - hasInstance :: InstanceContext -> Qualified (Either Text (ProperName 'TypeName)) -> Qualified (ProperName 'ClassName) -> Bool - hasInstance tcds ht@(Qualified qb _) cn@(Qualified cqb _) = - any (any tcdAppliesToType . findDicts tcds cn) (ordNub [ByNullSourcePos, cqb, qb]) - where - tcdAppliesToType tcd = case tcdInstanceTypes tcd of - [headOfType -> ht'] -> ht == ht' - -- It's possible that, if ht and ht' are Lefts, this might require - -- verifying that the name isn't shadowed by something in tcdForAll. I - -- can't devise a legal program that causes this issue, but if in the - -- future it seems like a good idea, it probably is. - _ -> False - - headOfType :: SourceType -> Qualified (Either Text (ProperName 'TypeName)) - headOfType = fix $ \go -> \case - TypeApp _ ty _ -> go ty - KindApp _ ty _ -> go ty - TypeVar _ nm -> Qualified ByNullSourcePos (Left nm) - Skolem _ nm _ _ _ -> Qualified ByNullSourcePos (Left nm) - TypeConstructor _ (Qualified qb nm) -> Qualified qb (Right nm) - ty -> internalError $ "headOfType missing a case: " <> show (void ty) - -usingLamIdent :: (Expr -> TypeCheckM Expr) -> TypeCheckM Expr -usingLamIdent cb = do - ident <- freshIdent "v" - lam ident <$> cb (mkVar ident) - -traverseFields :: forall c f. Applicative f => (ParamUsage c -> Expr -> f Expr) -> NonEmpty (PSString, ParamUsage c) -> Expr -> f Expr -traverseFields f fields r = fmap (ObjectUpdate r) . for (toList fields) $ \(lbl, usage) -> (lbl, ) <$> f usage (Accessor lbl r) - -unnestRecords :: forall c f. Applicative f => (ParamUsage c -> Expr -> f Expr) -> ParamUsage c -> Expr -> f Expr -unnestRecords f = fix $ \go -> \case - IsRecord fields -> traverseFields go fields - usage -> f usage - -mkCasesForTraversal - :: Applicative f - => ModuleName - -> (ParamUsage c -> Expr -> f Expr) -- how to handle constructor arguments - -> (f Expr -> TypeCheckM Expr) -- resolve the applicative effect into an expression - -> [(ProperName 'ConstructorName, [Maybe (ParamUsage c)])] - -> TypeCheckM Expr -mkCasesForTraversal mn handleArg extractExpr ctors = do - m <- freshIdent "m" - fmap (lamCase m) . for ctors $ \(ctorName, ctorUsages) -> do - ctorArgs <- for ctorUsages $ \usage -> freshIdent "v" <&> (, usage) - let ctor = mkCtor mn ctorName - let caseBinder = mkCtorBinder mn ctorName $ map (mkBinder . fst) ctorArgs - fmap (CaseAlternative [caseBinder] . unguarded) . extractExpr $ - fmap (foldl' App ctor) . for ctorArgs $ \(ident, mbUsage) -> maybe pure handleArg mbUsage $ mkVar ident - -data TraversalExprs = TraversalExprs - { recurseVar :: Expr -- a var representing map, foldMap, or traverse, for handling structured values - , birecurseVar :: Expr -- same, but bimap, bifoldMap, or bitraverse - , lrecurseExpr :: Expr -- same, but lmap or ltraverse (there is no lfoldMap, but we can use `flip bifoldMap mempty`) - , rrecurseExpr :: Expr -- same, but rmap or rtraverse etc., which conceptually should be the same as recurseVar but the bi classes aren't subclasses of the mono classes - } - -data ContraversalExprs = ContraversalExprs - { crecurseVar :: Expr - , direcurseVar :: Expr - , lcrecurseVar :: Expr - , rprorecurseVar :: Expr - } - -appBirecurseExprs :: TraversalExprs -> These Expr Expr -> Expr -appBirecurseExprs TraversalExprs{..} = these (App lrecurseExpr) (App rrecurseExpr) (App . App birecurseVar) - -appDirecurseExprs :: ContraversalExprs -> These Expr Expr -> Expr -appDirecurseExprs ContraversalExprs{..} = these (App lcrecurseVar) (App rprorecurseVar) (App . App direcurseVar) - -data TraversalOps m = forall f. Applicative f => TraversalOps - { visitExpr :: m Expr -> f Expr -- lift an expression into the applicative effect defining the traversal - , extractExpr :: f Expr -> m Expr -- resolve the applicative effect into an expression - } - -mkTraversal - :: forall c. ModuleName - -> Bool - -> TraversalExprs - -> (c -> ContraversalExprs) - -> TraversalOps TypeCheckM - -> [(ProperName 'ConstructorName, [Maybe (ParamUsage c)])] - -> TypeCheckM Expr -mkTraversal mn isBi te@TraversalExprs{..} getContraversalExprs (TraversalOps @_ @f visitExpr extractExpr) ctors = do - f <- freshIdent "f" - g <- if isBi then freshIdent "g" else pure f - let - handleValue :: ParamUsage c -> Expr -> f Expr - handleValue = unnestRecords $ \usage inputExpr -> visitExpr $ flip App inputExpr <$> mkFnExprForValue usage - - mkFnExprForValue :: ParamUsage c -> TypeCheckM Expr - mkFnExprForValue = \case - IsParam -> - pure $ mkVar g - IsLParam -> - pure $ mkVar f - MentionsParam innerUsage -> - App recurseVar <$> mkFnExprForValue innerUsage - MentionsParamBi theseInnerUsages -> - appBirecurseExprs te <$> both mkFnExprForValue theseInnerUsages - MentionsParamContravariantly c contraUsage -> do - let ce@ContraversalExprs{..} = getContraversalExprs c - case contraUsage of - MentionsParamContra innerUsage -> - App crecurseVar <$> mkFnExprForValue innerUsage - MentionsParamPro theseInnerUsages -> - appDirecurseExprs ce <$> both mkFnExprForValue theseInnerUsages - IsRecord fields -> - usingLamIdent $ extractExpr . traverseFields handleValue fields - - lam f . applyWhen isBi (lam g) <$> mkCasesForTraversal mn handleValue extractExpr ctors - -deriveFunctor - :: Maybe Bool -- does left parameter exist, and is it contravariant? - -> Bool -- is the (right) parameter contravariant? - -> PSString -- name of the map function for this functor type - -> Qualified (ProperName 'ClassName) - -> UnwrappedTypeConstructor - -> TypeCheckM [(PSString, Expr)] -deriveFunctor mbLParamIsContravariant paramIsContravariant mapName nm utc = do - ctors <- validateParamsInTypeConstructors nm utc isBi functorClasses $ Just $ ContravarianceSupport - { contravarianceWitness = () - , paramIsContravariant - , lparamIsContravariant = or mbLParamIsContravariant - , contravariantClasses - } - mapFun <- mkTraversal (utcModuleName utc) isBi mapExprs (const cmapExprs) (TraversalOps identity identity) ctors - pure [(mapName, mapFun)] - where - isBi = isJust mbLParamIsContravariant - mapExprs = TraversalExprs - { recurseVar = mkRef Libs.I_map - , birecurseVar = mkRef Libs.I_bimap - , lrecurseExpr = mkRef Libs.I_lmap - , rrecurseExpr = mkRef Libs.I_rmap - } - cmapExprs = ContraversalExprs - { crecurseVar = mkRef Libs.I_cmap - , direcurseVar = mkRef Libs.I_dimap - , lcrecurseVar = mkRef Libs.I_lcmap - , rprorecurseVar = mkRef Libs.I_profunctorRmap - } - functorClasses = CovariantClasses Libs.Functor Libs.Bifunctor - contravariantClasses = ContravariantClasses Libs.Contravariant Libs.Profunctor - -toConst :: forall f a b. f a -> Const [f a] b -toConst = Const . pure - -consumeConst :: forall f a b c. Applicative f => ([a] -> b) -> Const [f a] c -> f b -consumeConst f = fmap f . sequenceA . getConst - -applyWhen :: forall a. Bool -> (a -> a) -> a -> a -applyWhen cond f = if cond then f else identity - -deriveFoldable - :: Bool -- is there a left parameter (are we deriving Bifoldable)? - -> Qualified (ProperName 'ClassName) - -> UnwrappedTypeConstructor - -> TypeCheckM [(PSString, Expr)] -deriveFoldable isBi nm utc = do - ctors <- validateParamsInTypeConstructors nm utc isBi foldableClasses Nothing - foldlFun <- mkAsymmetricFoldFunction False foldlExprs ctors - foldrFun <- mkAsymmetricFoldFunction True foldrExprs ctors - foldMapFun <- mkTraversal mn isBi foldMapExprs absurd foldMapOps ctors - pure - [ (if isBi then Libs.S_bifoldl else Libs.S_foldl, foldlFun) - , (if isBi then Libs.S_bifoldr else Libs.S_foldr, foldrFun) - , (if isBi then Libs.S_bifoldMap else Libs.S_foldMap, foldMapFun) - ] - where - mn = utcModuleName utc - foldableClasses = CovariantClasses Libs.Foldable Libs.Bifoldable - foldlExprs = TraversalExprs - { recurseVar = mkRef Libs.I_foldl - , birecurseVar = bifoldlVar - , lrecurseExpr = App (App flipVar bifoldlVar) constVar - , rrecurseExpr = App bifoldlVar constVar - } - foldrExprs = TraversalExprs - { recurseVar = mkRef Libs.I_foldr - , birecurseVar = bifoldrVar - , lrecurseExpr = App (App flipVar bifoldrVar) (App constVar identityVar) - , rrecurseExpr = App bifoldrVar (App constVar identityVar) - } - foldMapExprs = TraversalExprs - { recurseVar = mkRef Libs.I_foldMap - , birecurseVar = bifoldMapVar - , lrecurseExpr = App (App flipVar bifoldMapVar) memptyVar - , rrecurseExpr = App bifoldMapVar memptyVar - } - bifoldlVar = mkRef Libs.I_bifoldl - bifoldrVar = mkRef Libs.I_bifoldr - bifoldMapVar = mkRef Libs.I_bifoldMap - constVar = mkRef Libs.I_const - flipVar = mkRef Libs.I_flip - identityVar = mkRef Libs.I_identity - memptyVar = mkRef Libs.I_mempty - - mkAsymmetricFoldFunction :: Bool -> TraversalExprs -> [(ProperName 'ConstructorName, [Maybe (ParamUsage Void)])] -> TypeCheckM Expr - mkAsymmetricFoldFunction isRightFold te@TraversalExprs{..} ctors = do - f <- freshIdent "f" - g <- if isBi then freshIdent "g" else pure f - z <- freshIdent "z" - let - appCombiner :: (Bool, Expr) -> Expr -> Expr -> Expr - appCombiner (isFlipped, fn) = applyWhen (isFlipped == isRightFold) flip $ App . App fn - - mkCombinerExpr :: ParamUsage Void -> TypeCheckM Expr - mkCombinerExpr = fmap (uncurry $ \isFlipped -> applyWhen isFlipped $ App flipVar) . getCombiner - - handleValue :: ParamUsage Void -> Expr -> Const [TypeCheckM (Expr -> Expr)] Expr - handleValue = unnestRecords $ \usage inputExpr -> toConst $ flip appCombiner inputExpr <$> getCombiner usage - - getCombiner :: ParamUsage Void -> TypeCheckM (Bool, Expr) - getCombiner = \case - IsParam -> - pure (False, mkVar g) - IsLParam -> - pure (False, mkVar f) - MentionsParam innerUsage -> - (isRightFold, ) . App recurseVar <$> mkCombinerExpr innerUsage - MentionsParamBi theseInnerUsages -> - (isRightFold, ) . appBirecurseExprs te <$> both mkCombinerExpr theseInnerUsages - IsRecord fields -> do - let foldFieldsOf = traverseFields handleValue fields - fmap (False, ) . usingLamIdent $ \lVar -> - usingLamIdent $ - if isRightFold - then flip extractExprStartingWith $ foldFieldsOf lVar - else extractExprStartingWith lVar . foldFieldsOf - - extractExprStartingWith :: Expr -> Const [TypeCheckM (Expr -> Expr)] Expr -> TypeCheckM Expr - extractExprStartingWith = consumeConst . if isRightFold then foldr ($) else foldl' (&) - - lam f . applyWhen isBi (lam g) . lam z <$> mkCasesForTraversal mn handleValue (extractExprStartingWith $ mkVar z) ctors - -foldMapOps :: forall m. Applicative m => TraversalOps m -foldMapOps = TraversalOps { visitExpr = toConst, .. } - where - appendVar = mkRef Libs.I_append - memptyVar = mkRef Libs.I_mempty - - extractExpr :: Const [m Expr] Expr -> m Expr - extractExpr = consumeConst $ \case - [] -> memptyVar - exprs -> foldr1 (App . App appendVar) exprs - -deriveTraversable - :: Bool -- is there a left parameter (are we deriving Bitraversable)? - -> Qualified (ProperName 'ClassName) - -> UnwrappedTypeConstructor - -> TypeCheckM [(PSString, Expr)] -deriveTraversable isBi nm utc = do - ctors <- validateParamsInTypeConstructors nm utc isBi traversableClasses Nothing - traverseFun <- mkTraversal (utcModuleName utc) isBi traverseExprs absurd traverseOps ctors - sequenceFun <- usingLamIdent $ pure . App (App (if isBi then App bitraverseVar identityVar else traverseVar) identityVar) - pure - [ (if isBi then Libs.S_bitraverse else Libs.S_traverse, traverseFun) - , (if isBi then Libs.S_bisequence else Libs.S_sequence, sequenceFun) - ] - where - traversableClasses = CovariantClasses Libs.Traversable Libs.Bitraversable - traverseExprs = TraversalExprs - { recurseVar = traverseVar - , birecurseVar = bitraverseVar - , lrecurseExpr = mkRef Libs.I_ltraverse - , rrecurseExpr = mkRef Libs.I_rtraverse - } - traverseVar = mkRef Libs.I_traverse - bitraverseVar = mkRef Libs.I_bitraverse - identityVar = mkRef Libs.I_identity - -traverseOps :: TraversalOps TypeCheckM -traverseOps = TraversalOps { .. } - where - pureVar = mkRef Libs.I_pure - mapVar = mkRef Libs.I_map - applyVar = mkRef Libs.I_apply - - visitExpr :: TypeCheckM Expr -> WriterT [(Ident, TypeCheckM Expr)] TypeCheckM Expr - visitExpr traversedExpr = do - ident <- freshIdent "v" - tell [(ident, traversedExpr)] $> mkVar ident - - extractExpr :: WriterT [(Ident, TypeCheckM Expr)] TypeCheckM Expr -> TypeCheckM Expr - extractExpr = runWriterT >=> \(result, unzip -> (ctx, args)) -> flip mkApps (foldr lam result ctx) <$> sequenceA args - - mkApps :: [Expr] -> Expr -> Expr - mkApps = \case - [] -> App pureVar - h : t -> \l -> foldl' (App . App applyVar) (App (App mapVar l) h) t diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment.hs deleted file mode 100644 index 8898caf2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment.hs +++ /dev/null @@ -1,920 +0,0 @@ --- | --- Type class entailment --- -module Language.PureScript.TypeChecker.Entailment - ( InstanceContext - , SolverOptions(..) - , replaceTypeClassDictionaries - , newDictionaries - , entails - , findDicts - ) where - -import Prelude -import Protolude (ordNub, headMay, headDef) - -import Control.Arrow (second, (&&&)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State (MonadState(..), MonadTrans(..), StateT(..), evalStateT, execStateT, gets, modify) -import Control.Monad (foldM, guard, join, zipWithM, zipWithM_, (<=<)) -import Control.Monad.Writer (MonadWriter(..), WriterT(..)) -import Data.Monoid (Any(..)) - -import Data.Either (lefts, partitionEithers) -import Data.Foldable (for_, fold, toList) -import Data.Function (on) -import Data.Functor (($>), (<&>)) -import Data.List (delete, findIndices, minimumBy, nubBy, sortOn, tails) -import Data.Maybe (catMaybes, fromMaybe, listToMaybe, mapMaybe) -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Traversable (for) -import Data.Text (Text, stripPrefix, stripSuffix) -import Data.Text qualified as T -import Data.List.NonEmpty (NonEmpty(..)) -import Data.List.NonEmpty qualified as NEL - -import Language.PureScript.AST (Binder(..), ErrorMessageHint(..), Expr(..), Literal(..), pattern NullSourceSpan, everywhereOnValuesTopDownM, nullSourceSpan, everythingOnValues) -import Language.PureScript.AST.Declarations (UnknownsHint(..)) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (Environment(..), FunctionalDependency(..), TypeClassData(..), dictTypeName, kindRow, tyBoolean, tyInt, tyString) -import Language.PureScript.Errors (SimpleErrorMessage(..), addHint, addHints, errorMessage, rethrow) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), byMaybeModuleName, coerceProperName, disqualify, freshIdent, getQual) -import Language.PureScript.TypeChecker.Entailment.Coercible (GivenSolverState(..), WantedSolverState(..), initialGivenSolverState, initialWantedSolverState, insoluble, solveGivens, solveWanteds) -import Language.PureScript.TypeChecker.Entailment.IntCompare (mkFacts, mkRelation, solveRelation) -import Language.PureScript.TypeChecker.Kinds (elaborateKind, unifyKinds') -import Language.PureScript.TypeChecker.Monad (CheckState(..), withErrorMessageHint, TypeCheckM) -import Language.PureScript.TypeChecker.Synonyms (replaceAllTypeSynonyms) -import Language.PureScript.TypeChecker.Unify (freshTypeWithKind, substituteType, unifyTypes) -import Language.PureScript.TypeClassDictionaries (NamedDict, TypeClassDictionaryInScope(..), superclassName) -import Language.PureScript.Types -import Language.PureScript.Label (Label(..)) -import Language.PureScript.PSString (PSString, mkString, decodeString) -import Language.PureScript.Constants.Libs qualified as C -import Language.PureScript.Constants.Prim qualified as C - --- | Describes what sort of dictionary to generate for type class instances -data Evidence - -- | An existing named instance - = NamedInstance (Qualified Ident) - - -- | Computed instances - | WarnInstance SourceType -- ^ Warn type class with a user-defined warning message - | IsSymbolInstance PSString -- ^ The IsSymbol type class for a given Symbol literal - | ReflectableInstance Reflectable -- ^ The Reflectable type class for a reflectable kind - | EmptyClassInstance -- ^ For any solved type class with no members - deriving (Show, Eq) - --- | Describes kinds that are reflectable to the term-level -data Reflectable - = ReflectableInt Integer -- ^ For type-level numbers - | ReflectableString PSString -- ^ For type-level strings - | ReflectableBoolean Bool -- ^ For type-level booleans - | ReflectableOrdering Ordering -- ^ For type-level orderings - deriving (Show, Eq) - --- | Reflect a reflectable type into an expression -asExpression :: Reflectable -> Expr -asExpression = \case - ReflectableInt n -> Literal NullSourceSpan $ NumericLiteral $ Left n - ReflectableString s -> Literal NullSourceSpan $ StringLiteral s - ReflectableBoolean b -> Literal NullSourceSpan $ BooleanLiteral b - ReflectableOrdering o -> Constructor NullSourceSpan $ case o of - LT -> C.C_LT - EQ -> C.C_EQ - GT -> C.C_GT - --- | Extract the identifier of a named instance -namedInstanceIdentifier :: Evidence -> Maybe (Qualified Ident) -namedInstanceIdentifier (NamedInstance i) = Just i -namedInstanceIdentifier _ = Nothing - --- | Description of a type class dictionary with instance evidence -type TypeClassDict = TypeClassDictionaryInScope Evidence - --- | The 'InstanceContext' tracks those constraints which can be satisfied. -type InstanceContext = M.Map QualifiedBy - (M.Map (Qualified (ProperName 'ClassName)) - (M.Map (Qualified Ident) (NonEmpty NamedDict))) - -findDicts :: InstanceContext -> Qualified (ProperName 'ClassName) -> QualifiedBy -> [TypeClassDict] -findDicts ctx cn = fmap (fmap NamedInstance) . foldMap NEL.toList . foldMap M.elems . (M.lookup cn <=< flip M.lookup ctx) - --- | A type substitution which makes an instance head match a list of types. --- --- Note: we store many types per type variable name. For any name, all types --- should unify if we are going to commit to an instance. -type Matching a = M.Map Text a - -combineContexts :: InstanceContext -> InstanceContext -> InstanceContext -combineContexts = M.unionWith (M.unionWith (M.unionWith (<>))) - --- | Replace type class dictionary placeholders with inferred type class dictionaries -replaceTypeClassDictionaries - :: - Bool - -> Expr - -> TypeCheckM (Expr, [(Ident, InstanceContext, SourceConstraint)]) -replaceTypeClassDictionaries shouldGeneralize expr = flip evalStateT M.empty $ do - -- Loop, deferring any unsolved constraints, until there are no more - -- constraints which can be solved, then make a generalization pass. - let loop e = do - (e', solved) <- deferPass e - if getAny solved - then loop e' - else return e' - loop expr >>= generalizePass - where - -- This pass solves constraints where possible, deferring constraints if not. - deferPass :: Expr -> StateT InstanceContext TypeCheckM (Expr, Any) - deferPass = fmap (second fst) . runWriterT . f where - f :: Expr -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) Expr - (_, f, _) = everywhereOnValuesTopDownM return (go True) return - - -- This pass generalizes any remaining constraints - generalizePass :: Expr -> StateT InstanceContext TypeCheckM (Expr, [(Ident, InstanceContext, SourceConstraint)]) - generalizePass = fmap (second snd) . runWriterT . f where - f :: Expr -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) Expr - (_, f, _) = everywhereOnValuesTopDownM return (go False) return - - go :: Bool -> Expr -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) Expr - go deferErrors (TypeClassDictionary constraint context hints) = - rethrow (addHints hints) $ entails (SolverOptions shouldGeneralize deferErrors) constraint context hints - go _ other = return other - --- | Three options for how we can handle a constraint, depending on the mode we're in. -data EntailsResult a - = Solved a TypeClassDict - -- ^ We solved this constraint - | Unsolved SourceConstraint - -- ^ We couldn't solve this constraint right now, it will be generalized - | Deferred - -- ^ We couldn't solve this constraint right now, so it has been deferred - deriving Show - --- | Options for the constraint solver -data SolverOptions = SolverOptions - { solverShouldGeneralize :: Bool - -- ^ Should the solver be allowed to generalize over unsolved constraints? - , solverDeferErrors :: Bool - -- ^ Should the solver be allowed to defer errors by skipping constraints? - } - -data Matched t - = Match t - | Apart - | Unknown - deriving (Eq, Show, Functor) - -instance Semigroup t => Semigroup (Matched t) where - (Match l) <> (Match r) = Match (l <> r) - Apart <> _ = Apart - _ <> Apart = Apart - _ <> _ = Unknown - -instance Monoid t => Monoid (Matched t) where - mempty = Match mempty - --- | Check that the current set of type class dictionaries entail the specified type class goal, and, if so, --- return a type class dictionary reference. -entails - :: - SolverOptions - -- ^ Solver options - -> SourceConstraint - -- ^ The constraint to solve - -> InstanceContext - -- ^ The contexts in which to solve the constraint - -> [ErrorMessageHint] - -- ^ Error message hints to apply to any instance errors - -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) Expr -entails SolverOptions{..} constraint context hints = - overConstraintArgsAll (lift . lift . traverse replaceAllTypeSynonyms) constraint >>= solve - where - forClassNameM :: Environment -> InstanceContext -> Qualified (ProperName 'ClassName) -> [SourceType] -> [SourceType] -> TypeCheckM [TypeClassDict] - forClassNameM env ctx cn@C.Coercible kinds args = - fromMaybe (forClassName env ctx cn kinds args) <$> - solveCoercible env ctx kinds args - forClassNameM env ctx cn kinds args = - pure $ forClassName env ctx cn kinds args - - forClassName :: Environment -> InstanceContext -> Qualified (ProperName 'ClassName) -> [SourceType] -> [SourceType] -> [TypeClassDict] - forClassName _ ctx cn@C.Warn _ [msg] = - -- Prefer a warning dictionary in scope if there is one available. - -- This allows us to defer a warning by propagating the constraint. - findDicts ctx cn ByNullSourcePos ++ [TypeClassDictionaryInScope Nothing 0 (WarnInstance msg) [] C.Warn [] [] [msg] Nothing Nothing] - forClassName _ _ C.IsSymbol _ args | Just dicts <- solveIsSymbol args = dicts - forClassName _ _ C.SymbolCompare _ args | Just dicts <- solveSymbolCompare args = dicts - forClassName _ _ C.SymbolAppend _ args | Just dicts <- solveSymbolAppend args = dicts - forClassName _ _ C.SymbolCons _ args | Just dicts <- solveSymbolCons args = dicts - forClassName _ _ C.IntAdd _ args | Just dicts <- solveIntAdd args = dicts - forClassName _ ctx C.IntCompare _ args | Just dicts <- solveIntCompare ctx args = dicts - forClassName _ _ C.IntMul _ args | Just dicts <- solveIntMul args = dicts - forClassName _ _ C.IntToString _ args | Just dicts <- solveIntToString args = dicts - forClassName _ _ C.Reflectable _ args | Just dicts <- solveReflectable args = dicts - forClassName _ _ C.RowUnion kinds args | Just dicts <- solveUnion kinds args = dicts - forClassName _ _ C.RowNub kinds args | Just dicts <- solveNub kinds args = dicts - forClassName _ _ C.RowLacks kinds args | Just dicts <- solveLacks kinds args = dicts - forClassName _ _ C.RowCons kinds args | Just dicts <- solveRowCons kinds args = dicts - forClassName _ _ C.RowToList kinds args | Just dicts <- solveRowToList kinds args = dicts - forClassName _ ctx cn@(Qualified (ByModuleName mn) _) _ tys = concatMap (findDicts ctx cn) (ordNub (ByNullSourcePos : ByModuleName mn : map ByModuleName (mapMaybe ctorModules tys))) - forClassName _ _ _ _ _ = internalError "forClassName: expected qualified class name" - - ctorModules :: SourceType -> Maybe ModuleName - ctorModules (TypeConstructor _ (Qualified (ByModuleName mn) _)) = Just mn - ctorModules (TypeConstructor _ (Qualified (BySourcePos _) _)) = internalError "ctorModules: unqualified type name" - ctorModules (TypeApp _ ty _) = ctorModules ty - ctorModules (KindApp _ ty _) = ctorModules ty - ctorModules (KindedType _ ty _) = ctorModules ty - ctorModules _ = Nothing - - valUndefined :: Expr - valUndefined = Var nullSourceSpan C.I_undefined - - solve :: SourceConstraint -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) Expr - solve = go 0 hints - where - go :: Int -> [ErrorMessageHint] -> SourceConstraint -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) Expr - go work _ (Constraint _ className' _ tys' _) | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys' - go work hints' con@(Constraint _ className' kinds' tys' conInfo) = WriterT . StateT . (withErrorMessageHint (ErrorSolvingConstraint con) .) . runStateT . runWriterT $ do - -- We might have unified types by solving other constraints, so we need to - -- apply the latest substitution. - latestSubst <- lift . lift $ gets checkSubstitution - let kinds'' = map (substituteType latestSubst) kinds' - tys'' = map (substituteType latestSubst) tys' - - -- Get the inferred constraint context so far, and merge it with the global context - inferred <- lift get - -- We need information about functional dependencies, so we have to look up the class - -- name in the environment: - env <- lift . lift $ gets checkEnv - let classesInScope = typeClasses env - TypeClassData - { typeClassArguments - , typeClassDependencies - , typeClassIsEmpty - , typeClassCoveringSets - , typeClassMembers - } <- case M.lookup className' classesInScope of - Nothing -> throwError . errorMessage $ UnknownClass className' - Just tcd -> pure tcd - - dicts <- lift . lift $ forClassNameM env (combineContexts context inferred) className' kinds'' tys'' - - let (catMaybes -> ambiguous, instances) = partitionEithers $ do - chain :: NonEmpty TypeClassDict <- - NEL.groupBy ((==) `on` tcdChain) $ - sortOn (tcdChain &&& tcdIndex) - dicts - -- process instances in a chain in index order - let found = for (tails1 chain) $ \(tcd :| tl) -> - -- Make sure the type unifies with the type in the type instance definition - case matches typeClassDependencies tcd tys'' of - Apart -> Right () -- keep searching - Match substs -> Left (Right (substs, tcd)) -- found a match - Unknown -> - if null (tcdChain tcd) || null tl - then Right () -- need proof of apartness but this is either not in a chain or at the end - else Left (Left (tcdToInstanceDescription tcd)) -- can't continue with this chain yet, need proof of apartness - - lefts [found] - solution <- lift . lift - $ unique kinds'' tys'' ambiguous instances - $ unknownsInAllCoveringSets (fst . (typeClassArguments !!)) typeClassMembers tys'' typeClassCoveringSets - case solution of - Solved substs tcd -> do - -- Note that we solved something. - tell (Any True, mempty) - -- Make sure the substitution is valid: - lift . lift . for_ substs $ pairwiseM unifyTypes - -- Now enforce any functional dependencies, using unification - -- Note: we need to generate fresh types for any unconstrained - -- type variables before unifying. - let subst = fmap (headDef $ internalError "entails: empty substitution") substs - currentSubst <- lift . lift $ gets checkSubstitution - subst' <- lift . lift $ withFreshTypes tcd (fmap (substituteType currentSubst) subst) - lift . lift $ zipWithM_ (\t1 t2 -> do - let inferredType = replaceAllTypeVars (M.toList subst') t1 - unifyTypes inferredType t2) (tcdInstanceTypes tcd) tys'' - currentSubst' <- lift . lift $ gets checkSubstitution - let subst'' = fmap (substituteType currentSubst') subst' - -- Solve any necessary subgoals - args <- solveSubgoals subst'' (ErrorSolvingConstraint con) (tcdDependencies tcd) - - initDict <- lift . lift $ mkDictionary (tcdValue tcd) args - - let match = foldr (\(className, index) dict -> subclassDictionaryValue dict className index) - initDict - (tcdPath tcd) - - return (if typeClassIsEmpty then Unused match else match) - Unsolved unsolved -> do - -- Generate a fresh name for the unsolved constraint's new dictionary - ident <- freshIdent ("dict" <> runProperName (disqualify (constraintClass unsolved))) - let qident = Qualified ByNullSourcePos ident - -- Store the new dictionary in the InstanceContext so that we can solve this goal in - -- future. - newDicts <- lift . lift $ newDictionaries [] qident unsolved - let newContext = mkContext newDicts - modify (combineContexts newContext) - -- Mark this constraint for generalization - tell (mempty, [(ident, context, unsolved)]) - return (Var nullSourceSpan qident) - Deferred -> - -- Constraint was deferred, just return the dictionary unchanged, - -- with no unsolved constraints. Hopefully, we can solve this later. - return (TypeClassDictionary (srcConstraint className' kinds'' tys'' conInfo) context hints') - where - -- When checking functional dependencies, we need to use unification to make - -- sure it is safe to use the selected instance. We will unify the solved type with - -- the type in the instance head under the substitution inferred from its instantiation. - -- As an example, when solving MonadState t0 (State Int), we choose the - -- MonadState s (State s) instance, and we unify t0 with Int, since the functional - -- dependency from MonadState dictates that t0 should unify with s\[s -> Int], which is - -- Int. This is fine, but in some cases, the substitution does not remove all TypeVars - -- from the type, so we end up with a unification error. So, any type arguments which - -- appear in the instance head, but not in the substitution need to be replaced with - -- fresh type variables. This function extends a substitution with fresh type variables - -- as necessary, based on the types in the instance head. It also unifies kinds based on - -- the substitution so kind information propagates correctly through the solver. - withFreshTypes - :: TypeClassDict - -> Matching SourceType - -> TypeCheckM (Matching SourceType) - withFreshTypes TypeClassDictionaryInScope{..} initSubst = do - subst <- foldM withFreshType initSubst $ filter (flip M.notMember initSubst . fst) tcdForAll - for_ (M.toList initSubst) $ unifySubstKind subst - pure subst - where - withFreshType subst (var, kind) = do - ty <- freshTypeWithKind $ replaceAllTypeVars (M.toList subst) kind - pure $ M.insert var ty subst - - unifySubstKind subst (var, ty) = - for_ (lookup var tcdForAll) $ \instKind -> do - tyKind <- elaborateKind ty - currentSubst <- gets checkSubstitution - unifyKinds' - (substituteType currentSubst . replaceAllTypeVars (M.toList subst) $ instKind) - (substituteType currentSubst tyKind) - - unique :: [SourceType] -> [SourceType] -> [Qualified (Either SourceType Ident)] -> [(a, TypeClassDict)] -> UnknownsHint -> TypeCheckM (EntailsResult a) - unique kindArgs tyArgs ambiguous [] unks - | solverDeferErrors = return Deferred - -- We need a special case for nullary type classes, since we want - -- to generalize over Partial constraints. - | solverShouldGeneralize && ((null kindArgs && null tyArgs) || any canBeGeneralized kindArgs || any canBeGeneralized tyArgs) = - return (Unsolved (srcConstraint className' kindArgs tyArgs conInfo)) - | otherwise = throwError . errorMessage $ NoInstanceFound (srcConstraint className' kindArgs tyArgs conInfo) ambiguous unks - unique _ _ _ [(a, dict)] _ = return $ Solved a dict - unique _ tyArgs _ tcds _ - | pairwiseAny overlapping (map snd tcds) = - throwError . errorMessage $ OverlappingInstances className' tyArgs (tcds >>= (toList . tcdToInstanceDescription . snd)) - | otherwise = return $ uncurry Solved (minimumBy (compare `on` length . tcdPath . snd) tcds) - - tcdToInstanceDescription :: TypeClassDict -> Maybe (Qualified (Either SourceType Ident)) - tcdToInstanceDescription TypeClassDictionaryInScope{ tcdDescription, tcdValue } = - let nii = namedInstanceIdentifier tcdValue - in case tcdDescription of - Just ty -> flip Qualified (Left ty) <$> fmap (byMaybeModuleName . getQual) nii - Nothing -> fmap Right <$> nii - - canBeGeneralized :: Type a -> Bool - canBeGeneralized TUnknown{} = True - canBeGeneralized (KindedType _ t _) = canBeGeneralized t - canBeGeneralized _ = False - - -- Check if two dictionaries are overlapping - -- - -- Dictionaries which are subclass dictionaries cannot overlap, since otherwise the overlap would have - -- been caught when constructing superclass dictionaries. - overlapping :: TypeClassDict -> TypeClassDict -> Bool - overlapping TypeClassDictionaryInScope{ tcdPath = _ : _ } _ = False - overlapping _ TypeClassDictionaryInScope{ tcdPath = _ : _ } = False - overlapping TypeClassDictionaryInScope{ tcdDependencies = Nothing } _ = False - overlapping _ TypeClassDictionaryInScope{ tcdDependencies = Nothing } = False - overlapping tcd1 tcd2 = tcdValue tcd1 /= tcdValue tcd2 - - -- Create dictionaries for subgoals which still need to be solved by calling go recursively - -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type - -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively. - solveSubgoals :: Matching SourceType -> ErrorMessageHint -> Maybe [SourceConstraint] -> WriterT (Any, [(Ident, InstanceContext, SourceConstraint)]) (StateT InstanceContext TypeCheckM) (Maybe [Expr]) - solveSubgoals _ _ Nothing = return Nothing - solveSubgoals subst hint (Just subgoals) = - Just <$> traverse (rethrow (addHint hint) . go (work + 1) (hints' <> [hint]) . mapConstraintArgsAll (map (replaceAllTypeVars (M.toList subst)))) subgoals - - -- We need subgoal dictionaries to appear in the term somewhere - -- If there aren't any then the dictionary is just undefined - useEmptyDict :: Maybe [Expr] -> Expr - useEmptyDict args = Unused (foldl (App . Abs (VarBinder nullSourceSpan UnusedIdent)) valUndefined (fold args)) - - -- Make a dictionary from subgoal dictionaries by applying the correct function - mkDictionary :: Evidence -> Maybe [Expr] -> TypeCheckM Expr - mkDictionary (NamedInstance n) args = return $ foldl App (Var nullSourceSpan n) (fold args) - mkDictionary EmptyClassInstance args = return (useEmptyDict args) - mkDictionary (WarnInstance msg) args = do - tell . errorMessage $ UserDefinedWarning msg - -- We cannot call the type class constructor here because Warn is declared in Prim. - -- This means that it doesn't have a definition that we can import. - -- So pass an empty placeholder (undefined) instead. - return (useEmptyDict args) - mkDictionary (IsSymbolInstance sym) _ = - let fields = [ ("reflectSymbol", Abs (VarBinder nullSourceSpan UnusedIdent) (Literal nullSourceSpan (StringLiteral sym))) ] in - return $ App (Constructor nullSourceSpan (coerceProperName . dictTypeName <$> C.IsSymbol)) (Literal nullSourceSpan (ObjectLiteral fields)) - mkDictionary (ReflectableInstance ref) _ = - let fields = [ ("reflectType", Abs (VarBinder nullSourceSpan UnusedIdent) (asExpression ref)) ] in - pure $ App (Constructor nullSourceSpan (coerceProperName . dictTypeName <$> C.Reflectable)) (Literal nullSourceSpan (ObjectLiteral fields)) - - unknownsInAllCoveringSets :: (Int -> Text) -> [(Ident, SourceType, Maybe (S.Set (NEL.NonEmpty Int)))] -> [SourceType] -> S.Set (S.Set Int) -> UnknownsHint - unknownsInAllCoveringSets indexToArgText tyClassMembers tyArgs coveringSets = do - let unkIndices = findIndices containsUnknowns tyArgs - if all (\s -> any (`S.member` s) unkIndices) coveringSets then - fromMaybe Unknowns unknownsRequiringVtas - else - NoUnknowns - where - unknownsRequiringVtas = do - tyClassModuleName <- getQual className' - let - tyClassMemberVta :: M.Map (Qualified Ident) [[Text]] - tyClassMemberVta = M.fromList $ mapMaybe qualifyAndFilter tyClassMembers - where - -- Only keep type class members that need VTAs to resolve their type class instances - qualifyAndFilter (ident, _, mbVtaRequiredArgs) = mbVtaRequiredArgs <&> \vtaRequiredArgs -> - (Qualified (ByModuleName tyClassModuleName) ident, map (map indexToArgText . NEL.toList) $ S.toList vtaRequiredArgs) - - tyClassMembersInExpr :: Expr -> [(Qualified Ident, [[Text]])] - tyClassMembersInExpr = getVars - where - (_, getVars, _, _, _) = everythingOnValues (++) ignore getVarIdents ignore ignore ignore - ignore = const [] - getVarIdents = \case - Var _ ident | Just vtas <- M.lookup ident tyClassMemberVta -> - [(ident, vtas)] - _ -> - [] - - getECTExpr = \case - ErrorCheckingType expr _ -> Just expr - _ -> Nothing - - tyClassMembers' <- headMay $ mapMaybe (fmap tyClassMembersInExpr . getECTExpr) hints - membersWithVtas <- NEL.nonEmpty tyClassMembers' - pure $ UnknownsWithVtaRequiringArgs membersWithVtas - - -- Turn a DictionaryValue into a Expr - subclassDictionaryValue :: Expr -> Qualified (ProperName 'ClassName) -> Integer -> Expr - subclassDictionaryValue dict className index = - App (Accessor (mkString (superclassName className index)) dict) valUndefined - - solveCoercible :: Environment -> InstanceContext -> [SourceType] -> [SourceType] -> TypeCheckM (Maybe [TypeClassDict]) - solveCoercible env ctx kinds [a, b] = do - let coercibleDictsInScope = findDicts ctx C.Coercible ByNullSourcePos - givens = flip mapMaybe coercibleDictsInScope $ \case - dict | [a', b'] <- tcdInstanceTypes dict -> Just (a', b') - | otherwise -> Nothing - GivenSolverState{ inertGivens } <- execStateT (solveGivens env) $ - initialGivenSolverState givens - (WantedSolverState{ inertWanteds }, hints') <- runWriterT . execStateT (solveWanteds env) $ - initialWantedSolverState inertGivens a b - -- Solving fails when there's irreducible wanteds left. - -- - -- We report the first residual constraint instead of the initial wanted, - -- unless we just swapped its arguments. - -- - -- We may have collected hints for the solving failure along the way, in - -- which case we decorate the error with the first one. - maybe id addHint (listToMaybe hints') `rethrow` case inertWanteds of - [] -> pure $ Just [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.Coercible [] kinds [a, b] Nothing Nothing] - (k, a', b') : _ | a' == b && b' == a -> throwError $ insoluble k b' a' - (k, a', b') : _ -> throwError $ insoluble k a' b' - solveCoercible _ _ _ _ = pure Nothing - - solveIsSymbol :: [SourceType] -> Maybe [TypeClassDict] - solveIsSymbol [TypeLevelString ann sym] = Just [TypeClassDictionaryInScope Nothing 0 (IsSymbolInstance sym) [] C.IsSymbol [] [] [TypeLevelString ann sym] Nothing Nothing] - solveIsSymbol _ = Nothing - - solveSymbolCompare :: [SourceType] -> Maybe [TypeClassDict] - solveSymbolCompare [arg0@(TypeLevelString _ lhs), arg1@(TypeLevelString _ rhs), _] = - let ordering = case compare lhs rhs of - LT -> C.LT - EQ -> C.EQ - GT -> C.GT - args' = [arg0, arg1, srcTypeConstructor ordering] - in Just [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.SymbolCompare [] [] args' Nothing Nothing] - solveSymbolCompare _ = Nothing - - solveSymbolAppend :: [SourceType] -> Maybe [TypeClassDict] - solveSymbolAppend [arg0, arg1, arg2] = do - (arg0', arg1', arg2') <- appendSymbols arg0 arg1 arg2 - let args' = [arg0', arg1', arg2'] - pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.SymbolAppend [] [] args' Nothing Nothing] - solveSymbolAppend _ = Nothing - - -- Append type level symbols, or, run backwards, strip a prefix or suffix - appendSymbols :: SourceType -> SourceType -> SourceType -> Maybe (SourceType, SourceType, SourceType) - appendSymbols arg0@(TypeLevelString _ lhs) arg1@(TypeLevelString _ rhs) _ = Just (arg0, arg1, srcTypeLevelString (lhs <> rhs)) - appendSymbols arg0@(TypeLevelString _ lhs) _ arg2@(TypeLevelString _ out) = do - lhs' <- decodeString lhs - out' <- decodeString out - rhs <- stripPrefix lhs' out' - pure (arg0, srcTypeLevelString (mkString rhs), arg2) - appendSymbols _ arg1@(TypeLevelString _ rhs) arg2@(TypeLevelString _ out) = do - rhs' <- decodeString rhs - out' <- decodeString out - lhs <- stripSuffix rhs' out' - pure (srcTypeLevelString (mkString lhs), arg1, arg2) - appendSymbols _ _ _ = Nothing - - solveSymbolCons :: [SourceType] -> Maybe [TypeClassDict] - solveSymbolCons [arg0, arg1, arg2] = do - (arg0', arg1', arg2') <- consSymbol arg0 arg1 arg2 - let args' = [arg0', arg1', arg2'] - pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.SymbolCons [] [] args' Nothing Nothing] - solveSymbolCons _ = Nothing - - consSymbol :: SourceType -> SourceType -> SourceType -> Maybe (SourceType, SourceType, SourceType) - consSymbol _ _ arg@(TypeLevelString _ s) = do - (h, t) <- T.uncons =<< decodeString s - pure (mkTLString (T.singleton h), mkTLString t, arg) - where mkTLString = srcTypeLevelString . mkString - consSymbol arg1@(TypeLevelString _ h) arg2@(TypeLevelString _ t) _ = do - h' <- decodeString h - t' <- decodeString t - guard (T.length h' == 1) - pure (arg1, arg2, srcTypeLevelString (mkString $ h' <> t')) - consSymbol _ _ _ = Nothing - - solveIntToString :: [SourceType] -> Maybe [TypeClassDict] - solveIntToString [arg0, _] = do - (arg0', arg1') <- printIntToString arg0 - let args' = [arg0', arg1'] - pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.IntToString [] [] args' Nothing Nothing] - solveIntToString _ = Nothing - - printIntToString :: SourceType -> Maybe (SourceType, SourceType) - printIntToString arg0@(TypeLevelInt _ i) = do - pure (arg0, srcTypeLevelString $ mkString $ T.pack $ show i) - printIntToString _ = Nothing - - solveReflectable :: [SourceType] -> Maybe [TypeClassDict] - solveReflectable [typeLevel, _] = do - (ref, typ) <- case typeLevel of - TypeLevelInt _ i -> pure (ReflectableInt i, tyInt) - TypeLevelString _ s -> pure (ReflectableString s, tyString) - TypeConstructor _ n - | n == C.True -> pure (ReflectableBoolean True, tyBoolean) - | n == C.False -> pure (ReflectableBoolean False, tyBoolean) - | n == C.LT -> pure (ReflectableOrdering LT, srcTypeConstructor C.Ordering) - | n == C.EQ -> pure (ReflectableOrdering EQ, srcTypeConstructor C.Ordering) - | n == C.GT -> pure (ReflectableOrdering GT, srcTypeConstructor C.Ordering) - _ -> Nothing - pure [TypeClassDictionaryInScope Nothing 0 (ReflectableInstance ref) [] C.Reflectable [] [] [typeLevel, typ] Nothing Nothing] - solveReflectable _ = Nothing - - solveIntAdd :: [SourceType] -> Maybe [TypeClassDict] - solveIntAdd [arg0, arg1, arg2] = do - (arg0', arg1', arg2') <- addInts arg0 arg1 arg2 - let args' = [arg0', arg1', arg2'] - pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.IntAdd [] [] args' Nothing Nothing] - solveIntAdd _ = Nothing - - addInts :: SourceType -> SourceType -> SourceType -> Maybe (SourceType, SourceType, SourceType) - -- l r -> o, l + r = o - addInts arg0@(TypeLevelInt _ l) arg1@(TypeLevelInt _ r) _ = pure (arg0, arg1, srcTypeLevelInt (l + r)) - -- l o -> r, o - l = r - addInts arg0@(TypeLevelInt _ l) _ arg2@(TypeLevelInt _ o) = pure (arg0, srcTypeLevelInt (o - l), arg2) - -- r o -> l, o - r = l - addInts _ arg1@(TypeLevelInt _ r) arg2@(TypeLevelInt _ o) = pure (srcTypeLevelInt (o - r), arg1, arg2) - addInts _ _ _ = Nothing - - solveIntCompare :: InstanceContext -> [SourceType] -> Maybe [TypeClassDict] - solveIntCompare _ [arg0@(TypeLevelInt _ a), arg1@(TypeLevelInt _ b), _] = - let ordering = case compare a b of - EQ -> C.EQ - LT -> C.LT - GT -> C.GT - args' = [arg0, arg1, srcTypeConstructor ordering] - in pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.IntCompare [] [] args' Nothing Nothing] - solveIntCompare ctx args@[a, b, _] = do - let compareDictsInScope = findDicts ctx C.IntCompare ByNullSourcePos - givens = flip mapMaybe compareDictsInScope $ \case - dict | [a', b', c'] <- tcdInstanceTypes dict -> mkRelation a' b' c' - | otherwise -> Nothing - facts = mkFacts (args : (tcdInstanceTypes <$> compareDictsInScope)) - c' <- solveRelation (givens <> facts) a b - pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.IntCompare [] [] [a, b, srcTypeConstructor c'] Nothing Nothing] - solveIntCompare _ _ = Nothing - - solveIntMul :: [SourceType] -> Maybe [TypeClassDict] - solveIntMul [arg0@(TypeLevelInt _ l), arg1@(TypeLevelInt _ r), _] = - let args' = [arg0, arg1, srcTypeLevelInt (l * r)] - in pure [TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.IntMul [] [] args' Nothing Nothing] - solveIntMul _ = Nothing - - solveUnion :: [SourceType] -> [SourceType] -> Maybe [TypeClassDict] - solveUnion kinds [l, r, u] = do - (lOut, rOut, uOut, cst, vars) <- unionRows kinds l r u - pure [ TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.RowUnion vars kinds [lOut, rOut, uOut] cst Nothing ] - solveUnion _ _ = Nothing - - -- Left biased union of two row types - - unionRows :: [SourceType] -> SourceType -> SourceType -> SourceType -> Maybe (SourceType, SourceType, SourceType, Maybe [SourceConstraint], [(Text, SourceType)]) - unionRows kinds l r u = - guard canMakeProgress $> (lOut, rOut, uOut, cons, vars) - where - (fixed, rest) = rowToList l - - rowVar = srcTypeVar "r" - - (canMakeProgress, lOut, rOut, uOut, cons, vars) = - case rest of - -- If the left hand side is a closed row, then we can merge - -- its labels into the right hand side. - REmptyKinded _ _ -> (True, l, r, rowFromList (fixed, r), Nothing, []) - -- If the right hand side and output are closed rows, then we can - -- compute the left hand side by subtracting the right hand side - -- from the output. - _ | (right, rightu@(REmptyKinded _ _)) <- rowToList r - , (output, restu@(REmptyKinded _ _)) <- rowToList u -> - let - -- Partition the output rows into those that belong in right - -- (taken off the end) and those that must end up in left. - grabLabel e (left', right', remaining) - | rowListLabel e `elem` remaining = - (left', e : right', delete (rowListLabel e) remaining) - | otherwise = - (e : left', right', remaining) - (outL, outR, leftover) = - foldr grabLabel ([], [], fmap rowListLabel right) output - in ( null leftover - , rowFromList (outL, restu) - , rowFromList (outR, rightu) - , u - , Nothing - , [] - ) - -- If the left hand side is not definitely closed, then the only way we - -- can safely make progress is to move any known labels from the left - -- input into the output, and add a constraint for any remaining labels. - -- Otherwise, the left hand tail might contain the same labels as on - -- the right hand side, and we can't be certain we won't reorder the - -- types for such labels. - _ -> ( not (null fixed) - , l, r - , rowFromList (fixed, rowVar) - , Just [ srcConstraint C.RowUnion kinds [rest, r, rowVar] Nothing ] - , [("r", kindRow (headDef (internalError "unionRows: empty kinds") kinds))] - ) - - solveRowCons :: [SourceType] -> [SourceType] -> Maybe [TypeClassDict] - solveRowCons kinds [TypeLevelString ann sym, ty, r, _] = - Just [ TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.RowCons [] kinds [TypeLevelString ann sym, ty, r, srcRCons (Label sym) ty r] Nothing Nothing ] - solveRowCons _ _ = Nothing - - solveRowToList :: [SourceType] -> [SourceType] -> Maybe [TypeClassDict] - solveRowToList [kind] [r, _] = do - entries <- rowToRowList kind r - pure [ TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.RowToList [] [kind] [r, entries] Nothing Nothing ] - solveRowToList _ _ = Nothing - - -- Convert a closed row to a sorted list of entries - rowToRowList :: SourceType -> SourceType -> Maybe SourceType - rowToRowList kind r = - guard (isREmpty rest) $> - foldr rowListCons (srcKindApp (srcTypeConstructor C.RowListNil) kind) fixed - where - (fixed, rest) = rowToSortedList r - rowListCons (RowListItem _ lbl ty) tl = - foldl srcTypeApp (srcKindApp (srcTypeConstructor C.RowListCons) kind) - [ srcTypeLevelString (runLabel lbl) - , ty - , tl ] - - solveNub :: [SourceType] -> [SourceType] -> Maybe [TypeClassDict] - solveNub kinds [r, _] = do - r' <- nubRows r - pure [ TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.RowNub [] kinds [r, r'] Nothing Nothing ] - solveNub _ _ = Nothing - - nubRows :: SourceType -> Maybe SourceType - nubRows r = - guard (isREmpty rest) $> - rowFromList (nubBy ((==) `on` rowListLabel) fixed, rest) - where - (fixed, rest) = rowToSortedList r - - solveLacks :: [SourceType] -> [SourceType] -> Maybe [TypeClassDict] - solveLacks kinds tys@[_, REmptyKinded _ _] = - pure [ TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.RowLacks [] kinds tys Nothing Nothing ] - solveLacks kinds [TypeLevelString ann sym, r] = do - (r', cst) <- rowLacks kinds sym r - pure [ TypeClassDictionaryInScope Nothing 0 EmptyClassInstance [] C.RowLacks [] kinds [TypeLevelString ann sym, r'] cst Nothing ] - solveLacks _ _ = Nothing - - rowLacks :: [SourceType] -> PSString -> SourceType -> Maybe (SourceType, Maybe [SourceConstraint]) - rowLacks kinds sym r = - guard (lacksSym && canMakeProgress) $> (r, cst) - where - (fixed, rest) = rowToList r - - lacksSym = - sym `notElem` (runLabel . rowListLabel <$> fixed) - - (canMakeProgress, cst) = case rest of - REmptyKinded _ _ -> (True, Nothing) - _ -> (not (null fixed), Just [ srcConstraint C.RowLacks kinds [srcTypeLevelString sym, rest] Nothing ]) - --- Check if an instance matches our list of types, allowing for types --- to be solved via functional dependencies. If the types match, we return a --- substitution which makes them match. If not, we return 'Nothing'. -matches :: [FunctionalDependency] -> TypeClassDict -> [SourceType] -> Matched (Matching [SourceType]) -matches deps TypeClassDictionaryInScope{..} tys = - -- First, find those types which match exactly - let matched = zipWith typeHeadsAreEqual tys tcdInstanceTypes in - -- Now, use any functional dependencies to infer any remaining types - if not (covers matched) - then if any ((==) Apart . fst) matched then Apart else Unknown - else -- Verify that any repeated type variables are unifiable - let determinedSet = foldMap (S.fromList . fdDetermined) deps - solved = map snd . filter ((`S.notMember` determinedSet) . fst) $ zipWith (\(_, ts) i -> (i, ts)) matched [0..] - in verifySubstitution (M.unionsWith (++) solved) - where - -- Find the closure of a set of functional dependencies. - covers :: [(Matched (), subst)] -> Bool - covers ms = finalSet == S.fromList [0..length ms - 1] - where - initialSet :: S.Set Int - initialSet = S.fromList . map snd . filter ((==) (Match ()) . fst . fst) $ zip ms [0..] - - finalSet :: S.Set Int - finalSet = untilFixedPoint applyAll initialSet - - untilFixedPoint :: Eq a => (a -> a) -> a -> a - untilFixedPoint f = go - where - go a | a' == a = a' - | otherwise = go a' - where a' = f a - - applyAll :: S.Set Int -> S.Set Int - applyAll s = foldr applyDependency s deps - - applyDependency :: FunctionalDependency -> S.Set Int -> S.Set Int - applyDependency FunctionalDependency{..} xs - | S.fromList fdDeterminers `S.isSubsetOf` xs = xs <> S.fromList fdDetermined - | otherwise = xs - - -- - -- Check whether the type heads of two types are equal (for the purposes of type class dictionary lookup), - -- and return a substitution from type variables to types which makes the type heads unify. - -- - typeHeadsAreEqual :: Type a -> Type a -> (Matched (), Matching [Type a]) - typeHeadsAreEqual (KindedType _ t1 _) t2 = typeHeadsAreEqual t1 t2 - typeHeadsAreEqual t1 (KindedType _ t2 _) = typeHeadsAreEqual t1 t2 - typeHeadsAreEqual (TUnknown _ u1) (TUnknown _ u2) | u1 == u2 = (Match (), M.empty) - typeHeadsAreEqual (Skolem _ _ _ s1 _) (Skolem _ _ _ s2 _) | s1 == s2 = (Match (), M.empty) - typeHeadsAreEqual t (TypeVar _ v) = (Match (), M.singleton v [t]) - typeHeadsAreEqual (TypeConstructor _ c1) (TypeConstructor _ c2) | c1 == c2 = (Match (), M.empty) - typeHeadsAreEqual (TypeLevelString _ s1) (TypeLevelString _ s2) | s1 == s2 = (Match (), M.empty) - typeHeadsAreEqual (TypeLevelInt _ n1) (TypeLevelInt _ n2) | n1 == n2 = (Match (), M.empty) - typeHeadsAreEqual (TypeApp _ h1 t1) (TypeApp _ h2 t2) = - both (typeHeadsAreEqual h1 h2) (typeHeadsAreEqual t1 t2) - typeHeadsAreEqual (KindApp _ h1 t1) (KindApp _ h2 t2) = - both (typeHeadsAreEqual h1 h2) (typeHeadsAreEqual t1 t2) - typeHeadsAreEqual (REmpty _) (REmpty _) = (Match (), M.empty) - typeHeadsAreEqual r1@RCons{} r2@RCons{} = - foldr both (uncurry go rest) common - where - (common, rest) = alignRowsWith (const typeHeadsAreEqual) r1 r2 - - go :: ([RowListItem a], Type a) -> ([RowListItem a], Type a) -> (Matched (), Matching [Type a]) - go (l, KindedType _ t1 _) (r, t2) = go (l, t1) (r, t2) - go (l, t1) (r, KindedType _ t2 _) = go (l, t1) (r, t2) - go (l, KindApp _ t1 k1) (r, KindApp _ t2 k2) | eqType k1 k2 = go (l, t1) (r, t2) - go ([], REmpty _) ([], REmpty _) = (Match (), M.empty) - go ([], TUnknown _ u1) ([], TUnknown _ u2) | u1 == u2 = (Match (), M.empty) - go ([], TypeVar _ v1) ([], TypeVar _ v2) | v1 == v2 = (Match (), M.empty) - go ([], Skolem _ _ _ sk1 _) ([], Skolem _ _ _ sk2 _) | sk1 == sk2 = (Match (), M.empty) - go ([], TUnknown _ _) _ = (Unknown, M.empty) - go (sd, r) ([], TypeVar _ v) = (Match (), M.singleton v [rowFromList (sd, r)]) - go _ _ = (Apart, M.empty) - typeHeadsAreEqual (TUnknown _ _) _ = (Unknown, M.empty) - typeHeadsAreEqual Skolem{} _ = (Unknown, M.empty) - typeHeadsAreEqual _ _ = (Apart, M.empty) - - both :: (Matched (), Matching [Type a]) -> (Matched (), Matching [Type a]) -> (Matched (), Matching [Type a]) - both (b1, m1) (b2, m2) = (b1 <> b2, M.unionWith (++) m1 m2) - - -- Ensure that a substitution is valid - verifySubstitution :: Matching [Type a] -> Matched (Matching [Type a]) - verifySubstitution mts = foldMap meet mts $> mts where - meet = pairwiseAll typesAreEqual - - -- Note that unknowns are only allowed to unify if they came from a type - -- which was _not_ solved, i.e. one which was inferred by a functional - -- dependency. - typesAreEqual :: Type a -> Type a -> Matched () - typesAreEqual (KindedType _ t1 _) t2 = typesAreEqual t1 t2 - typesAreEqual t1 (KindedType _ t2 _) = typesAreEqual t1 t2 - typesAreEqual (TUnknown _ u1) (TUnknown _ u2) | u1 == u2 = Match () - typesAreEqual (TUnknown _ u1) t2 = if t2 `containsUnknown` u1 then Apart else Unknown - typesAreEqual t1 (TUnknown _ u2) = if t1 `containsUnknown` u2 then Apart else Unknown - typesAreEqual (Skolem _ _ _ s1 _) (Skolem _ _ _ s2 _) | s1 == s2 = Match () - typesAreEqual (Skolem _ _ _ s1 _) t2 = if t2 `containsSkolem` s1 then Apart else Unknown - typesAreEqual t1 (Skolem _ _ _ s2 _) = if t1 `containsSkolem` s2 then Apart else Unknown - typesAreEqual (TypeVar _ v1) (TypeVar _ v2) | v1 == v2 = Match () - typesAreEqual (TypeLevelString _ s1) (TypeLevelString _ s2) | s1 == s2 = Match () - typesAreEqual (TypeLevelInt _ n1) (TypeLevelInt _ n2) | n1 == n2 = Match () - typesAreEqual (TypeConstructor _ c1) (TypeConstructor _ c2) | c1 == c2 = Match () - typesAreEqual (TypeApp _ h1 t1) (TypeApp _ h2 t2) = typesAreEqual h1 h2 <> typesAreEqual t1 t2 - typesAreEqual (KindApp _ h1 t1) (KindApp _ h2 t2) = typesAreEqual h1 h2 <> typesAreEqual t1 t2 - typesAreEqual (REmpty _) (REmpty _) = Match () - typesAreEqual r1 r2 | isRCons r1 || isRCons r2 = - let (common, rest) = alignRowsWith (const typesAreEqual) r1 r2 - in fold common <> uncurry go rest - where - go :: ([RowListItem a], Type a) -> ([RowListItem a], Type a) -> Matched () - go (l, KindedType _ t1 _) (r, t2) = go (l, t1) (r, t2) - go (l, t1) (r, KindedType _ t2 _) = go (l, t1) (r, t2) - go ([], KindApp _ t1 k1) ([], KindApp _ t2 k2) = typesAreEqual t1 t2 <> typesAreEqual k1 k2 - go ([], TUnknown _ u1) ([], TUnknown _ u2) | u1 == u2 = Match () - go ([], TUnknown _ _) ([], _) = Unknown - go ([], _) ([], TUnknown _ _) = Unknown - go ([], Skolem _ _ _ s1 _) ([], Skolem _ _ _ s2 _) | s1 == s2 = Match () - go ([], Skolem _ _ _ _ _) _ = Unknown - go _ ([], Skolem _ _ _ _ _) = Unknown - go ([], REmpty _) ([], REmpty _) = Match () - go ([], TypeVar _ v1) ([], TypeVar _ v2) | v1 == v2 = Match () - go _ _ = Apart - typesAreEqual _ _ = Apart - - isRCons :: Type a -> Bool - isRCons RCons{} = True - isRCons _ = False - - containsSkolem :: Type a -> Int -> Bool - containsSkolem t s = everythingOnTypes (||) (\case Skolem _ _ _ s' _ -> s == s'; _ -> False) t - - containsUnknown :: Type a -> Int -> Bool - containsUnknown t u = everythingOnTypes (||) (\case TUnknown _ u' -> u == u'; _ -> False) t - --- | Add a dictionary for the constraint to the scope, and dictionaries --- for all implied superclass instances. -newDictionaries - :: MonadState CheckState m - => [(Qualified (ProperName 'ClassName), Integer)] - -> Qualified Ident - -> SourceConstraint - -> m [NamedDict] -newDictionaries path name (Constraint _ className instanceKinds instanceTy _) = do - tcs <- gets (typeClasses . checkEnv) - let TypeClassData{..} = fromMaybe (internalError "newDictionaries: type class lookup failed") $ M.lookup className tcs - supDicts <- join <$> zipWithM (\(Constraint ann supName supKinds supArgs _) index -> - let sub = zip (map fst typeClassArguments) instanceTy in - newDictionaries ((supName, index) : path) - name - (Constraint ann supName - (replaceAllTypeVars sub <$> supKinds) - (replaceAllTypeVars sub <$> supArgs) - Nothing) - ) typeClassSuperclasses [0..] - return (TypeClassDictionaryInScope Nothing 0 name path className [] instanceKinds instanceTy Nothing Nothing : supDicts) - -mkContext :: [NamedDict] -> InstanceContext -mkContext = foldr combineContexts M.empty . map fromDict where - fromDict d = M.singleton ByNullSourcePos (M.singleton (tcdClassName d) (M.singleton (tcdValue d) (pure d))) - --- | Check all pairs of values in a list match a predicate -pairwiseAll :: Monoid m => (a -> a -> m) -> [a] -> m -pairwiseAll _ [] = mempty -pairwiseAll _ [_] = mempty -pairwiseAll p (x : xs) = foldMap (p x) xs <> pairwiseAll p xs - --- | Check any pair of values in a list match a predicate -pairwiseAny :: (a -> a -> Bool) -> [a] -> Bool -pairwiseAny _ [] = False -pairwiseAny _ [_] = False -pairwiseAny p (x : xs) = any (p x) xs || pairwiseAny p xs - -pairwiseM :: Applicative m => (a -> a -> m ()) -> [a] -> m () -pairwiseM _ [] = pure () -pairwiseM _ [_] = pure () -pairwiseM p (x : xs) = traverse (p x) xs *> pairwiseM p xs - --- | Return all nonempty tails of a nonempty list. For example: --- --- tails1 (fromList [1]) == fromList [fromList [1]] --- tails1 (fromList [1,2]) == fromList [fromList [1,2], fromList [2]] --- tails1 (fromList [1,2,3]) == fromList [fromList [1,2,3], fromList [2,3], fromList [3]] -tails1 :: NonEmpty a -> NonEmpty (NonEmpty a) -tails1 = - -- NEL.fromList is an unsafe function, but this usage should be safe, since: - -- - `tails xs = [xs, tail xs, tail (tail xs), ..., []]` - -- - If `xs` is nonempty, it follows that `tails xs` contains at least one nonempty - -- list, since `head (tails xs) = xs`. - -- - The only empty element of `tails xs` is the last one (by the definition of `tails`) - -- - Therefore, if we take all but the last element of `tails xs` i.e. - -- `init (tails xs)`, we have a nonempty list of nonempty lists - NEL.fromList . map NEL.fromList . init . tails . NEL.toList diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment/Coercible.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment/Coercible.hs deleted file mode 100644 index 18826f3a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment/Coercible.hs +++ /dev/null @@ -1,917 +0,0 @@ -{-# LANGUAGE DuplicateRecordFields #-} - --- | --- Interaction solver for Coercible constraints --- -module Language.PureScript.TypeChecker.Entailment.Coercible - ( GivenSolverState(..) - , initialGivenSolverState - , solveGivens - , WantedSolverState(..) - , initialWantedSolverState - , solveWanteds - , insoluble - ) where - -import Prelude hiding (interact) - -import Control.Applicative ((<|>), empty) -import Control.Arrow ((&&&)) -import Control.Monad ((<=<), guard, unless, when) -import Control.Monad.Error.Class (catchError, throwError) -import Control.Monad.State (StateT, get, gets, modify, put) -import Control.Monad.Trans.Class (lift) -import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) -import Control.Monad.Trans.Except (ExceptT(..), runExceptT) -import Control.Monad.Writer (Writer, execWriter, runWriter, runWriterT, tell, WriterT) -import Data.Either (partitionEithers) -import Data.Foldable (fold, foldl', for_, toList) -import Data.Functor (($>)) -import Data.List (find) -import Data.Maybe (fromMaybe, isJust) -import Data.Monoid (Any(..)) -import Data.Text (Text) - -import Data.Map qualified as M -import Data.Set qualified as S - -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (DataDeclType(..), Environment(..), TypeKind(..), unapplyKinds) -import Language.PureScript.Errors (DeclarationRef(..), ErrorMessageHint(..), ExportSource, ImportDeclarationType(..), MultipleErrors, SimpleErrorMessage(..), SourceAnn, errorMessage, UnknownsHint(..)) -import Language.PureScript.Names (ModuleName, ProperName, ProperNameType(..), Qualified(..), byMaybeModuleName, toMaybeModuleName) -import Language.PureScript.TypeChecker.Kinds (elaborateKind, freshKindWithKind, unifyKinds') -import Language.PureScript.TypeChecker.Monad (CheckState(..), TypeCheckM) -import Language.PureScript.TypeChecker.Roles (lookupRoles) -import Language.PureScript.TypeChecker.Synonyms (replaceAllTypeSynonyms) -import Language.PureScript.TypeChecker.Unify (alignRowsWith, freshTypeWithKind, substituteType) -import Language.PureScript.Roles (Role(..)) -import Language.PureScript.Types (Constraint(..), SourceType, Type(..), completeBinderList, containsUnknowns, everythingOnTypes, isMonoType, replaceAllTypeVars, rowFromList, srcConstraint, srcTypeApp, unapplyTypes) -import Language.PureScript.Constants.Prim qualified as Prim - --- | State of the given constraints solver. -data GivenSolverState = - GivenSolverState - { inertGivens :: [(SourceType, SourceType, SourceType)] - -- ^ A set of irreducible given constraints which do not interact together. - , unsolvedGivens :: [(SourceType, SourceType)] - -- ^ Given constraints yet to be solved. - } - --- | Initialize the given constraints solver state with the givens to solve. -initialGivenSolverState :: [(SourceType, SourceType)] -> GivenSolverState -initialGivenSolverState = - GivenSolverState [] - --- | The given constraints solver follows these steps: --- --- 1. Solving can diverge for recursive newtypes, so we check the solver depth --- and abort if we crossed an arbitrary limit. --- --- For instance the declarations: --- --- @ --- newtype N a = N (a -> N a) --- --- example :: forall a b. N a -> N b --- example = coerce --- @ --- --- yield the wanted @Coercible (N a) (N b)@ which we can unwrap on both sides --- to yield @Coercible (a -> N a) (b -> N b)@, which we can then decompose back --- to @Coercible a b@ and @Coercible (N a) (N b)@. --- --- 2. We pick a constraint from the unsolved queue. If the queue is empty we are --- done, otherwise we unify the constraint arguments kinds and continue. --- --- 3. Then we try to canonicalize the constraint. - --- 3a. Canonicalization can fail, in which case we swallow the error and pretend --- the constraint is irreducible because it is possible to eventually solve it. --- --- For instance the declarations: --- --- @ --- data D a = D a --- type role D nominal --- --- example :: forall a b. Coercible (D a) (D b) => D a -> D b --- example = coerce --- @ --- --- yield an insoluble given @Coercible (D a) (D b)@ which discharges the wanted --- constraint regardless, because the given can be solved if @a@ and @b@ turn --- out to be equal: @example (D true) :: D Boolean@ should compile. --- --- 3b. Canonicalization can succeed with an irreducible constraint which we --- then interact with the inert set. --- --- 3bi. These interactions can yield a derived constraint which we add to the --- unsolved queue and then go back to 1. --- --- 3bii. These interactions can discharge the constraint, in which case we go --- back to 1. --- --- 3biii The constraint may not react to the inert set, in which case we add it --- to the inert set, kick out any constraint that can be rewritten by the new --- inert, add them to the unsolved queue and then go back to 1. --- --- 3c. Otherwise canonicalization can succeed with derived constraints which we --- add to the unsolved queue and then go back to 1. -solveGivens - :: Environment - -> StateT GivenSolverState TypeCheckM () -solveGivens env = go (0 :: Int) where - go n = do - when (n > 1000) . throwError . errorMessage $ PossiblyInfiniteCoercibleInstance - gets unsolvedGivens >>= \case - [] -> pure () - given : unsolved -> do - (k, a, b) <- lift $ unify given - GivenSolverState{..} <- get - lift (fst <$> runWriterT (canon env Nothing k a b `catchError` recover)) >>= \case - Irreducible -> case interact env (a, b) inertGivens of - Just (Simplified (a', b')) -> - put $ GivenSolverState { unsolvedGivens = (a', b') : unsolved, .. } - Just Discharged -> - put $ GivenSolverState { unsolvedGivens = unsolved, .. } - Nothing -> do - let (kickedOut, kept) = partitionEithers $ kicksOut env (a, b) <$> inertGivens - put $ GivenSolverState - { inertGivens = (k, a, b) : kept - , unsolvedGivens = kickedOut <> unsolved - } - Canonicalized deriveds -> - put $ GivenSolverState { unsolvedGivens = toList deriveds <> unsolved, .. } - go (n + 1) - recover _ = pure Irreducible - --- | State of the wanted constraints solver. -data WantedSolverState = - WantedSolverState - { inertGivens :: [(SourceType, SourceType, SourceType)] - -- ^ A set of irreducible given constraints which do not interact together, - -- but which could interact with the wanteds. - , inertWanteds :: [(SourceType, SourceType, SourceType)] - -- ^ A set of irreducible wanted constraints which do not interact together, - -- nor with any given. - , unsolvedWanteds :: [(SourceType, SourceType)] - -- ^ Wanted constraints yet to be solved. - } - --- | Initialize the wanted constraints solver state with an inert set of givens --- and the two parameters of the wanted to solve. -initialWantedSolverState - :: [(SourceType, SourceType, SourceType)] - -> SourceType - -> SourceType - -> WantedSolverState -initialWantedSolverState givens a b = - WantedSolverState givens [] [(a, b)] - --- | The wanted constraints solver follows similar steps than the given solver, --- except for: --- --- 1. When canonicalization fails we can swallow the error, but only if the --- wanted interacts with the givens. --- --- For instance the declarations: --- --- @ --- data D a = D a --- type role D nominal --- --- example :: forall a b. Coercible (D a) (D b) => D a -> D b --- example = coerce --- @ --- --- yield an insoluble wanted @Coercible (D a) (D b)@ which is discharged by --- the given. But we want @example :: forall a b. D a -> D b@ to fail. --- --- 2. Irreducible wanted constraints don't interact with the inert wanteds set, --- because doing so would yield confusing error messages. --- --- For instance the declarations: --- --- @ --- data D a = D a --- --- example :: forall a. D a a -> D Boolean Char --- example = coerce --- @ --- --- yield the wanted @Coercible (D a a) (D Boolean Char)@, which is decomposed to --- the irreducibles @Coercible a Boolean@ and @Coercible a Char@. Would we --- interact the latter with the former, we would report an insoluble --- @Coercible Boolean Char@. -solveWanteds - :: Environment - -> StateT WantedSolverState CanonM () -solveWanteds env = go (0 :: Int) where - go n = do - when (n > 1000) . throwError . errorMessage $ PossiblyInfiniteCoercibleInstance - gets unsolvedWanteds >>= \case - [] -> pure () - wanted : unsolved -> do - (k, a, b) <- lift $ lift $ unify wanted - WantedSolverState{..} <- get - lift (canon env (Just inertGivens) k a b `catchError` recover (a, b) inertGivens) >>= \case - Irreducible -> case interact env (a, b) inertGivens of - Just (Simplified (a', b')) -> - put $ WantedSolverState { unsolvedWanteds = (a', b') : unsolved, .. } - Just Discharged -> - put $ WantedSolverState { unsolvedWanteds = unsolved, .. } - Nothing -> - put $ WantedSolverState - { inertWanteds = (k, a, b) : inertWanteds - , unsolvedWanteds = unsolved - , .. - } - Canonicalized deriveds -> - put $ WantedSolverState { unsolvedWanteds = toList deriveds <> unsolved, .. } - go (n + 1) - recover wanted givens errors = - case interact env wanted givens of - Nothing -> throwError errors - Just (Simplified wanted') -> pure . Canonicalized $ S.singleton wanted' - Just Discharged -> pure $ Canonicalized mempty - --- | Unifying constraints arguments kinds isn't strictly necessary but yields --- better error messages. For instance we cannot solve the constraint --- @Coercible (D :: Type -> Type) (D a :: Type)@ because its arguments kinds --- don't match and trying to unify them will say so, which is more helpful than --- simply saying that no type class instance was found. --- --- A subtle thing to note is that types with polymorphic kinds can be annotated --- with kind applications mentioning unknowns that we may have solved by --- unifying the kinds. --- --- For instance the declarations: --- --- @ --- data D :: forall k. k -> Type --- data D a = D --- --- type role D representational --- --- example :: D D -> D D --- example = coerce --- @ --- --- yield a wanted --- @Coercible (D \@(k1 -> Type) (D \@k1)) (D \@(k2 -> Type) (D \@k2))@, which we --- decompose to @Coercible (D \@k1) (D \@k2)@, where @k1@ and @k2@ are unknowns. --- This constraint is not reflexive because @D \@k1@ and @D \@k2@ are differents --- but both arguments kinds unify with @k -> Type@, where @k@ is a fresh unknown, --- so applying the substitution to @D \@k1@ and @D \@k2@ yields a --- @Coercible (D \@k) (D \@k)@ constraint which could be trivially solved by --- reflexivity instead of having to saturate the type constructors. -unify - :: (SourceType, SourceType) - -> TypeCheckM (SourceType, SourceType, SourceType) -unify (a, b) = do - let kindOf = sequence . (id &&& elaborateKind) <=< replaceAllTypeSynonyms - (a', kind) <- kindOf a - (b', kind') <- kindOf b - unifyKinds' kind kind' - subst <- gets checkSubstitution - pure ( substituteType subst kind - , substituteType subst a' - , substituteType subst b' - ) - --- | A successful interaction between an irreducible constraint and an inert --- given constraint has two possible outcomes: -data Interaction - = Simplified (SourceType, SourceType) - -- ^ The interaction can yield a derived constraint, - | Discharged - -- ^ or we can learn the irreducible constraint is redundant and discharge it. - --- | Interact an irreducible constraint with an inert set of givens. -interact - :: Environment - -> (SourceType, SourceType) - -> [(SourceType, SourceType, SourceType)] - -> Maybe Interaction -interact env irred = go where - go [] = Nothing - go (inert : _) - | canDischarge inert irred = Just Discharged - | Just derived <- interactSameTyVar inert irred = Just $ Simplified derived - | Just derived <- interactDiffTyVar env inert irred = Just $ Simplified derived - go (_ : inerts) = go inerts - --- | A given constraint of the form @Coercible a b@ can discharge constraints --- of the form @Coercible a b@ and @Coercible b a@. -canDischarge - :: (SourceType, SourceType, SourceType) - -> (SourceType, SourceType) - -> Bool -canDischarge (_, a, b) constraint = - (a, b) == constraint || (b, a) == constraint - --- | Two canonical constraints of the form @Coercible tv ty1@ and --- @Coercible tv ty2@ can interact together and yield a new constraint --- @Coercible ty1 ty2@. Canonicality matters to avoid loops. --- --- For instance the declarations: --- --- @ --- data D a = D a --- newtype N a = N (D (N a)) --- --- example :: forall a. Coercible a (D a) => a -> N a --- example = coerce --- @ --- --- yield a non canonical wanted @Coercible a (N a)@ that we can unwrap on the --- right to yield @Coercible a (D (N a))@. Would it interact with the non --- canonical given @Coercible a (D a)@ it would give @Coercible (D a) (D (N a))@, --- then decompose back to @Coercible a (N a)@. -interactSameTyVar - :: (SourceType, SourceType, SourceType) - -> (SourceType, SourceType) - -> Maybe (SourceType, SourceType) -interactSameTyVar (_, tv1, ty1) (tv2, ty2) - | tv1 == tv2 && isCanonicalTyVarEq (tv1, ty1) && isCanonicalTyVarEq (tv2, ty2) - = Just (ty1, ty2) - | otherwise = Nothing - --- | Two canonical constraints of the form @Coercible tv1 ty1@ and --- @Coercible tv2 ty2@ can interact together and yield a new constraint --- @Coercible tv2 ty2[ty1/tv1]@. Once again, canonicality matters to avoid loops. --- --- For instance the declarations: --- --- @ --- data D a = D a --- --- example :: forall a b. Coercible b (D b) => a -> b --- example = coerce --- @ --- --- yield an irreducible canonical wanted @Coercible a b@. Would it interact with --- the non canonical given @Coercible b (D b)@ it would give @Coercible a (D b)@, --- which would keep interacting indefinitely with the given. -interactDiffTyVar - :: Environment - -> (SourceType, SourceType, SourceType) - -> (SourceType, SourceType) - -> Maybe (SourceType, SourceType) -interactDiffTyVar env (_, tv1, ty1) (tv2, ty2) - | tv1 /= tv2 && isCanonicalTyVarEq (tv2, ty2) - , (ty2', Any True) <- runWriter $ rewrite env (tv1, ty1) ty2 - = Just (tv2, ty2') - | otherwise = Nothing - --- | A canonical constraint of the form @Coercible tv1 ty1@ can rewrite the --- right hand side of an irreducible constraint of the form @Coercible tv2 ty2@ --- by substituting @ty1@ for every occurrence of @tv1@ at representational and --- phantom role in @ty2@. Nominal occurrences are left untouched. -rewrite :: Environment -> (SourceType, SourceType) -> SourceType -> Writer Any SourceType -rewrite env (Skolem _ _ _ s1 _, ty1) | not $ occurs s1 ty1 = go where - go (Skolem _ _ _ s2 _) | s1 == s2 = tell (Any True) $> ty1 - go ty2 | (Skolem{}, _, xs) <- unapplyTypes ty2, not $ null xs = - rewriteTyVarApp go ty2 - | (TypeConstructor _ tyName, _, _) <- unapplyTypes ty2 = do - rewriteTyConApp go (lookupRoles env tyName) ty2 - go (KindApp sa ty k) = KindApp sa <$> go ty <*> pure k - go (ForAll sa vis tv k ty scope) = ForAll sa vis tv k <$> go ty <*> pure scope - go (ConstrainedType sa Constraint{..} ty) | s1 `S.notMember` foldMap skolems constraintArgs = - ConstrainedType sa Constraint{..} <$> go ty - go (RCons sa label ty rest) = RCons sa label <$> go ty <*> go rest - go (KindedType sa ty k) = KindedType sa <$> go ty <*> pure k - go ty2 = pure ty2 -rewrite _ _ = pure - --- | Rewrite the head of a type application of the form @tv a_0 .. a_n@. -rewriteTyVarApp - :: Applicative m - => (SourceType -> m SourceType) - -> SourceType - -> m SourceType -rewriteTyVarApp f = go where - go (TypeApp sa lhs rhs) = - TypeApp sa <$> go lhs <*> pure rhs - go (KindApp sa ty k) = - KindApp sa <$> go ty <*> pure k - go ty = f ty - --- | Rewrite the representational and phantom arguments of a type application --- of the form @D a_0 .. a_n@. -rewriteTyConApp - :: Applicative m - => (SourceType -> m SourceType) - -> [Role] - -> SourceType - -> m SourceType -rewriteTyConApp f = go where - go (role : roles) (TypeApp sa lhs rhs) = - TypeApp sa <$> go roles lhs <*> case role of - Nominal -> pure rhs - _ -> f rhs - go roles (KindApp sa ty k) = - KindApp sa <$> go roles ty <*> pure k - go _ ty = pure ty - -canRewrite :: Environment -> (SourceType, SourceType) -> SourceType -> Bool -canRewrite env irred = getAny . execWriter . rewrite env irred - --- | An irreducible given constraint must kick out of the inert set any --- constraint it can rewrite when it becomes inert, otherwise solving would be --- sensitive to the order of constraints. Wanteds cannot rewrite other wanteds --- so this applies only to givens. --- --- For instance the declaration: --- --- @ --- example :: forall f g a b. Coercible a (f b) => Coercible f g => Proxy f -> a -> g b --- example _ = coerce --- @ --- --- yields the irreducible givens @Coercible a (f b)@ and @Coercible f g@. Would --- we not kick out the former when adding the latter to the inert set we would --- not be able to rewrite it to @Coercible a (g b)@ and discharge the wanted, --- but inverting the givens would work. -kicksOut - :: Environment - -> (SourceType, SourceType) - -> (SourceType, SourceType, SourceType) - -> Either (SourceType, SourceType) (SourceType, SourceType, SourceType) -kicksOut env irred (_, tv2, ty2) - | isCanonicalTyVarEq (tv2, ty2) && canRewrite env irred ty2 - = Left (tv2, ty2) -kicksOut _ _ inert = Right inert - --- | A constraint of the form @Coercible tv ty@ is canonical when @tv@ does not --- occur in @ty@. Non canonical constraints do not interact to prevent loops. -isCanonicalTyVarEq :: (SourceType, SourceType) -> Bool -isCanonicalTyVarEq (Skolem _ _ _ s _, ty) = not $ occurs s ty -isCanonicalTyVarEq _ = False - -occurs :: Int -> SourceType -> Bool -occurs s1 = everythingOnTypes (||) go where - go (Skolem _ _ _ s2 _) | s1 == s2 = True - go _ = False - -skolems :: SourceType -> S.Set Int -skolems = everythingOnTypes (<>) go where - go (Skolem _ _ _ s _) = S.singleton s - go _ = mempty - --- | A successful canonicalization result has two possible outcomes: -data Canonicalized - = Canonicalized (S.Set (SourceType, SourceType)) - -- ^ Canonicalization can yield a set of derived constraints, - | Irreducible - -- ^ or we can learn the constraint is irreducible. Irreducibility is not - -- necessarily an error, we may make further progress by interacting with - -- inerts. - -type CanonM = WriterT [ErrorMessageHint] TypeCheckM - --- | Canonicalization takes a wanted constraint and try to reduce it to a set of --- simpler constraints whose satisfaction will imply the goal. -canon - :: Environment - -> Maybe [(SourceType, SourceType, SourceType)] - -> SourceType - -> SourceType - -> SourceType - -> CanonM Canonicalized -canon env givens k a b = - maybe (throwError $ insoluble k a b) pure <=< runMaybeT $ - canonRefl a b - <|> canonUnsaturatedHigherKindedType env a b - <|> canonRow a b - -- We unwrap newtypes before trying the decomposition rules because it let - -- us solve more constraints. - -- - -- For instance the declarations: - -- - -- @ - -- newtype N f a = N (f a) - -- - -- example :: forall a b. Coercible a b => N Maybe a -> N Maybe b - -- example = coerce - -- @ - -- - -- yield the wanted @Coercible (N Maybe a) (N Maybe b)@ which we cannot - -- decompose because the second parameter of @N@ is nominal. On the other - -- hand, unwrapping on both sides yields @Coercible (Maybe a) (Maybe b)@ - -- which we can then decompose to @Coercible a b@ and discharge with the - -- given. - <|> canonNewtypeLeft env a b - <|> canonNewtypeRight env a b - <|> canonDecomposition env a b - <|> canonDecompositionFailure env k a b - <|> canonNewtypeDecomposition env givens a b - <|> canonNewtypeDecompositionFailure a b - <|> canonTypeVars a b - <|> canonTypeVarLeft a b - <|> canonTypeVarRight a b - <|> canonApplicationLeft a b - <|> canonApplicationRight a b - -insoluble - :: SourceType - -> SourceType - -> SourceType - -> MultipleErrors -insoluble k a b = - -- We can erase kind applications when determining whether to show the - -- "Consider adding a type annotation" hint, because annotating kinds to - -- instantiate unknowns in Coercible constraints should never resolve - -- NoInstanceFound errors. - errorMessage $ NoInstanceFound (srcConstraint Prim.Coercible [k] [a, b] Nothing) [] - $ if any containsUnknowns [a, b] then Unknowns else NoUnknowns - --- | Constraints of the form @Coercible a b@ can be solved if the two arguments --- are the same. Since we currently don't support higher-rank arguments in --- instance heads, term equality is a sufficient notion of "the same". -canonRefl - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonRefl a b = - guard (a == b) $> Canonicalized mempty - --- | Constraints of the form @Coercible (T1 a_0 .. a_n) (T2 b_0 .. b_n)@, where --- both arguments have kind @k1 -> k2@, yield a constraint --- @Coercible (T1 a_0 .. a_n c_0 .. c_m) (T2 b_0 .. b_n c_0 .. c_m)@, where both --- arguments are fully saturated with the same unknowns and have kind @Type@. -canonUnsaturatedHigherKindedType - :: Environment - -> SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonUnsaturatedHigherKindedType env a b - | (TypeConstructor _ aTyName, akapps, axs) <- unapplyTypes a - , (ak, _) <- fromMaybe (internalError "canonUnsaturatedHigherKindedType: type lookup failed") $ M.lookup aTyName (types env) - , (aks, _) <- unapplyKinds ak - , length axs < length aks = do - ak' <- lift $ do - let (kvs, ak') = fromMaybe (internalError "canonUnsaturatedHigherKindedType: unkinded forall binder") $ completeBinderList ak - instantiatedKinds = zipWith (\(_, (kv, _)) k -> (kv, k)) kvs akapps - unknownKinds <- traverse (\((ss, _), (kv, k)) -> (kv,) <$> lift (freshKindWithKind ss k)) $ drop (length akapps) kvs - pure $ replaceAllTypeVars (instantiatedKinds <> unknownKinds) ak' - let (aks', _) = unapplyKinds ak' - tys <- traverse (lift . lift . freshTypeWithKind) $ drop (length axs) aks' - let a' = foldl' srcTypeApp a tys - b' = foldl' srcTypeApp b tys - pure . Canonicalized $ S.singleton (a', b') - | otherwise = empty - --- | Constraints of the form --- @Coercible ( label_0 :: a_0, .. label_n :: a_n | r ) ( label_0 :: b_0, .. label_n :: b_n | s )@ --- yield a constraint @Coercible r s@ and constraints on the types for each --- label in both rows. Labels exclusive to one row yield a failure. -canonRow - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonRow a b - | RCons{} <- a = - case alignRowsWith (const (,)) a b of - -- We throw early when a bare unknown remains on either side after - -- aligning the rows because we don't know how to canonicalize them yet - -- and the unification error thrown when the rows are misaligned should - -- not mention unknowns. - (_, (([], u@TUnknown{}), rl2)) -> do - k <- lift $ lift $ elaborateKind u - throwError $ insoluble k u (rowFromList rl2) - (_, (rl1, ([], u@TUnknown{}))) -> do - k <- lift $ lift $ elaborateKind u - throwError $ insoluble k (rowFromList rl1) u - (deriveds, (([], tail1), ([], tail2))) -> do - pure . Canonicalized . S.fromList $ (tail1, tail2) : deriveds - (_, (rl1, rl2)) -> - throwError . errorMessage $ TypesDoNotUnify (rowFromList rl1) (rowFromList rl2) - | otherwise = empty - --- | Unwrapping a newtype can fails in two ways: -data UnwrapNewtypeError - = CannotUnwrapInfiniteNewtypeChain - -- ^ The newtype might wrap an infinite newtype chain. We may think that this - -- is already handled by the solver depth check, but failing to unwrap - -- infinite chains of newtypes let us try other rules. - -- - -- For instance the declarations: - -- - -- @ - -- newtype N a = N (N a) - -- type role N representational - -- - -- example :: forall a b. Coercible a b => N a -> N b - -- example = coerce - -- @ - -- - -- yield a wanted @Coercible (N a) (N b)@ that we can decompose to - -- @Coercible a b@ then discharge with the given if the newtype - -- unwrapping rules do not apply. - | CannotUnwrapConstructor - -- ^ The constructor may not be in scope or may not belong to a newtype. - --- | Unwraps a newtype and yields its underlying type with the newtype arguments --- substituted in (e.g. @N[D/a] = D@ given @newtype N a = N a@ and @data D = D@). -unwrapNewtype - :: Environment - -> SourceType - -> CanonM (Either UnwrapNewtypeError SourceType) -unwrapNewtype env = go (0 :: Int) where - go n ty = runExceptT $ do - when (n > 1000) $ throwError CannotUnwrapInfiniteNewtypeChain - (currentModuleName, currentModuleImports) <- gets $ checkCurrentModule &&& checkCurrentModuleImports - case unapplyTypes ty of - (TypeConstructor _ newtypeName, ks, xs) - | Just (inScope, fromModuleName, tvs, newtypeCtorName, wrappedTy) <- - lookupNewtypeConstructorInScope env currentModuleName currentModuleImports newtypeName ks - -- We refuse to unwrap newtypes over polytypes because we don't know how - -- to canonicalize them yet and we'd rather try to make progress with - -- another rule. - , isMonoType wrappedTy -> do - unless inScope $ do - tell [MissingConstructorImportForCoercible newtypeCtorName] - throwError CannotUnwrapConstructor - for_ fromModuleName $ flip addConstructorImportForCoercible newtypeCtorName - let wrappedTySub = replaceAllTypeVars (zip tvs xs) wrappedTy - ExceptT (go (n + 1) wrappedTySub) `catchError` \case - CannotUnwrapInfiniteNewtypeChain -> throwError CannotUnwrapInfiniteNewtypeChain - CannotUnwrapConstructor -> pure wrappedTySub - _ -> throwError CannotUnwrapConstructor - addConstructorImportForCoercible fromModuleName newtypeCtorName = modify $ \st -> - st { checkConstructorImportsForCoercible = S.insert (fromModuleName, newtypeCtorName) $ checkConstructorImportsForCoercible st } - --- | Looks up a given name and, if it names a newtype, returns the names of the --- type's parameters, the type the newtype wraps and the names of the type's --- fields. -lookupNewtypeConstructor - :: Environment - -> Qualified (ProperName 'TypeName) - -> [SourceType] - -> Maybe ([Text], ProperName 'ConstructorName, SourceType) -lookupNewtypeConstructor env qualifiedNewtypeName ks = do - (newtyk, DataType Newtype tvs [(ctorName, [wrappedTy])]) <- M.lookup qualifiedNewtypeName (types env) - let (kvs, _) = fromMaybe (internalError "lookupNewtypeConstructor: unkinded forall binder") $ completeBinderList newtyk - instantiatedKinds = zipWith (\(_, (kv, _)) k -> (kv, k)) kvs ks - pure (map (\(name, _, _) -> name) tvs, ctorName, replaceAllTypeVars instantiatedKinds wrappedTy) - --- | Behaves like 'lookupNewtypeConstructor' but also returns whether the --- newtype constructor is in scope and the module from which it is imported, or --- 'Nothing' if it is defined in the current module. -lookupNewtypeConstructorInScope - :: Environment - -> Maybe ModuleName - -> [ ( SourceAnn - , ModuleName - , ImportDeclarationType - , Maybe ModuleName - , M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - ) - ] - -> Qualified (ProperName 'TypeName) - -> [SourceType] - -> Maybe (Bool, Maybe ModuleName, [Text], Qualified (ProperName 'ConstructorName), SourceType) -lookupNewtypeConstructorInScope env currentModuleName currentModuleImports qualifiedNewtypeName@(Qualified newtypeModuleName newtypeName) ks = do - let fromModule = find isNewtypeCtorImported currentModuleImports - fromModuleName = (\(_, n, _, _, _) -> n) <$> fromModule - asModuleName = (\(_, _, _, n, _) -> n) =<< fromModule - isDefinedInCurrentModule = toMaybeModuleName newtypeModuleName == currentModuleName - isImported = isJust fromModule - inScope = isDefinedInCurrentModule || isImported - (tvs, ctorName, wrappedTy) <- lookupNewtypeConstructor env qualifiedNewtypeName ks - pure (inScope, fromModuleName, tvs, Qualified (byMaybeModuleName asModuleName) ctorName, wrappedTy) - where - isNewtypeCtorImported (_, _, importDeclType, _, exportedTypes) = - case M.lookup newtypeName exportedTypes of - Just ([_], _) -> case importDeclType of - Implicit -> True - Explicit refs -> any isNewtypeCtorRef refs - Hiding refs -> not $ any isNewtypeCtorRef refs - _ -> False - isNewtypeCtorRef = \case - TypeRef _ importedTyName Nothing -> importedTyName == newtypeName - TypeRef _ importedTyName (Just [_]) -> importedTyName == newtypeName - _ -> False - --- | Constraints of the form @Coercible (N a_0 .. a_n) b@ yield a constraint --- @Coercible a b@ if unwrapping the newtype yields @a@. -canonNewtypeLeft - :: Environment - -> SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonNewtypeLeft env a b = - lift (unwrapNewtype env a) >>= \case - Left CannotUnwrapInfiniteNewtypeChain -> empty - Left CannotUnwrapConstructor -> empty - Right a' -> pure . Canonicalized $ S.singleton (a', b) - --- | Constraints of the form @Coercible a (N b_0 .. b_n)@ yield a constraint --- @Coercible a b@ if unwrapping the newtype yields @b@. -canonNewtypeRight - :: Environment - -> SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonNewtypeRight env = - flip $ canonNewtypeLeft env - --- | Decomposes constraints of the form @Coercible (D a_0 .. a_n) (D b_0 .. b_n)@ --- into constraints on their representational arguments, ignoring phantom --- arguments and failing on unequal nominal arguments. --- --- For instance given the declarations: --- --- @ --- data D a b c = D a b --- type role D nominal representational phantom --- @ --- --- We can decompose @Coercible (D a b d) (D a c e)@ into @Coercible b c@, but --- decomposing @Coercible (D a c d) (D b c d)@ would fail. -decompose - :: Environment - -> Qualified (ProperName 'TypeName) - -> [SourceType] - -> [SourceType] - -> TypeCheckM Canonicalized -decompose env tyName axs bxs = do - let roles = lookupRoles env tyName - f role ax bx = case role of - Nominal - -- If we had first-class equality constraints, we'd just - -- emit one of the form @(a ~ b)@ here and let the solver - -- recurse. Since we don't we must compare the types at - -- this point and fail if they don't match. This likely - -- means there are cases we should be able to handle that - -- we currently can't, but is at least sound. - | ax == bx -> - pure mempty - | otherwise -> - throwError . errorMessage $ TypesDoNotUnify ax bx - Representational -> - pure $ S.singleton (ax, bx) - Phantom -> - pure mempty - fmap (Canonicalized . fold) $ sequence $ zipWith3 f roles axs bxs - --- | Constraints of the form @Coercible (D a_0 .. a_n) (D b_0 .. b_n)@, where --- @D@ is not a newtype, yield constraints on their arguments. -canonDecomposition - :: Environment - -> SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonDecomposition env a b - | (TypeConstructor _ aTyName, _, axs) <- unapplyTypes a - , (TypeConstructor _ bTyName, _, bxs) <- unapplyTypes b - , aTyName == bTyName - , Nothing <- lookupNewtypeConstructor env aTyName [] = - lift $ lift $ decompose env aTyName axs bxs - | otherwise = empty - --- | Constraints of the form @Coercible (D1 a_0 .. a_n) (D2 b_0 .. b_n)@, where --- @D1@ and @D2@ are different type constructors and neither of them are --- newtypes, are insoluble. -canonDecompositionFailure - :: Environment - -> SourceType - -> SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonDecompositionFailure env k a b - | (TypeConstructor _ aTyName, _, _) <- unapplyTypes a - , (TypeConstructor _ bTyName, _, _) <- unapplyTypes b - , aTyName /= bTyName - , Nothing <- lookupNewtypeConstructor env aTyName [] - , Nothing <- lookupNewtypeConstructor env bTyName [] = - throwError $ insoluble k a b - | otherwise = empty - --- | Wanted constraints of the form @Coercible (N a_0 .. a_n) (N b_0 .. b_n)@, --- where @N@ is a newtype whose constructor is out of scope, yield constraints --- on their arguments only when no given constraint can discharge them. --- --- We cannot decompose given constraints because newtypes are not necessarily --- injective with respect to representational equality. --- --- For instance given the declaration: --- --- @ --- newtype Const a b = MkConst a --- type role Const representational representational --- @ --- --- Decomposing a given @Coercible (Const a a) (Const a b)@ constraint to --- @Coercible a b@ when @MkConst@ is out of scope would let us coerce arbitrary --- types in modules where @MkConst@ is imported, because the given is easily --- satisfied with the newtype unwrapping rules. --- --- Moreover we do not decompose wanted constraints if they could be discharged --- by a given constraint. --- --- For instance the declaration: --- --- @ --- example :: forall a b. Coercible (Const a a) (Const a b) => Const a a -> Const a b --- example = coerce --- @ --- --- yield an irreducible given @Coercible (Const a a) (Const a b)@ when @MkConst@ --- is out of scope. Would we decompose the wanted --- @Coercible (Const a a) (Const a b)@ to @Coercible a b@ we would not be able --- to discharge it with the given. -canonNewtypeDecomposition - :: Environment - -> Maybe [(SourceType, SourceType, SourceType)] - -> SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonNewtypeDecomposition env (Just givens) a b - | (TypeConstructor _ aTyName, _, axs) <- unapplyTypes a - , (TypeConstructor _ bTyName, _, bxs) <- unapplyTypes b - , aTyName == bTyName - , Just _ <- lookupNewtypeConstructor env aTyName [] = do - let givensCanDischarge = any (\given -> canDischarge given (a, b)) givens - guard $ not givensCanDischarge - lift $ lift $ decompose env aTyName axs bxs -canonNewtypeDecomposition _ _ _ _ = empty - --- | Constraints of the form @Coercible (N1 a_0 .. a_n) (N2 b_0 .. b_n)@, where --- @N1@ and @N2@ are different type constructors and either of them is a --- newtype whose constructor is out of scope, are irreducible. -canonNewtypeDecompositionFailure - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonNewtypeDecompositionFailure a b - | (TypeConstructor{}, _, _) <- unapplyTypes a - , (TypeConstructor{}, _, _) <- unapplyTypes b - = pure Irreducible - | otherwise = empty - --- | Constraints of the form @Coercible tv1 tv2@ may be irreducibles, but only --- when the variables are lexicographically ordered. Reordering variables is --- necessary to prevent loops. --- --- For instance the declaration: --- --- @ --- example :: forall a b. Coercible a b => Coercible b a => a -> b --- example = coerce --- @ --- --- yields the irreducible givens @Coercible a b@ and @Coercible b a@ which would --- repeatedly kick each other out the inert set whereas reordering the latter to --- @Coercible a b@ makes it redundant and let us discharge it. -canonTypeVars - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonTypeVars a b - | Skolem _ tv1 _ _ _ <- a - , Skolem _ tv2 _ _ _ <- b - , tv2 < tv1 - = pure . Canonicalized $ S.singleton (b, a) - | Skolem{} <- a, Skolem{} <- b - = pure Irreducible - | otherwise = empty - --- | Constraints of the form @Coercible tv ty@ are irreducibles. -canonTypeVarLeft - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonTypeVarLeft a _ - | Skolem{} <- a = pure Irreducible - | otherwise = empty - --- | Constraints of the form @Coercible ty tv@ are reordered to --- @Coercible tv ty@ to satisfy the canonicality requirement of having the type --- variable on the left. -canonTypeVarRight - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonTypeVarRight a b - | Skolem{} <- b = pure . Canonicalized $ S.singleton (b, a) - | otherwise = empty - --- | Constraints of the form @Coercible (f a_0 .. a_n) b@ are irreducibles. -canonApplicationLeft - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonApplicationLeft a _ - | TypeApp{} <- a = pure Irreducible - | otherwise = empty - --- | Constraints of the form @Coercible a (f b_0 .. b_n) b@ are irreducibles. -canonApplicationRight - :: SourceType - -> SourceType - -> MaybeT CanonM Canonicalized -canonApplicationRight _ b - | TypeApp{} <- b = pure Irreducible - | otherwise = empty diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment/IntCompare.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment/IntCompare.hs deleted file mode 100644 index 802e9d61..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Entailment/IntCompare.hs +++ /dev/null @@ -1,102 +0,0 @@ --- | --- Graph-based solver for comparing type-level numbers with respect to --- reflexivity, symmetry, and transitivity properties. --- -module Language.PureScript.TypeChecker.Entailment.IntCompare where - -import Protolude - -import Data.Graph qualified as G -import Data.Map qualified as M - -import Language.PureScript.Names qualified as P -import Language.PureScript.Types qualified as P -import Language.PureScript.Constants.Prim qualified as P - -data Relation a - = Equal a a - | LessThan a a - deriving (Functor, Show, Eq, Ord) - -type Context a = [Relation a] - -type PSOrdering = P.Qualified (P.ProperName 'P.TypeName) - --- Commentary: --- --- In essence, this solver builds a directed graph using the provided --- context, which is then used to determine the relationship between --- the two elements being compared. --- --- Given the context [a < b, b < c], we can infer that a < c as a --- path exists from a to c. Likewise, we can also infer that c > a --- as a path exists from c to a. --- --- ╔═══╗ ╔═══╗ ╔═══╗ --- ║ a ║ -> ║ b ║ -> ║ c ║ --- ╚═══╝ ╚═══╝ ╚═══╝ --- --- Introducing equality to the context augments the graph further, --- and it is represented by creating cycles between equal nodes. --- For example, [a < b, b < c, c = d] yields the following graph: --- --- ╔═══╗ ╔═══╗ ╔═══╗ ╔═══╗ --- ║ a ║ -> ║ b ║ -> ║ c ║ <-> ║ d ║ --- ╚═══╝ ╚═══╝ ╚═══╝ ╚═══╝ -solveRelation :: forall a. Ord a => Context a -> a -> a -> Maybe PSOrdering -solveRelation context lhs rhs = - if lhs == rhs then - pure P.EQ - else do - let (graph, search) = inequalities - lhs' <- search lhs - rhs' <- search rhs - case (G.path graph lhs' rhs', G.path graph rhs' lhs') of - (True, True) -> - pure P.EQ - (True, False) -> - pure P.LT - (False, True) -> - pure P.GT - _ -> - Nothing - where - inequalities :: (G.Graph, a -> Maybe G.Vertex) - inequalities = makeGraph $ clean $ foldMap convert context - where - convert :: Relation a -> [(a, [a])] - convert (Equal a b) = [(a, [b]), (b, [a])] - convert (LessThan a b) = [(a, [b]), (b, [])] - - makeGraph :: [(a, [a])] -> (G.Graph, a -> Maybe G.Vertex) - makeGraph m = - case G.graphFromEdges $ (\(a, b) -> (a, a, b)) <$> m of - (g, _, f) -> (g, f) - - clean :: forall k. Ord k => [(k, [k])] -> [(k, [k])] - clean = M.toList . M.fromListWith (<>) - -mkRelation :: P.Type a -> P.Type a -> P.Type a -> Maybe (Relation (P.Type a)) -mkRelation lhs rhs rel = case rel of - P.TypeConstructor _ ordering - | ordering == P.EQ -> pure $ Equal lhs rhs - | ordering == P.LT -> pure $ LessThan lhs rhs - | ordering == P.GT -> pure $ LessThan rhs lhs - _ -> - Nothing - -mkFacts :: [[P.Type a]] -> [Relation (P.Type a)] -mkFacts = mkRels [] . sort . findFacts - where - mkRels a [] = concat a - mkRels a (x : xs) = mkRels (map (LessThan x) xs : a) xs - - findFacts = mapMaybe $ \case - [P.TypeLevelInt _ _, P.TypeLevelInt _ _, _] -> - Nothing - [i@(P.TypeLevelInt _ _), _, _] -> - Just i - [_, i@(P.TypeLevelInt _ _), _] -> - Just i - _ -> - Nothing diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Kinds.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Kinds.hs deleted file mode 100644 index 681eb7f6..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Kinds.hs +++ /dev/null @@ -1,1021 +0,0 @@ --- | --- This module implements the kind checker --- -module Language.PureScript.TypeChecker.Kinds - ( kindOf - , kindOfWithUnknowns - , kindOfWithScopedVars - , kindOfData - , kindOfTypeSynonym - , kindOfClass - , kindsOfAll - , unifyKinds - , unifyKinds' - , subsumesKind - , instantiateKind - , checkKind - , inferKind - , elaborateKind - , checkConstraint - , checkInstanceDeclaration - , checkKindDeclaration - , checkTypeKind - , unknownsWithKinds - , freshKind - , freshKindWithKind - ) where - -import Prelude -import Protolude (headDef) - -import Control.Arrow ((***)) -import Control.Lens ((^.), _1, _2, _3) -import Control.Monad (join, unless, void, when, (<=<)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State (gets, modify) -import Control.Monad.Supply.Class (MonadSupply(..)) - -import Data.Bifunctor (first, second) -import Data.Bitraversable (bitraverse) -import Data.Foldable (for_, traverse_) -import Data.Function (on) -import Data.Functor (($>)) -import Data.IntSet qualified as IS -import Data.List (nubBy, sortOn, (\\)) -import Data.Map qualified as M -import Data.IntMap.Lazy qualified as IM -import Data.Maybe (fromJust, fromMaybe) -import Data.Text (Text) -import Data.Text qualified as T -import Data.Traversable (for) - -import Language.PureScript.Crash (HasCallStack, internalError) -import Language.PureScript.Environment qualified as E -import Language.PureScript.Errors -import Language.PureScript.Names (pattern ByNullSourcePos, ModuleName, Name(..), ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, mkQualified) -import Language.PureScript.TypeChecker.Monad (CheckState(..), Substitution(..), UnkLevel(..), Unknown, bindLocalTypeVariables, debugType, getEnv, lookupTypeVariable, unsafeCheckCurrentModule, withErrorMessageHint, withFreshSubstitution, TypeCheckM) -import Language.PureScript.TypeChecker.Skolems (newSkolemConstant, newSkolemScope, skolemize) -import Language.PureScript.TypeChecker.Synonyms (replaceAllTypeSynonyms) -import Language.PureScript.Types -import Language.PureScript.Pretty.Types (prettyPrintType) - -generalizeUnknowns :: [(Unknown, SourceType)] -> SourceType -> SourceType -generalizeUnknowns unks ty = - generalizeUnknownsWithVars (unknownVarNames (usedTypeVariables ty) unks) ty - -generalizeUnknownsWithVars :: [(Unknown, (Text, SourceType))] -> SourceType -> SourceType -generalizeUnknownsWithVars binders ty = - mkForAll ((getAnnForType ty,) . fmap (Just . replaceUnknownsWithVars binders) . snd <$> binders) . replaceUnknownsWithVars binders $ ty - -replaceUnknownsWithVars :: [(Unknown, (Text, a))] -> SourceType -> SourceType -replaceUnknownsWithVars binders ty - | null binders = ty - | otherwise = go ty - where - go :: SourceType -> SourceType - go = everywhereOnTypes $ \case - TUnknown ann unk | Just (name, _) <- lookup unk binders -> TypeVar ann name - other -> other - -unknownVarNames :: [Text] -> [(Unknown, SourceType)] -> [(Unknown, (Text, SourceType))] -unknownVarNames used unks = - zipWith (\(a, b) n -> (a, (n, b))) unks $ allVars \\ used - where - allVars :: [Text] - allVars - | [_] <- unks = "k" : vars - | otherwise = vars - - vars :: [Text] - vars = fmap (("k" <>) . T.pack . show) ([1..] :: [Int]) - -apply :: SourceType -> TypeCheckM SourceType -apply ty = flip substituteType ty <$> gets checkSubstitution - -substituteType :: Substitution -> SourceType -> SourceType -substituteType sub = everywhereOnTypes $ \case - TUnknown ann u -> - case IM.lookup u (substType sub) of - Nothing -> TUnknown ann u - Just (TUnknown ann' u1) | u1 == u -> TUnknown ann' u1 - Just t -> substituteType sub t - other -> - other - -freshUnknown :: TypeCheckM Unknown -freshUnknown = do - k <- gets checkNextType - modify $ \st -> st { checkNextType = k + 1 } - pure k - -freshKind :: SourceSpan -> TypeCheckM SourceType -freshKind ss = freshKindWithKind ss E.kindType - -freshKindWithKind :: SourceSpan -> SourceType -> TypeCheckM SourceType -freshKindWithKind ss kind = do - u <- freshUnknown - addUnsolved Nothing u kind - pure $ TUnknown (ss, []) u - -addUnsolved :: Maybe UnkLevel -> Unknown -> SourceType -> TypeCheckM () -addUnsolved lvl unk kind = modify $ \st -> do - let - newLvl = UnkLevel $ case lvl of - Nothing -> pure unk - Just (UnkLevel lvl') -> lvl' <> pure unk - subs = checkSubstitution st - uns = IM.insert unk (newLvl, kind) $ substUnsolved subs - st { checkSubstitution = subs { substUnsolved = uns } } - -solve :: Unknown -> SourceType -> TypeCheckM () -solve unk solution = modify $ \st -> do - let - subs = checkSubstitution st - tys = IM.insert unk solution $ substType subs - st { checkSubstitution = subs { substType = tys } } - -lookupUnsolved - :: HasCallStack - => Unknown - -> TypeCheckM (UnkLevel, SourceType) -lookupUnsolved u = do - uns <- gets (substUnsolved . checkSubstitution) - case IM.lookup u uns of - Nothing -> internalCompilerError $ "Unsolved unification variable ?" <> T.pack (show u) <> " is not bound" - Just res -> return res - -unknownsWithKinds - :: HasCallStack - => [Unknown] - -> TypeCheckM [(Unknown, SourceType)] -unknownsWithKinds = fmap (fmap snd . nubBy ((==) `on` fst) . sortOn fst . join) . traverse go - where - go u = do - (lvl, ty) <- traverse apply =<< lookupUnsolved u - rest <- fmap join . traverse go . IS.toList . unknowns $ ty - pure $ (lvl, (u, ty)) : rest - -inferKind - :: HasCallStack - => SourceType - -> TypeCheckM (SourceType, SourceType) -inferKind = \tyToInfer -> - withErrorMessageHint (ErrorInferringKind tyToInfer) - . rethrowWithPosition (fst $ getAnnForType tyToInfer) - $ go tyToInfer - where - go = \case - ty@(TypeConstructor ann v) -> do - env <- getEnv - case M.lookup v (E.types env) of - Nothing -> - throwError . errorMessage' (fst ann) . UnknownName . fmap TyName $ v - Just (kind, E.LocalTypeVariable) -> do - kind' <- apply kind - pure (ty, kind' $> ann) - Just (kind, _) -> do - pure (ty, kind $> ann) - ConstrainedType ann' con@(Constraint ann v _ _ _) ty -> do - env <- getEnv - con' <- case M.lookup (coerceProperName <$> v) (E.types env) of - Nothing -> - throwError . errorMessage' (fst ann) . UnknownName . fmap TyClassName $ v - Just _ -> - checkConstraint con - ty' <- checkIsSaturatedType ty - con'' <- applyConstraint con' - pure (ConstrainedType ann' con'' ty', E.kindType $> ann') - ty@(TypeLevelString ann _) -> - pure (ty, E.kindSymbol $> ann) - ty@(TypeLevelInt ann _) -> - pure (ty, E.tyInt $> ann) - ty@(TypeVar ann v) -> do - moduleName <- unsafeCheckCurrentModule - kind <- apply =<< lookupTypeVariable moduleName (Qualified ByNullSourcePos $ ProperName v) - pure (ty, kind $> ann) - ty@(Skolem ann _ mbK _ _) -> do - kind <- apply $ fromMaybe (internalError "Skolem has no kind") mbK - pure (ty, kind $> ann) - ty@(TUnknown ann u) -> do - kind <- apply . snd =<< lookupUnsolved u - pure (ty, kind $> ann) - ty@(TypeWildcard ann _) -> do - k <- freshKind (fst ann) - pure (ty, k $> ann) - ty@(REmpty ann) -> do - pure (ty, E.kindOfREmpty $> ann) - ty@(RCons ann _ _ _) | (rowList, rowTail) <- rowToList ty -> do - kr <- freshKind (fst ann) - rowList' <- for rowList $ \(RowListItem a lbl t) -> - RowListItem a lbl <$> checkKind t kr - rowTail' <- checkKind rowTail $ E.kindRow kr - kr' <- apply kr - pure (rowFromList (rowList', rowTail'), E.kindRow kr' $> ann) - TypeApp ann t1 t2 -> do - (t1', k1) <- go t1 - inferAppKind ann (t1', k1) t2 - KindApp ann t1 t2 -> do - (t1', kind) <- bitraverse pure apply =<< go t1 - case kind of - ForAll _ _ arg (Just argKind) resKind _ -> do - t2' <- checkKind t2 argKind - pure (KindApp ann t1' t2', replaceTypeVars arg t2' resKind) - _ -> - internalError "inferKind: unkinded forall binder" - KindedType _ t1 t2 -> do - t2' <- replaceAllTypeSynonyms . fst =<< go t2 - t1' <- checkKind t1 t2' - t2'' <- apply t2' - pure (t1', t2'') - ForAll ann vis arg mbKind ty sc -> do - moduleName <- unsafeCheckCurrentModule - kind <- case mbKind of - Just k -> replaceAllTypeSynonyms =<< checkIsSaturatedType k - Nothing -> freshKind (fst ann) - (ty', unks) <- bindLocalTypeVariables moduleName [(ProperName arg, kind)] $ do - ty' <- apply =<< checkIsSaturatedType ty - unks <- unknownsWithKinds . IS.toList $ unknowns ty' - pure (ty', unks) - for_ unks . uncurry $ addUnsolved Nothing - pure (ForAll ann vis arg (Just kind) ty' sc, E.kindType $> ann) - ParensInType _ ty -> - go ty - ty -> - internalError $ "inferKind: Unimplemented case \n" <> prettyPrintType 100 ty - -inferAppKind - :: HasCallStack - => SourceAnn - -> (SourceType, SourceType) - -> SourceType - -> TypeCheckM (SourceType, SourceType) -inferAppKind ann (fn, fnKind) arg = case fnKind of - TypeApp _ (TypeApp _ arrKind argKind) resKind | eqType arrKind E.tyFunction -> do - expandSynonyms <- requiresSynonymsToExpand fn - arg' <- checkKind' expandSynonyms arg argKind - (TypeApp ann fn arg',) <$> apply resKind - TUnknown _ u -> do - (lvl, _) <- lookupUnsolved u - u1 <- freshUnknown - u2 <- freshUnknown - addUnsolved (Just lvl) u1 E.kindType - addUnsolved (Just lvl) u2 E.kindType - solve u $ (TUnknown ann u1 E.-:> TUnknown ann u2) $> ann - arg' <- checkKind arg $ TUnknown ann u1 - pure (TypeApp ann fn arg', TUnknown ann u2) - ForAll _ _ a (Just k) ty _ -> do - u <- freshUnknown - addUnsolved Nothing u k - inferAppKind ann (KindApp ann fn (TUnknown ann u), replaceTypeVars a (TUnknown ann u) ty) arg - _ -> - cannotApplyTypeToType fn arg - where - requiresSynonymsToExpand = \case - TypeConstructor _ v -> M.notMember v . E.typeSynonyms <$> getEnv - TypeApp _ l _ -> requiresSynonymsToExpand l - KindApp _ l _ -> requiresSynonymsToExpand l - _ -> pure True - -cannotApplyTypeToType - :: HasCallStack - => SourceType - -> SourceType - -> TypeCheckM a -cannotApplyTypeToType fn arg = do - argKind <- snd <$> inferKind arg - _ <- checkKind fn . srcTypeApp (srcTypeApp E.tyFunction argKind) =<< freshKind nullSourceSpan - internalCompilerError . T.pack $ "Cannot apply type to type: " <> debugType (srcTypeApp fn arg) - -cannotApplyKindToType - :: HasCallStack - => SourceType - -> SourceType - -> TypeCheckM a -cannotApplyKindToType poly arg = do - let ann = getAnnForType arg - argKind <- snd <$> inferKind arg - _ <- checkKind poly . mkForAll [(ann, ("k", Just argKind))] =<< freshKind nullSourceSpan - internalCompilerError . T.pack $ "Cannot apply kind to type: " <> debugType (srcKindApp poly arg) - -checkKind - :: HasCallStack - => SourceType - -> SourceType - -> TypeCheckM SourceType -checkKind = checkKind' False - --- | `checkIsSaturatedType t` is identical to `checkKind t E.kindType` except --- that the former checks that the type synonyms in `t` expand completely. This --- is the appropriate function to use when expanding the types of type --- parameter kinds, arguments to data constructors, etc., in order for the --- PartiallyAppliedSynonym error to take precedence over the KindsDoNotUnify --- error. --- -checkIsSaturatedType - :: HasCallStack - => SourceType - -> TypeCheckM SourceType -checkIsSaturatedType ty = checkKind' True ty E.kindType - -checkKind' - :: HasCallStack - => Bool - -> SourceType - -> SourceType - -> TypeCheckM SourceType -checkKind' requireSynonymsToExpand ty kind2 = do - withErrorMessageHint (ErrorCheckingKind ty kind2) - . rethrowWithPosition (fst $ getAnnForType ty) $ do - (ty', kind1) <- inferKind ty - kind1' <- apply kind1 - kind2' <- apply kind2 - when requireSynonymsToExpand $ void $ replaceAllTypeSynonyms ty' - instantiateKind (ty', kind1') kind2' - -instantiateKind - :: HasCallStack - => (SourceType, SourceType) - -> SourceType - -> TypeCheckM SourceType -instantiateKind (ty, kind1) kind2 = case kind1 of - ForAll _ _ a (Just k) t _ | shouldInstantiate kind2 -> do - let ann = getAnnForType ty - u <- freshKindWithKind (fst ann) k - instantiateKind (KindApp ann ty u, replaceTypeVars a u t) kind2 - _ -> do - subsumesKind kind1 kind2 - pure ty - where - shouldInstantiate = not . \case - ForAll _ _ _ _ _ _ -> True - _ -> False - -subsumesKind - :: HasCallStack - => SourceType - -> SourceType - -> TypeCheckM () -subsumesKind = go - where - go = curry $ \case - (TypeApp _ (TypeApp _ arr1 a1) a2, TypeApp _ (TypeApp _ arr2 b1) b2) - | eqType arr1 E.tyFunction - , eqType arr2 E.tyFunction -> do - go b1 a1 - join $ go <$> apply a2 <*> apply b2 - (a, ForAll ann _ var mbKind b mbScope) -> do - scope <- maybe newSkolemScope pure mbScope - skolc <- newSkolemConstant - go a $ skolemize ann var mbKind skolc scope b - (ForAll ann _ var (Just kind) a _, b) -> do - a' <- freshKindWithKind (fst ann) kind - go (replaceTypeVars var a' a) b - (TUnknown ann u, b@(TypeApp _ (TypeApp _ arr _) _)) - | eqType arr E.tyFunction - , IS.notMember u (unknowns b) -> - join $ go <$> solveUnknownAsFunction ann u <*> pure b - (a@(TypeApp _ (TypeApp _ arr _) _), TUnknown ann u) - | eqType arr E.tyFunction - , IS.notMember u (unknowns a) -> - join $ go <$> pure a <*> solveUnknownAsFunction ann u - (a, b) -> - unifyKinds a b - -unifyKinds - :: SourceType - -> SourceType - -> TypeCheckM () -unifyKinds = unifyKindsWithFailure $ \w1 w2 -> - throwError - . errorMessage''' (fst . getAnnForType <$> [w1, w2]) - $ KindsDoNotUnify w1 w2 - --- | Does not attach positions to the error node, instead relies on the --- | local position context. This is useful when invoking kind unification --- | outside of kind checker internals. -unifyKinds' - :: HasCallStack - => SourceType - -> SourceType - -> TypeCheckM () -unifyKinds' = unifyKindsWithFailure $ \w1 w2 -> - throwError - . errorMessage - $ KindsDoNotUnify w1 w2 - --- | Check the kind of a type, failing if it is not of kind *. -checkTypeKind - :: HasCallStack - => SourceType - -> SourceType - -> TypeCheckM () -checkTypeKind ty kind = - unifyKindsWithFailure (\_ _ -> throwError . errorMessage $ ExpectedType ty kind) kind E.kindType - -unifyKindsWithFailure - :: HasCallStack - => (SourceType -> SourceType -> TypeCheckM ()) - -> SourceType - -> SourceType - -> TypeCheckM () -unifyKindsWithFailure onFailure = go - where - goWithLabel l t1 t2 = withErrorMessageHint (ErrorInRowLabel l) $ go t1 t2 - go = curry $ \case - (TypeApp _ p1 p2, TypeApp _ p3 p4) -> do - go p1 p3 - join $ go <$> apply p2 <*> apply p4 - (KindApp _ p1 p2, KindApp _ p3 p4) -> do - go p1 p3 - join $ go <$> apply p2 <*> apply p4 - (r1@(RCons _ _ _ _), r2) -> - unifyRows r1 r2 - (r1, r2@(RCons _ _ _ _)) -> - unifyRows r1 r2 - (r1@(REmpty _), r2) -> - unifyRows r1 r2 - (r1, r2@(REmpty _)) -> - unifyRows r1 r2 - (w1, w2) | eqType w1 w2 -> - pure () - (TUnknown _ a', p1) -> - solveUnknown a' p1 - (p1, TUnknown _ a') -> - solveUnknown a' p1 - (w1, w2) -> - onFailure w1 w2 - - unifyRows r1 r2 = do - let (matches, rest) = alignRowsWith goWithLabel r1 r2 - sequence_ matches - unifyTails rest - - unifyTails = \case - (([], TUnknown _ a'), (rs, p1)) -> - solveUnknown a' $ rowFromList (rs, p1) - ((rs, p1), ([], TUnknown _ a')) -> - solveUnknown a' $ rowFromList (rs, p1) - (([], w1), ([], w2)) | eqType w1 w2 -> - pure () - ((rs1, TUnknown _ u1), (rs2, TUnknown _ u2)) | u1 /= u2 -> do - rest <- freshKind nullSourceSpan - solveUnknown u1 $ rowFromList (rs2, rest) - solveUnknown u2 $ rowFromList (rs1, rest) - (w1, w2) -> - onFailure (rowFromList w1) (rowFromList w2) - -solveUnknown - :: HasCallStack - => Unknown - -> SourceType - -> TypeCheckM () -solveUnknown a' p1 = do - p2 <- promoteKind a' p1 - w1 <- snd <$> lookupUnsolved a' - join $ unifyKinds <$> apply w1 <*> elaborateKind p2 - solve a' p2 - -solveUnknownAsFunction - :: HasCallStack - => SourceAnn - -> Unknown - -> TypeCheckM SourceType -solveUnknownAsFunction ann u = do - lvl <- fst <$> lookupUnsolved u - u1 <- freshUnknown - u2 <- freshUnknown - addUnsolved (Just lvl) u1 E.kindType - addUnsolved (Just lvl) u2 E.kindType - let uarr = (TUnknown ann u1 E.-:> TUnknown ann u2) $> ann - solve u uarr - pure uarr - -promoteKind - :: HasCallStack - => Unknown - -> SourceType - -> TypeCheckM SourceType -promoteKind u2 ty = do - lvl2 <- fst <$> lookupUnsolved u2 - flip everywhereOnTypesM ty $ \case - ty'@(TUnknown ann u1) -> do - when (u1 == u2) . throwError . errorMessage . InfiniteKind $ ty - (lvl1, k) <- lookupUnsolved u1 - if lvl1 < lvl2 then - pure ty' - else do - k' <- promoteKind u2 =<< apply k - u1' <- freshUnknown - addUnsolved (Just lvl2) u1' k' - solve u1 $ TUnknown ann u1' - pure $ TUnknown ann u1' - ty' -> - pure ty' - -elaborateKind - :: HasCallStack - => SourceType - -> TypeCheckM SourceType -elaborateKind = \case - TypeLevelString ann _ -> - pure $ E.kindSymbol $> ann - TypeLevelInt ann _ -> - pure $ E.tyInt $> ann - TypeConstructor ann v -> do - env <- getEnv - case M.lookup v (E.types env) of - Nothing -> - throwError . errorMessage' (fst ann) . UnknownName . fmap TyName $ v - Just (kind, _) -> - ($> ann) <$> apply kind - TypeVar ann a -> do - moduleName <- unsafeCheckCurrentModule - kind <- apply =<< lookupTypeVariable moduleName (Qualified ByNullSourcePos $ ProperName a) - pure (kind $> ann) - (Skolem ann _ mbK _ _) -> do - kind <- apply $ fromMaybe (internalError "Skolem has no kind") mbK - pure $ kind $> ann - TUnknown ann a' -> do - kind <- snd <$> lookupUnsolved a' - ($> ann) <$> apply kind - REmpty ann -> do - pure $ E.kindOfREmpty $> ann - RCons ann _ t1 _ -> do - k1 <- elaborateKind t1 - pure $ E.kindRow k1 $> ann - ty@(TypeApp ann t1 t2) -> do - k1 <- elaborateKind t1 - case k1 of - TypeApp _ (TypeApp _ k _) w2 | eqType k E.tyFunction -> do - pure $ w2 $> ann - -- Normally we wouldn't unify in `elaborateKind`, since an unknown should - -- always have a known kind. However, since type holes are fully inference - -- driven, they are unknowns with unknown kinds, which may require some - -- late unification here. - TUnknown a u -> do - _ <- solveUnknownAsFunction a u - elaborateKind ty - _ -> - cannotApplyTypeToType t1 t2 - KindApp ann t1 t2 -> do - k1 <- elaborateKind t1 - case k1 of - ForAll _ _ a _ n _ -> do - flip (replaceTypeVars a) n . ($> ann) <$> apply t2 - _ -> - cannotApplyKindToType t1 t2 - ForAll ann _ _ _ _ _ -> do - pure $ E.kindType $> ann - ConstrainedType ann _ _ -> - pure $ E.kindType $> ann - KindedType ann _ k -> - pure $ k $> ann - ty -> - throwError . errorMessage' (fst (getAnnForType ty)) $ UnsupportedTypeInKind ty - -checkEscapedSkolems :: SourceType -> TypeCheckM () -checkEscapedSkolems ty = - traverse_ (throwError . toSkolemError) - . everythingWithContextOnTypes ty [] (<>) go - $ ty - where - go :: SourceType -> SourceType -> (SourceType, [(SourceSpan, Text, SourceType)]) - go ty' = \case - Skolem ss name _ _ _ -> (ty', [(fst ss, name, ty')]) - ty''@(KindApp _ _ _) -> (ty'', []) - _ -> (ty', []) - - toSkolemError (ss, name, ty') = - errorMessage' (fst $ getAnnForType ty') $ EscapedSkolem name (Just ss) ty' - -kindOfWithUnknowns - :: HasCallStack - => SourceType - -> TypeCheckM (([(Unknown, SourceType)], SourceType), SourceType) -kindOfWithUnknowns ty = do - (ty', kind) <- kindOf ty - unks <- unknownsWithKinds . IS.toList $ unknowns ty' - pure ((unks, ty'), kind) - --- | Infer the kind of a single type -kindOf - :: HasCallStack - => SourceType - -> TypeCheckM (SourceType, SourceType) -kindOf = fmap (first snd) . kindOfWithScopedVars - --- | Infer the kind of a single type, returning the kinds of any scoped type variables -kindOfWithScopedVars - :: HasCallStack - => SourceType - -> TypeCheckM (([(Text, SourceType)], SourceType), SourceType) -kindOfWithScopedVars ty = do - (ty', kind) <- bitraverse apply (replaceAllTypeSynonyms <=< apply) =<< inferKind ty - let binders = fst . fromJust $ completeBinderList ty' - pure ((snd <$> binders, ty'), kind) - -type DataDeclarationArgs = - ( SourceAnn - , ProperName 'TypeName - , [(Text, Maybe SourceType)] - , [DataConstructorDeclaration] - ) - -type DataDeclarationResult = - ( [(DataConstructorDeclaration, SourceType)] - -- The infered type signatures of data constructors - , SourceType - -- The inferred kind of the declaration - ) - -kindOfData - :: - ModuleName - -> DataDeclarationArgs - -> TypeCheckM DataDeclarationResult -kindOfData moduleName dataDecl = - headDef (internalError "kindOfData: empty list") . (^. _2) <$> kindsOfAll moduleName [] [dataDecl] [] - -inferDataDeclaration - :: - ModuleName - -> DataDeclarationArgs - -> TypeCheckM [(DataConstructorDeclaration, SourceType)] -inferDataDeclaration moduleName (ann, tyName, tyArgs, ctors) = do - tyKind <- apply =<< lookupTypeVariable moduleName (Qualified ByNullSourcePos tyName) - let (sigBinders, tyKind') = fromJust . completeBinderList $ tyKind - bindLocalTypeVariables moduleName (first ProperName . snd <$> sigBinders) $ do - tyArgs' <- for tyArgs . traverse . maybe (freshKind (fst ann)) $ replaceAllTypeSynonyms <=< apply <=< checkIsSaturatedType - subsumesKind (foldr ((E.-:>) . snd) E.kindType tyArgs') tyKind' - bindLocalTypeVariables moduleName (first ProperName <$> tyArgs') $ do - let tyCtorName = srcTypeConstructor $ mkQualified tyName moduleName - tyCtor = foldl (\ty -> srcKindApp ty . srcTypeVar . fst . snd) tyCtorName sigBinders - tyCtor' = foldl (\ty -> srcTypeApp ty . srcTypeVar . fst) tyCtor tyArgs' - ctorBinders = fmap (fmap (fmap Just)) $ sigBinders <> fmap (nullSourceAnn,) tyArgs' - visibility = second (const TypeVarVisible) <$> tyArgs - for ctors $ - fmap (fmap (addVisibility visibility . mkForAll ctorBinders)) . inferDataConstructor tyCtor' - -inferDataConstructor - :: - SourceType - -> DataConstructorDeclaration - -> TypeCheckM (DataConstructorDeclaration, SourceType) -inferDataConstructor tyCtor DataConstructorDeclaration{..} = do - dataCtorFields' <- traverse (traverse checkIsSaturatedType) dataCtorFields - dataCtor <- flip (foldr ((E.-:>) . snd)) dataCtorFields' <$> checkKind tyCtor E.kindType - pure ( DataConstructorDeclaration { dataCtorFields = dataCtorFields', .. }, dataCtor ) - -type TypeDeclarationArgs = - ( SourceAnn - , ProperName 'TypeName - , [(Text, Maybe SourceType)] - , SourceType - ) - -type TypeDeclarationResult = - ( SourceType - -- The elaborated rhs of the declaration - , SourceType - -- The inferred kind of the declaration - ) - -kindOfTypeSynonym - :: - ModuleName - -> TypeDeclarationArgs - -> TypeCheckM TypeDeclarationResult -kindOfTypeSynonym moduleName typeDecl = - headDef (internalError "kindOfTypeSynonym: empty list") . (^. _1) <$> kindsOfAll moduleName [typeDecl] [] [] - -inferTypeSynonym - :: - ModuleName - -> TypeDeclarationArgs - -> TypeCheckM SourceType -inferTypeSynonym moduleName (ann, tyName, tyArgs, tyBody) = do - tyKind <- apply =<< lookupTypeVariable moduleName (Qualified ByNullSourcePos tyName) - let (sigBinders, tyKind') = fromJust . completeBinderList $ tyKind - bindLocalTypeVariables moduleName (first ProperName . snd <$> sigBinders) $ do - kindRes <- freshKind (fst ann) - tyArgs' <- for tyArgs . traverse . maybe (freshKind (fst ann)) $ replaceAllTypeSynonyms <=< apply <=< checkIsSaturatedType - unifyKinds tyKind' $ foldr ((E.-:>) . snd) kindRes tyArgs' - bindLocalTypeVariables moduleName (first ProperName <$> tyArgs') $ do - tyBodyAndKind <- traverse apply =<< inferKind tyBody - instantiateKind tyBodyAndKind =<< apply kindRes - --- | Checks that a particular generalization is valid and well-scoped. --- | Implicitly generalized kinds are always elaborated before explicitly --- | quantified type variables. It's possible that such a kind can be --- | inserted before other variables that it depends on, making it --- | ill-scoped. We require that users explicitly generalize this kind --- | in such a case. -checkQuantification - :: - SourceType - -> TypeCheckM () -checkQuantification = - collectErrors . go [] [] . fst . fromJust . completeBinderList - where - collectErrors vars = - unless (null vars) - . throwError - . foldMap (\(ann, arg) -> errorMessage' (fst ann) $ QuantificationCheckFailureInKind arg) - $ vars - - go acc _ [] = reverse acc - go acc sco ((_, (arg, k)) : rest) - | not . all (flip elem sco) $ freeTypeVariables k = goDeps acc arg rest - | otherwise = go acc (arg : sco) rest - - goDeps acc _ [] = acc - goDeps acc karg ((ann, (arg, k)) : rest) - | isDep && arg == karg = (ann, arg) : acc - | isDep = goDeps ((ann, arg) : acc) karg rest - | otherwise = goDeps acc karg rest - where - isDep = - elem karg $ freeTypeVariables k - -checkVisibleTypeQuantification - :: - SourceType - -> TypeCheckM () -checkVisibleTypeQuantification = - collectErrors . freeTypeVariables - where - collectErrors vars = - unless (null vars) - . throwError - . foldMap (errorMessage . VisibleQuantificationCheckFailureInType) - $ vars - --- | Checks that there are no remaining unknowns in a type, and if so --- | throws an error. This is necessary for contexts where we can't --- | implicitly generalize unknowns, such as on the right-hand-side of --- | a type synonym, or in arguments to data constructors. -checkTypeQuantification - :: - SourceType - -> TypeCheckM () -checkTypeQuantification = - collectErrors . everythingWithContextOnTypes True [] (<>) unknownsInKinds - where - collectErrors tysWithUnks = - unless (null tysWithUnks) . throwError . foldMap toMultipleErrors $ tysWithUnks - - toMultipleErrors (ss, unks, ty) = - errorMessage' ss $ QuantificationCheckFailureInType (IS.toList unks) ty - - unknownsInKinds False _ = (False, []) - unknownsInKinds _ ty = case ty of - ForAll sa _ _ _ _ _ | unks <- unknowns ty, not (IS.null unks) -> - (False, [(fst sa, unks, ty)]) - KindApp sa _ _ | unks <- unknowns ty, not (IS.null unks) -> - (False, [(fst sa, unks, ty)]) - ConstrainedType sa _ _ | unks <- unknowns ty, not (IS.null unks) -> - (False, [(fst sa, unks, ty)]) - _ -> - (True, []) - -type ClassDeclarationArgs = - ( SourceAnn - , ProperName 'ClassName - , [(Text, Maybe SourceType)] - , [SourceConstraint] - , [Declaration] - ) - -type ClassDeclarationResult = - ( [(Text, SourceType)] - -- The kind annotated class arguments - , [SourceConstraint] - -- The kind annotated superclass constraints - , [Declaration] - -- The kind annotated declarations - , SourceType - -- The inferred kind of the declaration - ) - -kindOfClass - :: - ModuleName - -> ClassDeclarationArgs - -> TypeCheckM ClassDeclarationResult -kindOfClass moduleName clsDecl = - headDef (internalError "kindOfClass: empty list") . (^. _3) <$> kindsOfAll moduleName [] [] [clsDecl] - -inferClassDeclaration - :: - ModuleName - -> ClassDeclarationArgs - -> TypeCheckM ([(Text, SourceType)], [SourceConstraint], [Declaration]) -inferClassDeclaration moduleName (ann, clsName, clsArgs, superClasses, decls) = do - clsKind <- apply =<< lookupTypeVariable moduleName (Qualified ByNullSourcePos $ coerceProperName clsName) - let (sigBinders, clsKind') = fromJust . completeBinderList $ clsKind - bindLocalTypeVariables moduleName (first ProperName . snd <$> sigBinders) $ do - clsArgs' <- for clsArgs . traverse . maybe (freshKind (fst ann)) $ replaceAllTypeSynonyms <=< apply <=< checkIsSaturatedType - unifyKinds clsKind' $ foldr ((E.-:>) . snd) E.kindConstraint clsArgs' - bindLocalTypeVariables moduleName (first ProperName <$> clsArgs') $ do - (clsArgs',,) - <$> for superClasses checkConstraint - <*> for decls checkClassMemberDeclaration - -checkClassMemberDeclaration - :: - Declaration - -> TypeCheckM Declaration -checkClassMemberDeclaration = \case - TypeDeclaration (TypeDeclarationData ann ident ty) -> - TypeDeclaration . TypeDeclarationData ann ident <$> checkKind ty E.kindType - _ -> internalError "Invalid class member declaration" - -applyClassMemberDeclaration - :: - Declaration - -> TypeCheckM Declaration -applyClassMemberDeclaration = \case - TypeDeclaration (TypeDeclarationData ann ident ty) -> - TypeDeclaration . TypeDeclarationData ann ident <$> apply ty - _ -> internalError "Invalid class member declaration" - -mapTypeDeclaration :: (SourceType -> SourceType) -> Declaration -> Declaration -mapTypeDeclaration f = \case - TypeDeclaration (TypeDeclarationData ann ident ty) -> - TypeDeclaration . TypeDeclarationData ann ident $ f ty - other -> - other - -checkConstraint - :: - SourceConstraint - -> TypeCheckM SourceConstraint -checkConstraint (Constraint ann clsName kinds args dat) = do - let ty = foldl (TypeApp ann) (foldl (KindApp ann) (TypeConstructor ann (fmap coerceProperName clsName)) kinds) args - (_, kinds', args') <- unapplyTypes <$> checkKind ty E.kindConstraint - pure $ Constraint ann clsName kinds' args' dat - -applyConstraint - :: - SourceConstraint - -> TypeCheckM SourceConstraint -applyConstraint (Constraint ann clsName kinds args dat) = do - let ty = foldl (TypeApp ann) (foldl (KindApp ann) (TypeConstructor ann (fmap coerceProperName clsName)) kinds) args - (_, kinds', args') <- unapplyTypes <$> apply ty - pure $ Constraint ann clsName kinds' args' dat - -type InstanceDeclarationArgs = - ( SourceAnn - , [SourceConstraint] - , Qualified (ProperName 'ClassName) - , [SourceType] - ) - -type InstanceDeclarationResult = - ( [SourceConstraint] - , [SourceType] - , [SourceType] - , [(Text, SourceType)] - ) - -checkInstanceDeclaration - :: - ModuleName - -> InstanceDeclarationArgs - -> TypeCheckM InstanceDeclarationResult -checkInstanceDeclaration moduleName (ann, constraints, clsName, args) = do - let ty = foldl (TypeApp ann) (TypeConstructor ann (fmap coerceProperName clsName)) args - tyWithConstraints = foldr srcConstrainedType ty constraints - freeVars = freeTypeVariables tyWithConstraints - freeVarsDict <- for freeVars $ \v -> (ProperName v,) <$> freshKind (fst ann) - bindLocalTypeVariables moduleName freeVarsDict $ do - ty' <- checkKind ty E.kindConstraint - constraints' <- for constraints checkConstraint - allTy <- apply $ foldr srcConstrainedType ty' constraints' - allUnknowns <- unknownsWithKinds . IS.toList . foldMap unknowns . (allTy :) =<< traverse (apply . snd) freeVarsDict - let unknownVars = unknownVarNames (usedTypeVariables allTy) allUnknowns - let allWithVars = replaceUnknownsWithVars unknownVars allTy - let (allConstraints, (_, allKinds, allArgs)) = unapplyTypes <$> unapplyConstraints allWithVars - varKinds <- traverse (traverse (fmap (replaceUnknownsWithVars unknownVars) . apply)) $ (snd <$> unknownVars) <> (first runProperName <$> freeVarsDict) - pure (allConstraints, allKinds, allArgs, varKinds) - -checkKindDeclaration - :: - ModuleName - -> SourceType - -> TypeCheckM SourceType -checkKindDeclaration _ ty = do - (ty', kind) <- kindOf ty - checkTypeKind kind E.kindType - ty'' <- replaceAllTypeSynonyms ty' - unks <- unknownsWithKinds . IS.toList $ unknowns ty'' - finalTy <- generalizeUnknowns unks <$> freshenForAlls ty' ty'' - checkQuantification finalTy - checkValidKind finalTy - where - -- When expanding type synonyms and generalizing, we need to generate more - -- unique names so that they don't clash or shadow other names, or can - -- be referenced (easily). - freshVar arg = (arg <>) . T.pack . show <$> fresh - freshenForAlls = curry $ \case - (ForAll _ _ v1 _ ty1 _, ForAll a2 vis v2 k2 ty2 sc2) | v1 == v2 -> do - ty2' <- freshenForAlls ty1 ty2 - pure $ ForAll a2 vis v2 k2 ty2' sc2 - (_, ty2) -> go ty2 where - go = \case - ForAll a' vis v' k' ty' sc' -> do - v'' <- freshVar v' - ty'' <- go (replaceTypeVars v' (TypeVar a' v'') ty') - pure $ ForAll a' vis v'' k' ty'' sc' - other -> pure other - - checkValidKind = everywhereOnTypesM $ \case - ty'@(ConstrainedType ann _ _) -> - throwError . errorMessage' (fst ann) $ UnsupportedTypeInKind ty' - other -> pure other - -existingSignatureOrFreshKind - :: - ModuleName - -> SourceSpan - -> ProperName 'TypeName - -> TypeCheckM SourceType -existingSignatureOrFreshKind moduleName ss name = do - env <- getEnv - case M.lookup (Qualified (ByModuleName moduleName) name) (E.types env) of - Nothing -> freshKind ss - Just (kind, _) -> pure kind - -kindsOfAll - :: - ModuleName - -> [TypeDeclarationArgs] - -> [DataDeclarationArgs] - -> [ClassDeclarationArgs] - -> TypeCheckM ([TypeDeclarationResult], [DataDeclarationResult], [ClassDeclarationResult]) -kindsOfAll moduleName syns dats clss = withFreshSubstitution $ do - synDict <- for syns $ \(sa, synName, _, _) -> (synName,) <$> existingSignatureOrFreshKind moduleName (fst sa) synName - datDict <- for dats $ \(sa, datName, _, _) -> (datName,) <$> existingSignatureOrFreshKind moduleName (fst sa) datName - clsDict <- for clss $ \(sa, clsName, _, _, _) -> fmap (coerceProperName clsName,) $ existingSignatureOrFreshKind moduleName (fst sa) $ coerceProperName clsName - let bindingGroup = synDict <> datDict <> clsDict - bindLocalTypeVariables moduleName bindingGroup $ do - synResults <- for syns (inferTypeSynonym moduleName) - datResults <- for dats (inferDataDeclaration moduleName) - clsResults <- for clss (inferClassDeclaration moduleName) - synResultsWithUnks <- for (zip synDict synResults) $ \((synName, synKind), synBody) -> do - synKind' <- apply synKind - synBody' <- apply synBody - pure (((synName, synKind'), synBody'), unknowns synKind') - datResultsWithUnks <- for (zip datDict datResults) $ \((datName, datKind), ctors) -> do - datKind' <- apply datKind - ctors' <- traverse (bitraverse (traverseDataCtorFields (traverse (traverse apply))) apply) ctors - pure (((datName, datKind'), ctors'), unknowns datKind') - clsResultsWithUnks <- for (zip clsDict clsResults) $ \((clsName, clsKind), (args, supers, decls)) -> do - clsKind' <- apply clsKind - args' <- traverse (traverse apply) args - supers' <- traverse applyConstraint supers - decls' <- traverse applyClassMemberDeclaration decls - pure (((clsName, clsKind'), (args', supers', decls')), unknowns clsKind') - let synUnks = fmap (\(((synName, _), _), unks) -> (synName, unks)) synResultsWithUnks - datUnks = fmap (\(((datName, _), _), unks) -> (datName, unks)) datResultsWithUnks - clsUnks = fmap (\(((clsName, _), _), unks) -> (clsName, unks)) clsResultsWithUnks - tysUnks = synUnks <> datUnks <> clsUnks - allUnks <- unknownsWithKinds . IS.toList $ foldMap snd tysUnks - let mkTySub (name, unks) = do - let tyCtorName = mkQualified name moduleName - tyUnks = filter (flip IS.member unks . fst) allUnks - tyCtor = foldl (\ty -> srcKindApp ty . TUnknown nullSourceAnn . fst) (srcTypeConstructor tyCtorName) tyUnks - (tyCtorName, (tyCtor, tyUnks)) - tySubs = fmap mkTySub tysUnks - replaceTypeCtors = everywhereOnTypes $ \case - TypeConstructor _ name - | Just (tyCtor, _) <- lookup name tySubs -> tyCtor - other -> other - clsResultsWithKinds = flip fmap clsResultsWithUnks $ \(((clsName, clsKind), (args, supers, decls)), _) -> do - let tyUnks = snd . fromJust $ lookup (mkQualified clsName moduleName) tySubs - (usedTypeVariablesInDecls, _, _, _, _) = accumTypes usedTypeVariables - usedVars = usedTypeVariables clsKind - <> foldMap (usedTypeVariables . snd) args - <> foldMap (foldMap usedTypeVariables . (\c -> constraintKindArgs c <> constraintArgs c)) supers - <> foldMap usedTypeVariablesInDecls decls - unkBinders = unknownVarNames usedVars tyUnks - args' = fmap (replaceUnknownsWithVars unkBinders . replaceTypeCtors) <$> args - supers' = mapConstraintArgsAll (fmap (replaceUnknownsWithVars unkBinders . replaceTypeCtors)) <$> supers - decls' = mapTypeDeclaration (replaceUnknownsWithVars unkBinders . replaceTypeCtors) <$> decls - (args', supers', decls', generalizeUnknownsWithVars unkBinders clsKind) - datResultsWithKinds <- for datResultsWithUnks $ \(((datName, datKind), ctors), _) -> do - let tyUnks = snd . fromJust $ lookup (mkQualified datName moduleName) tySubs - replaceDataCtorField ty = replaceUnknownsWithVars (unknownVarNames (usedTypeVariables ty) tyUnks) $ replaceTypeCtors ty - ctors' = fmap (mapDataCtorFields (fmap (fmap replaceDataCtorField)) *** generalizeUnknowns tyUnks . replaceTypeCtors) ctors - traverse_ (traverse_ checkTypeQuantification) ctors' - pure (ctors', generalizeUnknowns tyUnks datKind) - synResultsWithKinds <- for synResultsWithUnks $ \(((synName, synKind), synBody), _) -> do - let tyUnks = snd . fromJust $ lookup (mkQualified synName moduleName) tySubs - unkBinders = unknownVarNames (usedTypeVariables synKind <> usedTypeVariables synBody) tyUnks - genBody = replaceUnknownsWithVars unkBinders $ replaceTypeCtors synBody - genSig = generalizeUnknownsWithVars unkBinders synKind - checkEscapedSkolems genBody - checkTypeQuantification genBody - checkVisibleTypeQuantification genSig - pure (genBody, genSig) - pure (synResultsWithKinds, datResultsWithKinds, clsResultsWithKinds) diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Monad.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Monad.hs deleted file mode 100644 index dbcd7808..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Monad.hs +++ /dev/null @@ -1,496 +0,0 @@ -{-# LANGUAGE GADTs #-} - --- | --- Monads for type checking and type inference and associated data types --- -module Language.PureScript.TypeChecker.Monad where - -import Prelude - -import Control.Arrow (second) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State (MonadState(..), StateT(..), gets, modify) -import Control.Monad.State.Strict qualified as StrictState - -import Data.Maybe (fromMaybe) -import Data.IntMap.Lazy qualified as IM -import Data.Map qualified as M -import Data.Set qualified as S -import Data.Text (Text, isPrefixOf, unpack) -import Data.List.NonEmpty qualified as NEL - -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (Environment(..), NameKind(..), NameVisibility(..), TypeClassData(..), TypeKind(..)) -import Language.PureScript.Errors (Context, ErrorMessageHint, ExportSource, Expr, ImportDeclarationType, MultipleErrors, SimpleErrorMessage(..), SourceAnn, SourceSpan(..), addHint, errorMessage, positionedError, rethrow, warnWithPosition) -import Language.PureScript.Names (Ident(..), ModuleName, ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, disqualify, runIdent, runModuleName, showQualified, toMaybeModuleName) -import Language.PureScript.Pretty.Types (prettyPrintType) -import Language.PureScript.Pretty.Values (prettyPrintValue) -import Language.PureScript.TypeClassDictionaries (NamedDict, TypeClassDictionaryInScope(..)) -import Language.PureScript.Types (Constraint(..), SourceType, Type(..), srcKindedType, srcTypeVar) -import Text.PrettyPrint.Boxes (render) -import Control.Monad.Supply (SupplyT (unSupplyT)) -import Control.Monad.Supply.Class (MonadSupply) -import Control.Monad.Except (ExceptT, runExceptT) -import Control.Monad.Trans.Writer.CPS qualified as SW -import Control.Monad.Writer (MonadWriter(..), censor) -import Control.Monad.Supply.Class qualified as Supply -import Control.Monad.Identity (Identity(runIdentity)) -import Control.Monad (forM_, when, join, (<=<), guard) - -newtype TypeCheckM a = TypeCheckM { unTypeCheckM :: StateT CheckState (SupplyT (ExceptT MultipleErrors (SW.Writer MultipleErrors))) a } - deriving newtype (Functor, Applicative, Monad, MonadSupply, MonadState CheckState, MonadWriter MultipleErrors, MonadError MultipleErrors) - --- | Lift a TypeCheckM computation into another monad that satisfies all its constraints -liftTypeCheckM :: - (MonadSupply m, MonadError MultipleErrors m, MonadState CheckState m, MonadWriter MultipleErrors m) => - TypeCheckM a -> m a -liftTypeCheckM (TypeCheckM m) = do - st <- get - freshId <- Supply.peek - let (result, errors) = runIdentity $ SW.runWriterT $ runExceptT $ flip StrictState.runStateT freshId $ unSupplyT $ runStateT m st - tell errors - case result of - Left err -> - throwError err - Right ((a, st'), freshId') -> do - put st' - Supply.consumeUpTo freshId' - return a - -newtype UnkLevel = UnkLevel (NEL.NonEmpty Unknown) - deriving (Eq, Show) - --- This instance differs from the NEL instance in that longer but otherwise --- equal paths are LT rather than GT. An extended path puts it *before* its root. -instance Ord UnkLevel where - compare (UnkLevel a) (UnkLevel b) = - go (NEL.toList a) (NEL.toList b) - where - go [] [] = EQ - go _ [] = LT - go [] _ = GT - go (x:xs) (y:ys) = - compare x y <> go xs ys - --- | A substitution of unification variables for types. -data Substitution = Substitution - { substType :: IM.IntMap SourceType - -- ^ Type substitution - , substUnsolved :: IM.IntMap (UnkLevel, SourceType) - -- ^ Unsolved unification variables with their level (scope ordering) and kind - , substNames :: IM.IntMap Text - -- ^ The original names of unknowns - } - -insertUnkName :: (MonadState CheckState m) => Unknown -> Text -> m () -insertUnkName u t = do - modify (\s -> - s { checkSubstitution = - (checkSubstitution s) { substNames = - IM.insert u t $ substNames $ checkSubstitution s - } - } - ) - -lookupUnkName :: (MonadState CheckState m) => Unknown -> m (Maybe Text) -lookupUnkName u = gets $ IM.lookup u . substNames . checkSubstitution - --- | An empty substitution -emptySubstitution :: Substitution -emptySubstitution = Substitution IM.empty IM.empty IM.empty - --- | State required for type checking -data CheckState = CheckState - { checkEnv :: Environment - -- ^ The current @Environment@ - , checkNextType :: Int - -- ^ The next type unification variable - , checkNextSkolem :: Int - -- ^ The next skolem variable - , checkNextSkolemScope :: Int - -- ^ The next skolem scope constant - , checkCurrentModule :: Maybe ModuleName - -- ^ The current module - , checkCurrentModuleImports :: - [ ( SourceAnn - , ModuleName - , ImportDeclarationType - , Maybe ModuleName - , M.Map (ProperName 'TypeName) ([ProperName 'ConstructorName], ExportSource) - ) - ] - -- ^ The current module imports and their exported types. - -- Newtype constructors have to be in scope for some Coercible constraints to - -- be solvable, so we need to know which constructors are imported and whether - -- they are actually defined in or re-exported from the imported modules. - , checkSubstitution :: Substitution - -- ^ The current substitution - , checkHints :: [ErrorMessageHint] - -- ^ The current error message hint stack. - -- This goes into state, rather than using 'rethrow', - -- since this way, we can provide good error messages - -- during instance resolution. - , checkConstructorImportsForCoercible :: S.Set (ModuleName, Qualified (ProperName 'ConstructorName)) - -- ^ Newtype constructors imports required to solve Coercible constraints. - -- We have to keep track of them so that we don't emit unused import warnings. - , unificationCache :: S.Set (SourceType, SourceType) - } - --- | Create an empty @CheckState@ -emptyCheckState :: Environment -> CheckState -emptyCheckState env = CheckState env 0 0 0 Nothing [] emptySubstitution [] mempty mempty - --- | Unification variables -type Unknown = Int - --- | Temporarily bind a collection of names to values -bindNames - :: M.Map (Qualified Ident) (SourceType, NameKind, NameVisibility) - -> TypeCheckM a - -> TypeCheckM a -bindNames newNames action = do - orig <- get - modify $ \st -> st { checkEnv = (checkEnv st) { names = newNames `M.union` (names . checkEnv $ st) } } - a <- action - modify $ \st -> st { checkEnv = (checkEnv st) { names = names . checkEnv $ orig } } - return a - --- | Temporarily bind a collection of names to types -bindTypes - :: M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) - -> TypeCheckM a - -> TypeCheckM a -bindTypes newNames action = do - orig <- get - modify $ \st -> st { checkEnv = (checkEnv st) { types = newNames `M.union` (types . checkEnv $ st) } } - a <- action - modify $ \st -> st { checkEnv = (checkEnv st) { types = types . checkEnv $ orig } } - return a - --- | Temporarily bind a collection of names to types -withScopedTypeVars - :: ModuleName - -> [(Text, SourceType)] - -> TypeCheckM a - -> TypeCheckM a -withScopedTypeVars mn ks ma = do - orig <- get - forM_ ks $ \(name, _) -> - when (Qualified (ByModuleName mn) (ProperName name) `M.member` types (checkEnv orig)) $ - tell . errorMessage $ ShadowedTypeVar name - bindTypes (M.fromList (map (\(name, k) -> (Qualified (ByModuleName mn) (ProperName name), (k, ScopedTypeVar))) ks)) ma - -withErrorMessageHint - :: (MonadState CheckState m, MonadError MultipleErrors m) - => ErrorMessageHint - -> m a - -> m a -withErrorMessageHint hint action = do - orig <- get - modify $ \st -> st { checkHints = hint : checkHints st } - -- Need to use 'rethrow' anyway, since we have to handle regular errors - a <- rethrow (addHint hint) action - modify $ \st -> st { checkHints = checkHints orig } - return a - --- | These hints are added at the front, so the most nested hint occurs --- at the front, but the simplifier assumes the reverse order. -getHints :: TypeCheckM [ErrorMessageHint] -getHints = gets (reverse . checkHints) - -rethrowWithPositionTC - :: SourceSpan - -> TypeCheckM a - -> TypeCheckM a -rethrowWithPositionTC pos = withErrorMessageHint (positionedError pos) - -warnAndRethrowWithPositionTC - :: SourceSpan - -> TypeCheckM a - -> TypeCheckM a -warnAndRethrowWithPositionTC pos = rethrowWithPositionTC pos . warnWithPosition pos - --- | Temporarily make a collection of type class dictionaries available -withTypeClassDictionaries - :: [NamedDict] - -> TypeCheckM a - -> TypeCheckM a -withTypeClassDictionaries entries action = do - orig <- get - - let mentries = - M.fromListWith (M.unionWith (M.unionWith (<>))) - [ (qb, M.singleton className (M.singleton tcdValue (pure entry))) - | entry@TypeClassDictionaryInScope{ tcdValue = tcdValue@(Qualified qb _), tcdClassName = className } - <- entries - ] - - modify $ \st -> st { checkEnv = (checkEnv st) { typeClassDictionaries = M.unionWith (M.unionWith (M.unionWith (<>))) (typeClassDictionaries . checkEnv $ st) mentries } } - a <- action - modify $ \st -> st { checkEnv = (checkEnv st) { typeClassDictionaries = typeClassDictionaries . checkEnv $ orig } } - return a - --- | Get the currently available map of type class dictionaries -getTypeClassDictionaries - :: TypeCheckM (M.Map QualifiedBy (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)))) -getTypeClassDictionaries = gets $ typeClassDictionaries . checkEnv - --- | Lookup type class dictionaries in a module. -lookupTypeClassDictionaries - :: QualifiedBy - -> TypeCheckM (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict))) -lookupTypeClassDictionaries mn = gets $ fromMaybe M.empty . M.lookup mn . typeClassDictionaries . checkEnv - --- | Lookup type class dictionaries in a module. -lookupTypeClassDictionariesForClass - :: QualifiedBy - -> Qualified (ProperName 'ClassName) - -> TypeCheckM (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)) -lookupTypeClassDictionariesForClass mn cn = fromMaybe M.empty . M.lookup cn <$> lookupTypeClassDictionaries mn - --- | Temporarily bind a collection of names to local variables -bindLocalVariables - :: [(SourceSpan, Ident, SourceType, NameVisibility)] - -> TypeCheckM a - -> TypeCheckM a -bindLocalVariables bindings = - bindNames (M.fromList $ flip map bindings $ \(ss, name, ty, visibility) -> (Qualified (BySourcePos $ spanStart ss) name, (ty, Private, visibility))) - --- | Temporarily bind a collection of names to local type variables -bindLocalTypeVariables - :: ModuleName - -> [(ProperName 'TypeName, SourceType)] - -> TypeCheckM a - -> TypeCheckM a -bindLocalTypeVariables moduleName bindings = - bindTypes (M.fromList $ flip map bindings $ \(pn, kind) -> (Qualified (ByModuleName moduleName) pn, (kind, LocalTypeVariable))) - --- | Update the visibility of all names to Defined -makeBindingGroupVisible :: TypeCheckM () -makeBindingGroupVisible = modifyEnv $ \e -> e { names = M.map (\(ty, nk, _) -> (ty, nk, Defined)) (names e) } - --- | Update the visibility of all names to Defined in the scope of the provided action -withBindingGroupVisible :: TypeCheckM a -> TypeCheckM a -withBindingGroupVisible action = preservingNames $ makeBindingGroupVisible >> action - --- | Perform an action while preserving the names from the @Environment@. -preservingNames :: TypeCheckM a -> TypeCheckM a -preservingNames action = do - orig <- gets (names . checkEnv) - a <- action - modifyEnv $ \e -> e { names = orig } - return a - --- | Lookup the type of a value by name in the @Environment@ -lookupVariable - :: Qualified Ident - -> TypeCheckM SourceType -lookupVariable qual = do - env <- getEnv - case M.lookup qual (names env) of - Nothing -> throwError . errorMessage $ NameIsUndefined (disqualify qual) - Just (ty, _, _) -> return ty - --- | Lookup the visibility of a value by name in the @Environment@ -getVisibility - :: Qualified Ident - -> TypeCheckM NameVisibility -getVisibility qual = do - env <- getEnv - case M.lookup qual (names env) of - Nothing -> throwError . errorMessage $ NameIsUndefined (disqualify qual) - Just (_, _, vis) -> return vis - --- | Assert that a name is visible -checkVisibility - :: Qualified Ident - -> TypeCheckM () -checkVisibility name@(Qualified _ var) = do - vis <- getVisibility name - case vis of - Undefined -> throwError . errorMessage $ CycleInDeclaration var - _ -> return () - --- | Lookup the kind of a type by name in the @Environment@ -lookupTypeVariable - :: ModuleName - -> Qualified (ProperName 'TypeName) - -> TypeCheckM SourceType -lookupTypeVariable currentModule (Qualified qb name) = do - env <- getEnv - case M.lookup (Qualified qb' name) (types env) of - Nothing -> throwError . errorMessage $ UndefinedTypeVariable name - Just (k, _) -> return k - where - qb' = ByModuleName $ case qb of - ByModuleName m -> m - BySourcePos _ -> currentModule - --- | Get the current @Environment@ -getEnv :: TypeCheckM Environment -getEnv = gets checkEnv - --- | Get locally-bound names in context, to create an error message. -getLocalContext :: TypeCheckM Context -getLocalContext = do - env <- getEnv - return [ (ident, ty') | (Qualified (BySourcePos _) ident@Ident{}, (ty', _, Defined)) <- M.toList (names env) ] - --- | Update the @Environment@ -putEnv :: Environment -> TypeCheckM () -putEnv env = modify (\s -> s { checkEnv = env }) - --- | Modify the @Environment@ -modifyEnv :: (Environment -> Environment) -> TypeCheckM () -modifyEnv f = modify (\s -> s { checkEnv = f (checkEnv s) }) - --- | Run a computation in the typechecking monad, failing with an error, or succeeding with a return value and the final @Environment@. -runCheck :: Functor m => CheckState -> StateT CheckState m a -> m (a, Environment) -runCheck st check = second checkEnv <$> runStateT check st - --- | Make an assertion, failing with an error message -guardWith :: MonadError MultipleErrors m => MultipleErrors -> Bool -> m () -guardWith _ True = return () -guardWith e False = throwError e - -capturingSubstitution - :: (a -> Substitution -> b) - -> TypeCheckM a - -> TypeCheckM b -capturingSubstitution f ma = do - a <- ma - subst <- gets checkSubstitution - return (f a subst) - -withFreshSubstitution - :: TypeCheckM a - -> TypeCheckM a -withFreshSubstitution ma = do - orig <- get - modify $ \st -> st { checkSubstitution = emptySubstitution } - a <- ma - modify $ \st -> st { checkSubstitution = checkSubstitution orig } - return a - -withoutWarnings - :: TypeCheckM a - -> TypeCheckM (a, MultipleErrors) -withoutWarnings = censor (const mempty) . listen - -unsafeCheckCurrentModule - :: forall m - . (MonadError MultipleErrors m, MonadState CheckState m) - => m ModuleName -unsafeCheckCurrentModule = gets checkCurrentModule >>= \case - Nothing -> internalError "No module name set in scope" - Just name -> pure name - -debugEnv :: Environment -> [String] -debugEnv env = join - [ debugTypes env - , debugTypeSynonyms env - , debugTypeClasses env - , debugTypeClassDictionaries env - , debugDataConstructors env - , debugNames env - ] - -debugType :: Type a -> String -debugType = init . prettyPrintType 100 - -debugConstraint :: Constraint a -> String -debugConstraint (Constraint ann clsName kinds args _) = - debugType $ foldl (TypeApp ann) (foldl (KindApp ann) (TypeConstructor ann (fmap coerceProperName clsName)) kinds) args - -debugTypes :: Environment -> [String] -debugTypes = go <=< M.toList . types - where - go (qual, (srcTy, which)) = do - let - ppTy = prettyPrintType 100 srcTy - name = showQualified runProperName qual - decl = case which of - DataType _ _ _ -> "data" - TypeSynonym -> "type" - ExternData _ -> "extern" - LocalTypeVariable -> "local" - ScopedTypeVar -> "scoped" - guard (not ("Prim" `isPrefixOf` name)) - pure $ decl <> " " <> unpack name <> " :: " <> init ppTy - -debugNames :: Environment -> [String] -debugNames = fmap go . M.toList . names - where - go (qual, (srcTy, _, _)) = do - let - ppTy = prettyPrintType 100 srcTy - name = showQualified runIdent qual - unpack name <> " :: " <> init ppTy - -debugDataConstructors :: Environment -> [String] -debugDataConstructors = fmap go . M.toList . dataConstructors - where - go (qual, (_, _, ty, _)) = do - let - ppTy = prettyPrintType 100 ty - name = showQualified runProperName qual - unpack name <> " :: " <> init ppTy - -debugTypeSynonyms :: Environment -> [String] -debugTypeSynonyms = fmap go . M.toList . typeSynonyms - where - go (qual, (binders, subTy)) = do - let - vars = unwords $ flip fmap binders $ \case - (v, Just k) -> "(" <> unpack v <> " :: " <> init (prettyPrintType 100 k) <> ")" - (v, Nothing) -> unpack v - ppTy = prettyPrintType 100 subTy - name = showQualified runProperName qual - "type " <> unpack name <> " " <> vars <> " = " <> init ppTy - -debugTypeClassDictionaries :: Environment -> [String] -debugTypeClassDictionaries = go . typeClassDictionaries - where - go tcds = do - (mbModuleName, classes) <- M.toList tcds - (className, instances) <- M.toList classes - (ident, dicts) <- M.toList instances - let - moduleName = maybe "" (\m -> "[" <> runModuleName m <> "] ") (toMaybeModuleName mbModuleName) - className' = showQualified runProperName className - ident' = showQualified runIdent ident - kds = unwords $ fmap ((\a -> "@(" <> a <> ")") . debugType) $ tcdInstanceKinds $ NEL.head dicts - tys = unwords $ fmap ((\a -> "(" <> a <> ")") . debugType) $ tcdInstanceTypes $ NEL.head dicts - pure $ "dict " <> unpack moduleName <> unpack className' <> " " <> unpack ident' <> " (" <> show (length dicts) <> ")" <> " " <> kds <> " " <> tys - -debugTypeClasses :: Environment -> [String] -debugTypeClasses = fmap go . M.toList . typeClasses - where - go (className, tc) = do - let - className' = showQualified runProperName className - args = unwords $ (\(a, b) -> "(" <> debugType (maybe (srcTypeVar a) (srcKindedType (srcTypeVar a)) b) <> ")") <$> typeClassArguments tc - "class " <> unpack className' <> " " <> args - -debugValue :: Expr -> String -debugValue = init . render . prettyPrintValue 100 - -debugSubstitution :: Substitution -> [String] -debugSubstitution (Substitution solved unsolved names) = - concat - [ fmap go1 (IM.toList solved) - , fmap go2 (IM.toList unsolved') - , fmap go3 (IM.toList names) - ] - where - unsolved' = - IM.filterWithKey (\k _ -> IM.notMember k solved) unsolved - - go1 (u, ty) = - "?" <> show u <> " = " <> debugType ty - - go2 (u, (_, k)) = - "?" <> show u <> " :: " <> debugType k - - go3 (u, t) = - unpack t <> show u diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Roles.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Roles.hs deleted file mode 100644 index 7b38a317..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Roles.hs +++ /dev/null @@ -1,263 +0,0 @@ -{-# LANGUAGE TypeApplications #-} - --- | --- Role inference --- -module Language.PureScript.TypeChecker.Roles - ( lookupRoles - , checkRoles - , checkRoleDeclarationArity - , inferRoles - , inferDataBindingGroupRoles - ) where - -import Prelude - -import Control.Arrow ((&&&)) -import Control.Monad (unless, when, zipWithM_) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State (MonadState(..), runState, state) -import Data.Coerce (coerce) -import Data.Map qualified as M -import Data.Maybe (fromMaybe) -import Data.Set qualified as S -import Data.Semigroup (Any(..)) -import Data.Text (Text) - -import Language.PureScript.Environment (Environment(..), TypeKind(..)) -import Language.PureScript.Errors (DataConstructorDeclaration(..), MultipleErrors, RoleDeclarationData(..), SimpleErrorMessage(..), errorMessage) -import Language.PureScript.Names (ModuleName, ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..)) -import Language.PureScript.Roles (Role(..)) -import Language.PureScript.Types (Constraint(..), SourceType, Type(..), freeTypeVariables, unapplyTypes) - --- | --- A map of a type's formal parameter names to their roles. This type's --- @Semigroup@ and @Monoid@ instances preserve the least-permissive role --- ascribed to any given variable, as defined by the @Role@ type's @Ord@ --- instance. That is, a variable that has been marked as @Nominal@ can not --- later be marked @Representational@, and so on. -newtype RoleMap = RoleMap { getRoleMap :: M.Map Text Role } - -instance Semigroup RoleMap where - (<>) = - coerce @(M.Map Text Role -> _ -> _) @(RoleMap -> _ -> _) (M.unionWith min) - -instance Monoid RoleMap where - mempty = - RoleMap M.empty - -type RoleEnv = M.Map (Qualified (ProperName 'TypeName)) [Role] - -typeKindRoles :: TypeKind -> Maybe [Role] -typeKindRoles = \case - DataType _ args _ -> - Just $ map (\(_, _, role) -> role) args - ExternData roles -> - Just roles - _ -> - Nothing - -getRoleEnv :: Environment -> RoleEnv -getRoleEnv env = - M.mapMaybe (typeKindRoles . snd) (types env) - -updateRoleEnv - :: Qualified (ProperName 'TypeName) - -> [Role] - -> RoleEnv - -> (Any, RoleEnv) -updateRoleEnv qualTyName roles' roleEnv = - let roles = fromMaybe (repeat Phantom) $ M.lookup qualTyName roleEnv - mostRestrictiveRoles = zipWith min roles roles' - didRolesChange = any (uncurry (<)) $ zip mostRestrictiveRoles roles - in (Any didRolesChange, M.insert qualTyName mostRestrictiveRoles roleEnv) - --- | --- Lookup the roles for a type in the environment. If the type does not have --- roles (e.g. is a type synonym or a type variable), then this function --- returns an empty list. --- -lookupRoles - :: Environment - -> Qualified (ProperName 'TypeName) - -> [Role] -lookupRoles env tyName = - fromMaybe [] $ M.lookup tyName (types env) >>= typeKindRoles . snd - --- | --- Compares the inferred roles to the explicitly declared roles and ensures --- that the explicitly declared roles are not more permissive than the --- inferred ones. --- -checkRoles - :: forall m - . (MonadError MultipleErrors m) - => [(Text, Maybe SourceType, Role)] - -- ^ type parameters for the data type whose roles we are checking - -> [Role] - -- ^ roles declared for the data type - -> m () -checkRoles tyArgs declaredRoles = do - let k (var, _, inf) dec = - when (inf < dec) . throwError . errorMessage $ RoleMismatch var inf dec - zipWithM_ k tyArgs declaredRoles - -checkRoleDeclarationArity - :: forall m - . (MonadError MultipleErrors m) - => ProperName 'TypeName - -> [Role] - -> Int - -> m () -checkRoleDeclarationArity tyName roles expected = do - let actual = length roles - unless (expected == actual) $ - throwError . errorMessage $ - RoleDeclarationArityMismatch tyName expected actual - --- | --- Infers roles for the given data type declaration. --- -inferRoles - :: Environment - -> ModuleName - -> ProperName 'TypeName - -- ^ The name of the data type whose roles we are checking - -> [(Text, Maybe SourceType)] - -- ^ type parameters for the data type whose roles we are checking - -> [DataConstructorDeclaration] - -- ^ constructors of the data type whose roles we are checking - -> [Role] -inferRoles env moduleName tyName tyArgs ctors = - inferDataBindingGroupRoles env moduleName [] [(tyName, tyArgs, ctors)] tyName tyArgs - -inferDataBindingGroupRoles - :: Environment - -> ModuleName - -> [RoleDeclarationData] - -> [DataDeclaration] - -> ProperName 'TypeName - -> [(Text, Maybe SourceType)] - -> [Role] -inferDataBindingGroupRoles env moduleName roleDeclarations group = - let declaredRoleEnv = M.fromList $ map (Qualified (ByModuleName moduleName) . rdeclIdent &&& rdeclRoles) roleDeclarations - inferredRoleEnv = getRoleEnv env - initialRoleEnv = declaredRoleEnv `M.union` inferredRoleEnv - inferredRoleEnv' = inferDataBindingGroupRoles' moduleName group initialRoleEnv - in \tyName tyArgs -> - let qualTyName = Qualified (ByModuleName moduleName) tyName - inferredRoles = M.lookup qualTyName inferredRoleEnv' - in fromMaybe (Phantom <$ tyArgs) inferredRoles - -type DataDeclaration = - ( ProperName 'TypeName - , [(Text, Maybe SourceType)] - , [DataConstructorDeclaration] - ) - -inferDataBindingGroupRoles' - :: ModuleName - -> [DataDeclaration] - -> RoleEnv - -> RoleEnv -inferDataBindingGroupRoles' moduleName group roleEnv = - let (Any didRolesChange, roleEnv') = flip runState roleEnv $ - mconcat <$> traverse (state . inferDataDeclarationRoles moduleName) group - in if didRolesChange - then inferDataBindingGroupRoles' moduleName group roleEnv' - else roleEnv' - --- | --- Infers roles for the given data type declaration, along with a flag to tell --- if more restrictive roles were added to the environment. --- -inferDataDeclarationRoles - :: ModuleName - -> DataDeclaration - -> RoleEnv - -> (Any, RoleEnv) -inferDataDeclarationRoles moduleName (tyName, tyArgs, ctors) roleEnv = - let qualTyName = Qualified (ByModuleName moduleName) tyName - ctorRoles = getRoleMap . foldMap (walk mempty . snd) $ ctors >>= dataCtorFields - inferredRoles = map (\(arg, _) -> fromMaybe Phantom (M.lookup arg ctorRoles)) tyArgs - in updateRoleEnv qualTyName inferredRoles roleEnv - where - -- This function is named @walk@ to match the specification given in the - -- "Role inference" section of the paper "Safe Zero-cost Coercions for - -- Haskell". - walk :: S.Set Text -> SourceType -> RoleMap - walk btvs (TypeVar _ v) - -- A type variable standing alone (e.g. @a@ in @data D a b = D a@) is - -- representational, _unless_ it has been bound by a quantifier, in which - -- case it is not actually a parameter to the type (e.g. @z@ in - -- @data T z = T (forall z. z -> z)@). - | S.member v btvs = - mempty - | otherwise = - RoleMap $ M.singleton v Representational - walk btvs (ForAll _ _ tv _ t _) = - -- We can walk under universal quantifiers as long as we make note of the - -- variables that they bind. For instance, given a definition - -- @data T z = T (forall z. z -> z)@, we will make note that @z@ is bound - -- by a quantifier so that we do not mark @T@'s parameter as - -- representational later on. Similarly, given a definition like - -- @data D a = D (forall r. r -> a)@, we'll mark @r@ as bound so that it - -- doesn't appear as a spurious parameter to @D@ when we complete - -- inference. - walk (S.insert tv btvs) t - walk btvs (ConstrainedType _ Constraint{..} t) = - -- For constrained types, mark all free variables in the constraint - -- arguments as nominal and recurse on the type beneath the constraint. - walk btvs t <> foldMap (freeNominals btvs) constraintArgs - walk btvs (RCons _ _ thead ttail) = do - -- For row types, we just walk along them and collect the results. - walk btvs thead <> walk btvs ttail - walk btvs (KindedType _ t _k) = - -- For kind-annotated types, discard the annotation and recurse on the - -- type beneath. - walk btvs t - walk btvs t - | (t1, _, t2s) <- unapplyTypes t - , not $ null t2s = - case t1 of - -- If the type is an application of a type constructor to some - -- arguments, recursively infer the roles of the type constructor's - -- arguments. For each (role, argument) pair: - -- - -- - If the role is nominal, mark all free variables in the argument - -- as nominal also, since they cannot be coerced if the - -- argument's nominality is to be preserved. - -- - -- - If the role is representational, recurse on the argument, since - -- its use of our parameters is important. - -- - -- - If the role is phantom, terminate, since the argument's use of - -- our parameters is unimportant. - TypeConstructor _ t1Name -> - let - t1Roles = fromMaybe (repeat Phantom) $ M.lookup t1Name roleEnv - k role ti = case role of - Nominal -> - freeNominals btvs ti - Representational -> - go ti - Phantom -> - mempty - in mconcat (zipWith k t1Roles t2s) - -- If the type is an application of any other type-level term, walk - -- that term to collect its roles and mark all free variables in - -- its argument as nominal. - _ -> do - go t1 <> foldMap (freeNominals btvs) t2s - | otherwise = - mempty - where - go = walk btvs - --- Given a type, computes the list of free variables in that type --- (taking into account those bound in @walk@) and returns a @RoleMap@ --- ascribing a nominal role to each of those variables. -freeNominals :: S.Set Text -> SourceType -> RoleMap -freeNominals btvs x = - let ftvs = filter (flip S.notMember btvs) (freeTypeVariables x) - in RoleMap (M.fromList $ map (, Nominal) ftvs) diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Skolems.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Skolems.hs deleted file mode 100644 index aa49997f..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Skolems.hs +++ /dev/null @@ -1,131 +0,0 @@ --- | Functions relating to skolemization used during typechecking -module Language.PureScript.TypeChecker.Skolems - ( newSkolemConstant - , introduceSkolemScope - , newSkolemScope - , skolemize - , skolemizeTypesInValue - , skolemEscapeCheck - ) where - -import Prelude - -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State.Class (MonadState(..), gets, modify) -import Data.Foldable (traverse_) -import Data.Functor.Identity (Identity(), runIdentity) -import Data.Set (Set, fromList, notMember) -import Data.Text (Text) -import Language.PureScript.AST (Binder(..), ErrorMessageHint(..), Expr(..), SourceAnn, SourceSpan, everythingWithContextOnValues, everywhereWithContextOnValuesM, nonEmptySpan) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Errors (ErrorMessage(..), MultipleErrors, SimpleErrorMessage(..), positionedError, singleError) -import Language.PureScript.Traversals (defS) -import Language.PureScript.TypeChecker.Monad (CheckState(..)) -import Language.PureScript.Types (SkolemScope(..), SourceType, Type(..), everythingOnTypes, everywhereOnTypesM, replaceTypeVars) - --- | Generate a new skolem constant -newSkolemConstant :: MonadState CheckState m => m Int -newSkolemConstant = do - s <- gets checkNextSkolem - modify $ \st -> st { checkNextSkolem = s + 1 } - return s - --- | Introduce skolem scope at every occurrence of a ForAll -introduceSkolemScope :: MonadState CheckState m => Type a -> m (Type a) -introduceSkolemScope = everywhereOnTypesM go - where - go (ForAll ann vis ident mbK ty Nothing) = ForAll ann vis ident mbK ty <$> (Just <$> newSkolemScope) - go other = return other - --- | Generate a new skolem scope -newSkolemScope :: MonadState CheckState m => m SkolemScope -newSkolemScope = do - s <- gets checkNextSkolemScope - modify $ \st -> st { checkNextSkolemScope = s + 1 } - return $ SkolemScope s - --- | Skolemize a type variable by replacing its instances with fresh skolem constants -skolemize :: a -> Text -> Maybe (Type a) -> Int -> SkolemScope -> Type a -> Type a -skolemize ann ident mbK sko scope = replaceTypeVars ident (Skolem ann ident mbK sko scope) - --- | This function skolemizes type variables appearing in any type signatures or --- 'DeferredDictionary' placeholders. These type variables are the only places --- where scoped type variables can appear in expressions. -skolemizeTypesInValue :: SourceAnn -> Text -> Maybe SourceType -> Int -> SkolemScope -> Expr -> Expr -skolemizeTypesInValue ann ident mbK sko scope = - runIdentity . onExpr' - where - onExpr' :: Expr -> Identity Expr - (_, onExpr', _, _, _, _) = everywhereWithContextOnValuesM [] defS onExpr onBinder defS defS defS - - onExpr :: [Text] -> Expr -> Identity ([Text], Expr) - onExpr sco (DeferredDictionary c ts) - | ident `notElem` sco = return (sco, DeferredDictionary c (map (skolemize ann ident mbK sko scope) ts)) - onExpr sco (TypedValue check val ty) - | ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedValue check val (skolemize ann ident mbK sko scope ty)) - onExpr sco (VisibleTypeApp val ty) - | ident `notElem` sco = return (sco ++ peelTypeVars ty, VisibleTypeApp val (skolemize ann ident mbK sko scope ty)) - onExpr sco other = return (sco, other) - - onBinder :: [Text] -> Binder -> Identity ([Text], Binder) - onBinder sco (TypedBinder ty b) - | ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedBinder (skolemize ann ident mbK sko scope ty) b) - onBinder sco other = return (sco, other) - - peelTypeVars :: SourceType -> [Text] - peelTypeVars (ForAll _ _ i _ ty _) = i : peelTypeVars ty - peelTypeVars _ = [] - --- | Ensure skolem variables do not escape their scope --- --- Every skolem variable is created when a 'ForAll' type is skolemized. --- This determines the scope of that skolem variable, which is copied from --- the 'SkolemScope' field of the 'ForAll' constructor. --- --- This function traverses the tree top-down, and collects any 'SkolemScope's --- introduced by 'ForAll's. If a 'Skolem' is encountered whose 'SkolemScope' is --- not in the current list, then we have found an escaped skolem variable. -skolemEscapeCheck :: MonadError MultipleErrors m => Expr -> m () -skolemEscapeCheck (TypedValue False _ _) = return () -skolemEscapeCheck expr@TypedValue{} = - traverse_ (throwError . singleError) (toSkolemErrors expr) - where - toSkolemErrors :: Expr -> [ErrorMessage] - (_, toSkolemErrors, _, _, _) = everythingWithContextOnValues (mempty, Nothing) [] (<>) def go def def def - - def s _ = (s, []) - - go :: (Set SkolemScope, Maybe SourceSpan) - -> Expr - -> ((Set SkolemScope, Maybe SourceSpan), [ErrorMessage]) - go (scopes, _) (PositionedValue ss _ _) = ((scopes, Just ss), []) - go (scopes, ssUsed) val@(TypedValue _ _ ty) = - ( (allScopes, ssUsed) - , [ ErrorMessage (maybe id ((:) . positionedError) ssUsed [ ErrorInExpression val ]) $ - EscapedSkolem name (nonEmptySpan ssBound) ty - | (ssBound, name, scope) <- collectSkolems ty - , notMember scope allScopes - ] - ) - where - -- Any new skolem scopes introduced by universal quantifiers - newScopes :: [SkolemScope] - newScopes = collectScopes ty - - -- All scopes, including new scopes - allScopes :: Set SkolemScope - allScopes = fromList newScopes <> scopes - - -- Collect any scopes appearing in quantifiers at the top level - collectScopes :: SourceType -> [SkolemScope] - collectScopes (ForAll _ _ _ _ t (Just sco)) = sco : collectScopes t - collectScopes ForAll{} = internalError "skolemEscapeCheck: No skolem scope" - collectScopes _ = [] - - -- Collect any skolem variables appearing in a type - collectSkolems :: SourceType -> [(SourceAnn, Text, SkolemScope)] - collectSkolems = everythingOnTypes (++) collect where - collect (Skolem ss name _ _ scope) = [(ss, name, scope)] - collect _ = [] - go scos _ = (scos, []) -skolemEscapeCheck _ = internalError "skolemEscapeCheck: untyped value" diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Subsumption.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Subsumption.hs deleted file mode 100644 index 9e360462..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Subsumption.hs +++ /dev/null @@ -1,129 +0,0 @@ -{-# LANGUAGE GADTs #-} - --- | Subsumption checking -module Language.PureScript.TypeChecker.Subsumption - ( subsumes - ) where - -import Prelude - -import Control.Monad (when) -import Control.Monad.Error.Class (MonadError(..)) - -import Data.Foldable (for_) -import Data.List (uncons) -import Data.List.Ordered (minusBy') -import Data.Ord (comparing) - -import Language.PureScript.AST (ErrorMessageHint(..), Expr(..), pattern NullSourceAnn) -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment (tyFunction, tyRecord) -import Language.PureScript.Errors (SimpleErrorMessage(..), errorMessage, internalCompilerError) -import Language.PureScript.TypeChecker.Monad (getHints, getTypeClassDictionaries, withErrorMessageHint, TypeCheckM) -import Language.PureScript.TypeChecker.Skolems (newSkolemConstant, skolemize) -import Language.PureScript.TypeChecker.Unify (alignRowsWith, freshTypeWithKind, unifyTypes) -import Language.PureScript.Types (RowListItem(..), SourceType, Type(..), eqType, isREmpty, replaceTypeVars, rowFromList) - --- | Subsumption can operate in two modes: --- --- * Elaboration mode, in which we try to insert type class dictionaries --- * No-elaboration mode, in which we do not insert dictionaries --- --- Some subsumption rules apply in both modes, and others are specific to --- certain modes. --- --- The subsumption algorithm follows the structure of the types in question, --- and we can switch into no-elaboration mode when we move under a type --- constructor where we can no longer insert dictionaries, e.g. into the fields --- of a record. -data Mode = Elaborate | NoElaborate - --- | Value-level proxies for the two modes -data ModeSing (mode :: Mode) where - SElaborate :: ModeSing 'Elaborate - SNoElaborate :: ModeSing 'NoElaborate - --- | This type family tracks what evidence we return from 'subsumes' for each --- mode. -type family Coercion (mode :: Mode) where - -- When elaborating, we generate a coercion - Coercion 'Elaborate = Expr -> Expr - -- When we're not elaborating, we don't generate coercions - Coercion 'NoElaborate = () - --- | The default coercion for each mode. -defaultCoercion :: ModeSing mode -> Coercion mode -defaultCoercion SElaborate = id -defaultCoercion SNoElaborate = () - --- | Check that one type subsumes another, rethrowing errors to provide a better error message -subsumes - :: () - => SourceType - -> SourceType - -> TypeCheckM (Expr -> Expr) -subsumes ty1 ty2 = - withErrorMessageHint (ErrorInSubsumption ty1 ty2) $ - subsumes' SElaborate ty1 ty2 - --- | Check that one type subsumes another -subsumes' - :: () - => ModeSing mode - -> SourceType - -> SourceType - -> TypeCheckM (Coercion mode) -subsumes' mode (ForAll _ _ ident mbK ty1 _) ty2 = do - u <- maybe (internalCompilerError "Unelaborated forall") freshTypeWithKind mbK - let replaced = replaceTypeVars ident u ty1 - subsumes' mode replaced ty2 -subsumes' mode ty1 (ForAll _ _ ident mbK ty2 sco) = - case sco of - Just sco' -> do - sko <- newSkolemConstant - let sk = skolemize NullSourceAnn ident mbK sko sco' ty2 - subsumes' mode ty1 sk - Nothing -> internalError "subsumes: unspecified skolem scope" -subsumes' mode (TypeApp _ (TypeApp _ f1 arg1) ret1) (TypeApp _ (TypeApp _ f2 arg2) ret2) | eqType f1 tyFunction && eqType f2 tyFunction = do - subsumes' SNoElaborate arg2 arg1 - subsumes' SNoElaborate ret1 ret2 - -- Nothing was elaborated, return the default coercion - return (defaultCoercion mode) -subsumes' mode (KindedType _ ty1 _) ty2 = - subsumes' mode ty1 ty2 -subsumes' mode ty1 (KindedType _ ty2 _) = - subsumes' mode ty1 ty2 --- Only check subsumption for constrained types when elaborating. --- Otherwise fall back to unification. -subsumes' SElaborate (ConstrainedType _ con ty1) ty2 = do - dicts <- getTypeClassDictionaries - hints <- getHints - elaborate <- subsumes' SElaborate ty1 ty2 - let addDicts val = App val (TypeClassDictionary con dicts hints) - return (elaborate . addDicts) -subsumes' mode (TypeApp _ f1 r1) (TypeApp _ f2 r2) | eqType f1 tyRecord && eqType f2 tyRecord = do - let goWithLabel l t1 t2 = withErrorMessageHint (ErrorInRowLabel l) $ subsumes' SNoElaborate t1 t2 - let (common, ((ts1', r1'), (ts2', r2'))) = alignRowsWith goWithLabel r1 r2 - -- For { ts1 | r1 } to subsume { ts2 | r2 } when r1 is empty (= we're working with a closed row), - -- every property in ts2 must appear in ts1. If not, then the candidate expression is missing a required property. - -- Conversely, when r2 is empty, every property in ts1 must appear in ts2, or else the expression has - -- an additional property which is not allowed. - when (isREmpty r1') - (for_ (firstMissingProp ts2' ts1') (throwError . errorMessage . PropertyIsMissing . rowListLabel)) - when (isREmpty r2') - (for_ (firstMissingProp ts1' ts2') (throwError . errorMessage . AdditionalProperty . rowListLabel)) - -- Check subsumption for common labels - sequence_ common - -- Inject the info here - unifyTypes (rowFromList (ts1', r1')) (rowFromList (ts2', r2')) - -- Nothing was elaborated, return the default coercion - return (defaultCoercion mode) - where - -- Find the first property that's in the first list (of tuples) but not in the second - firstMissingProp t1 t2 = fst <$> uncons (minusBy' (comparing rowListLabel) t1 t2) -subsumes' mode ty1 ty2@(TypeApp _ obj _) | obj == tyRecord = - subsumes' mode ty2 ty1 -subsumes' mode ty1 ty2 = do - unifyTypes ty1 ty2 - -- Nothing was elaborated, return the default coercion - return (defaultCoercion mode) diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Synonyms.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Synonyms.hs deleted file mode 100644 index 9672836d..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Synonyms.hs +++ /dev/null @@ -1,61 +0,0 @@ -{-# LANGUAGE GADTs #-} - --- | --- Functions for replacing fully applied type synonyms --- -module Language.PureScript.TypeChecker.Synonyms - ( SynonymMap - , KindMap - , replaceAllTypeSynonyms - ) where - -import Prelude - -import Control.Monad.Error.Class (MonadError(..)) -import Data.Maybe (fromMaybe) -import Data.Map qualified as M -import Data.Text (Text) -import Language.PureScript.Environment (Environment(..), TypeKind) -import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), SourceSpan, errorMessage') -import Language.PureScript.Names (ProperName, ProperNameType(..), Qualified) -import Language.PureScript.TypeChecker.Monad (getEnv, TypeCheckM) -import Language.PureScript.Types (SourceType, Type(..), completeBinderList, everywhereOnTypesTopDownM, getAnnForType, replaceAllTypeVars) - --- | Type synonym information (arguments with kinds, aliased type), indexed by name -type SynonymMap = M.Map (Qualified (ProperName 'TypeName)) ([(Text, Maybe SourceType)], SourceType) - -type KindMap = M.Map (Qualified (ProperName 'TypeName)) (SourceType, TypeKind) - -replaceAllTypeSynonyms' - :: SynonymMap - -> KindMap - -> SourceType - -> Either MultipleErrors SourceType -replaceAllTypeSynonyms' syns kinds = everywhereOnTypesTopDownM try - where - try :: SourceType -> Either MultipleErrors SourceType - try t = fromMaybe t <$> go (fst $ getAnnForType t) 0 [] [] t - - go :: SourceSpan -> Int -> [SourceType] -> [SourceType] -> SourceType -> Either MultipleErrors (Maybe SourceType) - go ss c kargs args (TypeConstructor _ ctor) - | Just (synArgs, body) <- M.lookup ctor syns - , c == length synArgs - , kindArgs <- lookupKindArgs ctor - , length kargs == length kindArgs - = let repl = replaceAllTypeVars (zip (map fst synArgs) args <> zip kindArgs kargs) body - in Just <$> try repl - | Just (synArgs, _) <- M.lookup ctor syns - , length synArgs > c - = throwError . errorMessage' ss $ PartiallyAppliedSynonym ctor - go ss c kargs args (TypeApp _ f arg) = go ss (c + 1) kargs (arg : args) f - go ss c kargs args (KindApp _ f arg) = go ss c (arg : kargs) args f - go _ _ _ _ _ = return Nothing - - lookupKindArgs :: Qualified (ProperName 'TypeName) -> [Text] - lookupKindArgs ctor = fromMaybe [] $ fmap (fmap (fst . snd) . fst) . completeBinderList . fst =<< M.lookup ctor kinds - --- | Replace fully applied type synonyms -replaceAllTypeSynonyms :: SourceType -> TypeCheckM SourceType -replaceAllTypeSynonyms d = do - env <- getEnv - either throwError return $ replaceAllTypeSynonyms' (typeSynonyms env) (types env) d diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/TypeSearch.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/TypeSearch.hs deleted file mode 100644 index 580befa2..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/TypeSearch.hs +++ /dev/null @@ -1,134 +0,0 @@ -module Language.PureScript.TypeChecker.TypeSearch - ( typeSearch - ) where - -import Protolude - -import Control.Monad.Writer (WriterT, runWriterT) -import Data.Map qualified as Map -import Language.PureScript.TypeChecker.Entailment qualified as Entailment - -import Language.PureScript.TypeChecker.Monad qualified as TC -import Language.PureScript.TypeChecker.Subsumption (subsumes) -import Language.PureScript.TypeChecker.Unify as P - -import Language.PureScript.AST as P -import Language.PureScript.Environment as P -import Language.PureScript.Label (Label) -import Language.PureScript.Names as P -import Language.PureScript.Pretty.Types as P -import Language.PureScript.TypeChecker.Skolems as Skolem -import Language.PureScript.TypeChecker.Synonyms as P -import Language.PureScript.Types as P -import Control.Monad.Supply qualified as P -import Language.PureScript.TypeChecker.Monad qualified as P - -checkInEnvironment - :: Environment - -> TC.CheckState - -> TC.TypeCheckM a - -> Maybe (a, Environment) -checkInEnvironment env st = - either (const Nothing) Just - . runExcept - . evalWriterT - . P.evalSupplyT 0 - . TC.runCheck (st { TC.checkEnv = env }) - . P.liftTypeCheckM - -evalWriterT :: Monad m => WriterT b m r -> m r -evalWriterT m = fmap fst (runWriterT m) - -checkSubsume - :: Maybe [(P.Ident, Entailment.InstanceContext, P.SourceConstraint)] - -- ^ Additional constraints we need to satisfy - -> P.Environment - -- ^ The Environment which contains the relevant definitions and typeclasses - -> TC.CheckState - -- ^ The typechecker state - -> P.SourceType - -- ^ The user supplied type - -> P.SourceType - -- ^ The type supplied by the environment - -> Maybe ((P.Expr, [(P.Ident, Entailment.InstanceContext, P.SourceConstraint)]), P.Environment) -checkSubsume unsolved env st userT envT = checkInEnvironment env st $ do - let initializeSkolems = - Skolem.introduceSkolemScope - <=< P.replaceAllTypeSynonyms - <=< P.replaceTypeWildcards - - userT' <- initializeSkolems userT - envT' <- initializeSkolems envT - - let dummyExpression = P.Var nullSourceSpan (P.Qualified P.ByNullSourcePos (P.Ident "x")) - - elab <- subsumes envT' userT' - subst <- gets TC.checkSubstitution - let expP = P.overTypes (P.substituteType subst) (elab dummyExpression) - - -- Now check that any unsolved constraints have not become impossible - (traverse_ . traverse_) (\(_, context, constraint) -> do - let constraint' = P.mapConstraintArgs (map (P.substituteType subst)) constraint - flip evalStateT Map.empty . evalWriterT $ - Entailment.entails - (Entailment.SolverOptions - { solverShouldGeneralize = True - , solverDeferErrors = False - }) constraint' context []) unsolved - - -- Finally, check any constraints which were found during elaboration - Entailment.replaceTypeClassDictionaries (isJust unsolved) expP - -accessorSearch - :: Maybe [(P.Ident, Entailment.InstanceContext, P.SourceConstraint)] - -> P.Environment - -> TC.CheckState - -> P.SourceType - -> ([(Label, P.SourceType)], [(Label, P.SourceType)]) - -- ^ (all accessors we found, all accessors we found that match the result type) -accessorSearch unsolved env st userT = maybe ([], []) fst $ checkInEnvironment env st $ do - let initializeSkolems = - Skolem.introduceSkolemScope - <=< P.replaceAllTypeSynonyms - <=< P.replaceTypeWildcards - - userT' <- initializeSkolems userT - - rowType <- freshTypeWithKind (P.kindRow P.kindType) - resultType <- freshTypeWithKind P.kindType - let recordFunction = srcTypeApp (srcTypeApp tyFunction (srcTypeApp tyRecord rowType)) resultType - _ <- subsumes recordFunction userT' - subst <- gets TC.checkSubstitution - let solvedRow = toRowPair <$> fst (rowToList (substituteType subst rowType)) - tcS <- get - pure (solvedRow, filter (\x -> checkAccessor tcS (substituteType subst resultType) x) solvedRow) - where - checkAccessor tcs x (_, type') = isJust (checkSubsume unsolved env tcs x type') - toRowPair (RowListItem _ lbl ty) = (lbl, ty) - -typeSearch - :: Maybe [(P.Ident, Entailment.InstanceContext, P.SourceConstraint)] - -- ^ Additional constraints we need to satisfy - -> P.Environment - -- ^ The Environment which contains the relevant definitions and typeclasses - -> TC.CheckState - -- ^ The typechecker state - -> P.SourceType - -- ^ The type we are looking for - -> ([(P.Qualified Text, P.SourceType)], Maybe [(Label, P.SourceType)]) -typeSearch unsolved env st type' = - let - runTypeSearch :: Map k P.SourceType -> Map k P.SourceType - runTypeSearch = Map.mapMaybe (\ty -> checkSubsume unsolved env st type' ty $> ty) - - matchingNames = runTypeSearch (Map.map (\(ty, _, _) -> ty) (P.names env)) - matchingConstructors = runTypeSearch (Map.map (\(_, _, ty, _) -> ty) (P.dataConstructors env)) - (allLabels, matchingLabels) = accessorSearch unsolved env st type' - - runPlainIdent (Qualified m (Ident k), v) = Just (Qualified m k, v) - runPlainIdent _ = Nothing - in - ( (first (P.Qualified P.ByNullSourcePos . ("_." <>) . P.prettyPrintLabel) <$> matchingLabels) - <> mapMaybe runPlainIdent (Map.toList matchingNames) - <> (first (map P.runProperName) <$> Map.toList matchingConstructors) - , if null allLabels then Nothing else Just allLabels) diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Types.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Types.hs deleted file mode 100644 index 6fe4cbf1..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Types.hs +++ /dev/null @@ -1,1035 +0,0 @@ --- | --- This module implements the type checker --- -{-# OPTIONS_GHC -Wno-redundant-constraints #-} -module Language.PureScript.TypeChecker.Types - ( BindingGroupType(..) - , typesOf - , checkTypeKind - ) where - -{- - The following functions represent the corresponding type checking judgements: - - infer - Synthesize a type for a value - - check - Check a value has a given type - - checkProperties - Check an object with a given type contains specified properties - - checkFunctionApplication - Check a function of a given type returns a value of another type when applied to its arguments --} - -import Prelude -import Protolude (ordNub, fold, atMay) - -import Control.Arrow (first, second, (***)) -import Control.Monad (forM, forM_, guard, replicateM, unless, when, zipWithM, (<=<)) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State.Class (MonadState(..), gets) -import Control.Monad.Supply.Class (MonadSupply) -import Control.Monad.Writer.Class (MonadWriter(..)) - -import Data.Bifunctor (bimap) -import Data.Either (partitionEithers) -import Data.Functor (($>)) -import Data.List (transpose, (\\), partition, delete) -import Data.Maybe (fromMaybe) -import Data.Text (Text) -import Data.Traversable (for) -import Data.List.NonEmpty qualified as NEL -import Data.Map qualified as M -import Data.Set qualified as S -import Data.IntSet qualified as IS - -import Language.PureScript.AST -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment -import Language.PureScript.Errors (ErrorMessage(..), MultipleErrors, SimpleErrorMessage(..), errorMessage, errorMessage', escalateWarningWhen, internalCompilerError, onErrorMessages, onTypesInErrorMessage, parU) -import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Name(..), ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), byMaybeModuleName, coerceProperName, freshIdent) -import Language.PureScript.TypeChecker.Deriving (deriveInstance) -import Language.PureScript.TypeChecker.Entailment (InstanceContext, newDictionaries, replaceTypeClassDictionaries) -import Language.PureScript.TypeChecker.Kinds (checkConstraint, checkKind, checkTypeKind, kindOf, kindOfWithScopedVars, unifyKinds', unknownsWithKinds) -import Language.PureScript.TypeChecker.Monad -import Language.PureScript.TypeChecker.Skolems (introduceSkolemScope, newSkolemConstant, newSkolemScope, skolemEscapeCheck, skolemize, skolemizeTypesInValue) -import Language.PureScript.TypeChecker.Subsumption (subsumes) -import Language.PureScript.TypeChecker.Synonyms (replaceAllTypeSynonyms) -import Language.PureScript.TypeChecker.TypeSearch (typeSearch) -import Language.PureScript.TypeChecker.Unify (freshTypeWithKind, replaceTypeWildcards, substituteType, unifyTypes, unknownsInType, varIfUnknown) -import Language.PureScript.Types -import Language.PureScript.Label (Label(..)) -import Language.PureScript.PSString (PSString) - -data BindingGroupType - = RecursiveBindingGroup - | NonRecursiveBindingGroup - deriving (Show, Eq, Ord) - --- | The result of a successful type check. -data TypedValue' = TypedValue' Bool Expr SourceType - --- | Convert an type checked value into an expression. -tvToExpr :: TypedValue' -> Expr -tvToExpr (TypedValue' c e t) = TypedValue c e t - --- | Lookup data about a type class in the @Environment@ -lookupTypeClass :: MonadState CheckState TypeCheckM => Qualified (ProperName 'ClassName) -> TypeCheckM TypeClassData -lookupTypeClass name = - let findClass = fromMaybe (internalError "entails: type class not found in environment") . M.lookup name - in gets (findClass . typeClasses . checkEnv) - --- | Infer the types of multiple mutually-recursive values, and return elaborated values including --- type class dictionaries and type annotations. -typesOf - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => BindingGroupType - -> ModuleName - -> [((SourceAnn, Ident), Expr)] - -> TypeCheckM [((SourceAnn, Ident), (Expr, SourceType))] -typesOf bindingGroupType moduleName vals = withFreshSubstitution $ do - (tys, wInfer) <- capturingSubstitution tidyUp $ do - (SplitBindingGroup untyped typed dict, w) <- withoutWarnings $ typeDictionaryForBindingGroup (Just moduleName) vals - ds1 <- parU typed $ \e -> withoutWarnings $ checkTypedBindingGroupElement moduleName e dict - ds2 <- forM untyped $ \e -> withoutWarnings $ typeForBindingGroupElement e dict - return (map (False, ) ds1 ++ map (True, ) ds2, w) - - inferred <- forM tys $ \(shouldGeneralize, ((sai@((ss, _), ident), (val, ty)), _)) -> do - -- Replace type class dictionary placeholders with actual dictionaries - (val', unsolved) <- replaceTypeClassDictionaries shouldGeneralize val - -- Generalize and constrain the type - currentSubst <- gets checkSubstitution - let ty' = substituteType currentSubst ty - ty'' = constrain unsolved ty' - unsolvedTypeVarsWithKinds <- unknownsWithKinds . IS.toList . unknowns $ constrain unsolved ty'' - let unsolvedTypeVars = IS.toList $ unknowns ty' - - generalized <- varIfUnknown unsolvedTypeVarsWithKinds ty'' - - when shouldGeneralize $ do - -- Show the inferred type in a warning - tell - . errorMessage' ss - $ MissingTypeDeclaration ident generalized - -- For non-recursive binding groups, can generalize over constraints. - -- For recursive binding groups, we throw an error here for now. - when (bindingGroupType == RecursiveBindingGroup && not (null unsolved)) - . throwError - . errorMessage' ss - $ CannotGeneralizeRecursiveFunction ident generalized - -- We need information about functional dependencies, since we allow - -- ambiguous types to be inferred if they can be solved by some functional - -- dependency. - conData <- forM unsolved $ \(_, _, con) -> do - TypeClassData{ typeClassDependencies } <- lookupTypeClass $ constraintClass con - let - -- The set of unknowns mentioned in each argument. - unknownsForArg :: [S.Set Int] - unknownsForArg = - map (S.fromList . map snd . unknownsInType) (constraintArgs con) - pure (typeClassDependencies, unknownsForArg) - -- Make sure any unsolved type constraints are determined by the - -- type variables which appear unknown in the inferred type. - let - -- Take the closure of fundeps across constraints, to get more - -- and more solved variables until reaching a fixpoint. - solveFrom :: S.Set Int -> S.Set Int - solveFrom determined = do - let solved = solve1 determined - if solved `S.isSubsetOf` determined - then determined - else solveFrom (determined <> solved) - solve1 :: S.Set Int -> S.Set Int - solve1 determined = fold $ do - (tcDeps, conArgUnknowns) <- conData - let - lookupUnknowns :: Int -> Maybe (S.Set Int) - lookupUnknowns = atMay conArgUnknowns - unknownsDetermined :: Maybe (S.Set Int) -> Bool - unknownsDetermined Nothing = False - unknownsDetermined (Just unks) = - unks `S.isSubsetOf` determined - -- If all of the determining arguments of a particular fundep are - -- already determined, add the determined arguments from the fundep - tcDep <- tcDeps - guard $ all (unknownsDetermined . lookupUnknowns) (fdDeterminers tcDep) - map (fromMaybe S.empty . lookupUnknowns) (fdDetermined tcDep) - -- These unknowns can be determined from the body of the inferred - -- type (i.e. excluding the unknowns mentioned in the constraints) - let determinedFromType = S.fromList unsolvedTypeVars - -- These are all the unknowns mentioned in the constraints - let constraintTypeVars = fold (conData >>= snd) - let solved = solveFrom determinedFromType - let unsolvedVars = S.difference constraintTypeVars solved - let lookupUnkName' i = do - mn <- lookupUnkName i - pure (fromMaybe "t" mn, i) - unsolvedVarNames <- traverse lookupUnkName' (S.toList unsolvedVars) - unless (S.null unsolvedVars) . - throwError - . onErrorMessages (replaceTypes currentSubst) - . errorMessage' ss - $ AmbiguousTypeVariables generalized unsolvedVarNames - - -- Check skolem variables did not escape their scope - skolemEscapeCheck val' - return ((sai, (foldr (Abs . VarBinder nullSourceSpan . (\(x, _, _) -> x)) val' unsolved, generalized)), unsolved) - - -- Show warnings here, since types in wildcards might have been solved during - -- instance resolution (by functional dependencies). - finalState <- get - let replaceTypes' = replaceTypes (checkSubstitution finalState) - runTypeSearch' gen = runTypeSearch (guard gen $> foldMap snd inferred) finalState - raisePreviousWarnings gen = escalateWarningWhen isHoleError . tell . onErrorMessages (runTypeSearch' gen . replaceTypes') - - raisePreviousWarnings False wInfer - forM_ tys $ \(shouldGeneralize, ((_, (_, _)), w)) -> - raisePreviousWarnings shouldGeneralize w - - return (map fst inferred) - where - replaceTypes - :: Substitution - -> ErrorMessage - -> ErrorMessage - replaceTypes subst = onTypesInErrorMessage (substituteType subst) - - -- Run type search to complete any typed hole error messages - runTypeSearch - :: Maybe [(Ident, InstanceContext, SourceConstraint)] - -- Any unsolved constraints which we need to continue to satisfy - -> CheckState - -- The final type checker state - -> ErrorMessage - -> ErrorMessage - runTypeSearch cons st = \case - ErrorMessage hints (HoleInferredType x ty y (Just (TSBefore env))) -> - let subst = checkSubstitution st - searchResult = onTypeSearchTypes - (substituteType subst) - (uncurry TSAfter (typeSearch cons env st (substituteType subst ty))) - in ErrorMessage hints (HoleInferredType x ty y (Just searchResult)) - other -> other - - -- Add any unsolved constraints - constrain cs ty = foldr srcConstrainedType ty (map (\(_, _, x) -> x) cs) - - -- Apply the substitution that was returned from runUnify to both types and (type-annotated) values - - tidyUp ts sub = first (map (second (first (second (overTypes (substituteType sub) *** substituteType sub))))) ts - - isHoleError :: ErrorMessage -> Bool - isHoleError (ErrorMessage _ HoleInferredType{}) = True - isHoleError _ = False - --- | A binding group contains multiple value definitions, some of which are typed --- and some which are not. --- --- This structure breaks down a binding group into typed and untyped parts. -data SplitBindingGroup = SplitBindingGroup - { _splitBindingGroupUntyped :: [((SourceAnn, Ident), (Expr, SourceType))] - -- ^ The untyped expressions - , _splitBindingGroupTyped :: [((SourceAnn, Ident), (Expr, [(Text, SourceType)], SourceType, Bool))] - -- ^ The typed expressions, along with their type annotations - , _splitBindingGroupNames :: M.Map (Qualified Ident) (SourceType, NameKind, NameVisibility) - -- ^ A map containing all expressions and their assigned types (which might be - -- fresh unification variables). These will be added to the 'Environment' after - -- the binding group is checked, so the value type of the 'Map' is chosen to be - -- compatible with the type of 'bindNames'. - } - --- | This function breaks a binding group down into two sets of declarations: --- those which contain type annotations, and those which don't. --- This function also generates fresh unification variables for the types of --- declarations without type annotations, returned in the 'UntypedData' structure. -typeDictionaryForBindingGroup - :: (MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => Maybe ModuleName - -> [((SourceAnn, Ident), Expr)] - -> TypeCheckM SplitBindingGroup -typeDictionaryForBindingGroup moduleName vals = do - -- Filter the typed and untyped declarations and make a map of names to typed declarations. - -- Replace type wildcards here so that the resulting dictionary of types contains the - -- fully expanded types. - let (untyped, typed) = partitionEithers (map splitTypeAnnotation vals) - (typedDict, typed') <- fmap unzip . for typed $ \(sai, (expr, ty, checkType)) -> do - ((args, elabTy), kind) <- kindOfWithScopedVars ty - checkTypeKind ty kind - elabTy' <- replaceTypeWildcards elabTy - return ((sai, elabTy'), (sai, (expr, args, elabTy', checkType))) - -- Create fresh unification variables for the types of untyped declarations - (untypedDict, untyped') <- fmap unzip . for untyped $ \(sai, expr) -> do - ty <- freshTypeWithKind kindType - return ((sai, ty), (sai, (expr, ty))) - -- Create the dictionary of all name/type pairs, which will be added to the - -- environment during type checking - let dict = M.fromList [ (Qualified (maybe (BySourcePos $ spanStart ss) ByModuleName moduleName) ident, (ty, Private, Undefined)) - | (((ss, _), ident), ty) <- typedDict <> untypedDict - ] - return (SplitBindingGroup untyped' typed' dict) - where - -- Check if a value contains a type annotation, and if so, separate it - -- from the value itself. - splitTypeAnnotation :: (a, Expr) -> Either (a, Expr) (a, (Expr, SourceType, Bool)) - splitTypeAnnotation (a, TypedValue checkType value ty) = Right (a, (value, ty, checkType)) - splitTypeAnnotation (a, PositionedValue pos c value) = - bimap (second (PositionedValue pos c)) - (second (\(e, t, b) -> (PositionedValue pos c e, t, b))) - (splitTypeAnnotation (a, value)) - splitTypeAnnotation (a, value) = Left (a, value) - --- | Check the type annotation of a typed value in a binding group. -checkTypedBindingGroupElement - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => ModuleName - -> ((SourceAnn, Ident), (Expr, [(Text, SourceType)], SourceType, Bool)) - -- ^ The identifier we are trying to define, along with the expression and its type annotation - -> M.Map (Qualified Ident) (SourceType, NameKind, NameVisibility) - -- ^ Names brought into scope in this binding group - -> TypeCheckM ((SourceAnn, Ident), (Expr, SourceType)) -checkTypedBindingGroupElement mn (ident, (val, args, ty, checkType)) dict = do - -- We replace type synonyms _after_ kind-checking, since we don't want type - -- synonym expansion to bring type variables into scope. See #2542. - ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty - -- Check the type with the new names in scope - val' <- if checkType - then withScopedTypeVars mn args $ bindNames dict $ check val ty' - else return (TypedValue' False val ty') - return (ident, (tvToExpr val', ty')) - --- | Infer a type for a value in a binding group which lacks an annotation. -typeForBindingGroupElement - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => ((SourceAnn, Ident), (Expr, SourceType)) - -- ^ The identifier we are trying to define, along with the expression and its assigned type - -- (at this point, this should be a unification variable) - -> M.Map (Qualified Ident) (SourceType, NameKind, NameVisibility) - -- ^ Names brought into scope in this binding group - -> TypeCheckM ((SourceAnn, Ident), (Expr, SourceType)) -typeForBindingGroupElement (ident, (val, ty)) dict = do - -- Infer the type with the new names in scope - TypedValue' _ val' ty' <- bindNames dict $ infer val - -- Unify the type with the unification variable we chose for this definition - unifyTypes ty ty' - return (ident, (TypedValue True val' ty', ty')) - --- | Remove any ForAlls and ConstrainedType constructors in a type by introducing new unknowns --- or TypeClassDictionary values. --- --- This is necessary during type checking to avoid unifying a polymorphic type with a --- unification variable. -instantiatePolyTypeWithUnknowns - :: (MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM) - => Expr - -> SourceType - -> TypeCheckM (Expr, SourceType) -instantiatePolyTypeWithUnknowns val (ForAll _ _ ident mbK ty _) = do - u <- maybe (internalCompilerError "Unelaborated forall") freshTypeWithKind mbK - insertUnkName' u ident - instantiatePolyTypeWithUnknowns val $ replaceTypeVars ident u ty -instantiatePolyTypeWithUnknowns val (ConstrainedType _ con ty) = do - dicts <- getTypeClassDictionaries - hints <- getHints - instantiatePolyTypeWithUnknowns (App val (TypeClassDictionary con dicts hints)) ty -instantiatePolyTypeWithUnknowns val ty = return (val, ty) - -instantiatePolyTypeWithUnknownsUntilVisible - :: (MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM) - => Expr - -> SourceType - -> TypeCheckM (Expr, SourceType) -instantiatePolyTypeWithUnknownsUntilVisible val (ForAll _ TypeVarInvisible ident mbK ty _) = do - u <- maybe (internalCompilerError "Unelaborated forall") freshTypeWithKind mbK - insertUnkName' u ident - instantiatePolyTypeWithUnknownsUntilVisible val $ replaceTypeVars ident u ty -instantiatePolyTypeWithUnknownsUntilVisible val ty = return (val, ty) - -instantiateConstraint :: MonadState CheckState TypeCheckM => Expr -> Type SourceAnn -> TypeCheckM (Expr, Type SourceAnn) -instantiateConstraint val (ConstrainedType _ con ty) = do - dicts <- getTypeClassDictionaries - hints <- getHints - instantiateConstraint (App val (TypeClassDictionary con dicts hints)) ty -instantiateConstraint val ty = pure (val, ty) - --- | Match against TUnknown and call insertUnkName, failing otherwise. -insertUnkName' :: (MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM) => SourceType -> Text -> TypeCheckM () -insertUnkName' (TUnknown _ i) n = insertUnkName i n -insertUnkName' _ _ = internalCompilerError "type is not TUnknown" - --- | Infer a type for a value, rethrowing any error to provide a more useful error message -infer - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => Expr - -> TypeCheckM TypedValue' -infer val = withErrorMessageHint (ErrorInferringType val) $ infer' val - --- | Infer a type for a value -infer' - :: Expr - -> TypeCheckM TypedValue' -infer' v@(Literal _ (NumericLiteral (Left _))) = return $ TypedValue' True v tyInt -infer' v@(Literal _ (NumericLiteral (Right _))) = return $ TypedValue' True v tyNumber -infer' v@(Literal _ (StringLiteral _)) = return $ TypedValue' True v tyString -infer' v@(Literal _ (CharLiteral _)) = return $ TypedValue' True v tyChar -infer' v@(Literal _ (BooleanLiteral _)) = return $ TypedValue' True v tyBoolean -infer' (Literal ss (ArrayLiteral vals)) = do - ts <- traverse infer vals - els <- freshTypeWithKind kindType - ts' <- forM ts $ \(TypedValue' ch val t) -> do - (val', t') <- instantiatePolyTypeWithUnknowns val t - unifyTypes els t' - return (TypedValue ch val' t') - return $ TypedValue' True (Literal ss (ArrayLiteral ts')) (srcTypeApp tyArray els) -infer' (Literal ss (ObjectLiteral ps)) = do - ensureNoDuplicateProperties ps - typedFields <- inferProperties ps - let - toRowListItem :: (PSString, (Expr, SourceType)) -> RowListItem SourceAnn - toRowListItem (l, (_, t)) = srcRowListItem (Label l) t - - recordType :: SourceType - recordType = srcTypeApp tyRecord $ rowFromList (toRowListItem <$> typedFields, srcKindApp srcREmpty kindType) - - typedProperties :: [(PSString, Expr)] - typedProperties = fmap (fmap (uncurry (TypedValue True))) typedFields - pure $ TypedValue' True (Literal ss (ObjectLiteral typedProperties)) recordType -infer' (ObjectUpdate ob ps) = do - ensureNoDuplicateProperties ps - -- This "tail" holds all other fields not being updated. - rowType <- freshTypeWithKind (kindRow kindType) - let updateLabels = Label . fst <$> ps - -- Generate unification variables for each field in ps. - -- - -- Given: - -- - -- ob { a = 0, b = 0 } - -- - -- Then: - -- - -- obTypes = [(a, ?0), (b, ?1)] - obTypes <- zip updateLabels <$> replicateM (length updateLabels) (freshTypeWithKind kindType) - let obItems :: [RowListItem SourceAnn] - obItems = uncurry srcRowListItem <$> obTypes - -- Create a record type that contains the unification variables. - -- - -- obRecordType = Record ( a :: ?0, b :: ?1 | rowType ) - obRecordType :: SourceType - obRecordType = srcTypeApp tyRecord $ rowFromList (obItems, rowType) - -- Check ob against obRecordType. - -- - -- Given: - -- - -- ob : { a :: Int, b :: Int } - -- - -- Then: - -- - -- ?0 ~ Int - -- ?1 ~ Int - -- ob' : { a :: ?0, b :: ?1 } - ob' <- TypedValue True <$> (tvToExpr <$> check ob obRecordType) <*> pure obRecordType - -- Infer the types of the values used for the record update. - typedFields <- inferProperties ps - let newItems :: [RowListItem SourceAnn] - newItems = (\(l, (_, t)) -> srcRowListItem (Label l) t) <$> typedFields - - ps' :: [(PSString, Expr)] - ps' = (\(l, (e, t)) -> (l, TypedValue True e t)) <$> typedFields - - newRecordType :: SourceType - newRecordType = srcTypeApp tyRecord $ rowFromList (newItems, rowType) - pure $ TypedValue' True (ObjectUpdate ob' ps') newRecordType -infer' (Accessor prop val) = withErrorMessageHint (ErrorCheckingAccessor val prop) $ do - field <- freshTypeWithKind kindType - rest <- freshTypeWithKind (kindRow kindType) - typed <- tvToExpr <$> check val (srcTypeApp tyRecord (srcRCons (Label prop) field rest)) - return $ TypedValue' True (Accessor prop typed) field -infer' (Abs binder ret) - | VarBinder ss arg <- binder = do - ty <- freshTypeWithKind kindType - withBindingGroupVisible $ bindLocalVariables [(ss, arg, ty, Defined)] $ do - body@(TypedValue' _ _ bodyTy) <- infer' ret - (body', bodyTy') <- instantiatePolyTypeWithUnknowns (tvToExpr body) bodyTy - return $ TypedValue' True (Abs (VarBinder ss arg) body') (function ty bodyTy') - | otherwise = internalError "Binder was not desugared" -infer' (App f arg) = do - f'@(TypedValue' _ _ ft) <- infer f - (ret, app) <- checkFunctionApplication (tvToExpr f') ft arg - return $ TypedValue' True app ret -infer' (VisibleTypeApp valFn (TypeWildcard _ _)) = do - TypedValue' _ valFn' valTy <- infer valFn - (valFn'', valTy') <- instantiatePolyTypeWithUnknownsUntilVisible valFn' valTy - case valTy' of - ForAll qAnn _ qName qKind qBody qSko -> do - pure $ TypedValue' True valFn'' (ForAll qAnn TypeVarInvisible qName qKind qBody qSko) - _ -> - throwError $ errorMessage $ CannotSkipTypeApplication valTy' -infer' (VisibleTypeApp valFn tyArg) = do - TypedValue' _ valFn' valTy <- infer valFn - tyArg' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ tyArg - (valFn'', valTy') <- instantiatePolyTypeWithUnknownsUntilVisible valFn' valTy - case valTy' of - ForAll _ _ qName (Just qKind) qBody _ -> do - tyArg'' <- replaceAllTypeSynonyms <=< checkKind tyArg' $ qKind - let resTy = replaceTypeVars qName tyArg'' qBody - (valFn''', resTy') <- instantiateConstraint valFn'' resTy - pure $ TypedValue' True valFn''' resTy' - _ -> - throwError $ errorMessage $ CannotApplyExpressionOfTypeOnType valTy tyArg -infer' (Var ss var) = do - checkVisibility var - ty <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards <=< lookupVariable $ var - case ty of - ConstrainedType _ con ty' -> do - dicts <- getTypeClassDictionaries - hints <- getHints - return $ TypedValue' True (App (Var ss var) (TypeClassDictionary con dicts hints)) ty' - _ -> return $ TypedValue' True (Var ss var) ty -infer' v@(Constructor _ c) = do - env <- getEnv - case M.lookup c (dataConstructors env) of - Nothing -> throwError . errorMessage . UnknownName . fmap DctorName $ c - Just (_, _, ty, _) -> TypedValue' True v <$> (introduceSkolemScope <=< replaceAllTypeSynonyms $ ty) -infer' (Case vals binders) = do - (vals', ts) <- instantiateForBinders vals binders - ret <- freshTypeWithKind kindType - binders' <- checkBinders ts ret binders - return $ TypedValue' True (Case vals' binders') ret -infer' (IfThenElse cond th el) = do - cond' <- tvToExpr <$> check cond tyBoolean - th'@(TypedValue' _ _ thTy) <- infer th - el'@(TypedValue' _ _ elTy) <- infer el - (th'', thTy') <- instantiatePolyTypeWithUnknowns (tvToExpr th') thTy - (el'', elTy') <- instantiatePolyTypeWithUnknowns (tvToExpr el') elTy - unifyTypes thTy' elTy' - return $ TypedValue' True (IfThenElse cond' th'' el'') thTy' -infer' (Let w ds val) = do - (ds', tv@(TypedValue' _ _ valTy)) <- inferLetBinding [] ds val infer - return $ TypedValue' True (Let w ds' (tvToExpr tv)) valTy -infer' (DeferredDictionary className tys) = do - dicts <- getTypeClassDictionaries - hints <- getHints - con <- checkConstraint (srcConstraint className [] tys Nothing) - return $ TypedValue' False - (TypeClassDictionary con dicts hints) - (foldl srcTypeApp (srcTypeConstructor (fmap coerceProperName className)) tys) -infer' (TypedValue checkType val ty) = do - moduleName <- unsafeCheckCurrentModule - ((args, elabTy), kind) <- kindOfWithScopedVars ty - checkTypeKind ty kind - ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ elabTy - tv <- if checkType then withScopedTypeVars moduleName args (check val ty') else return (TypedValue' False val ty) - return $ TypedValue' True (tvToExpr tv) ty' -infer' (Hole name) = do - ty <- freshTypeWithKind kindType - ctx <- getLocalContext - env <- getEnv - tell . errorMessage $ HoleInferredType name ty ctx . Just $ TSBefore env - return $ TypedValue' True (Hole name) ty -infer' (PositionedValue pos c val) = warnAndRethrowWithPositionTC pos $ do - TypedValue' t v ty <- infer' val - return $ TypedValue' t (PositionedValue pos c v) ty -infer' v = internalError $ "Invalid argument to infer: " ++ show v - --- | --- Infer the types of named record fields. -inferProperties - :: ( MonadSupply TypeCheckM - , MonadState CheckState TypeCheckM - , MonadError MultipleErrors TypeCheckM - , MonadWriter MultipleErrors TypeCheckM - ) - => [(PSString, Expr)] - -> TypeCheckM [(PSString, (Expr, SourceType))] -inferProperties = traverse (traverse inferWithinRecord) - --- | --- Infer the type of a value when used as a record field. -inferWithinRecord - :: ( MonadSupply TypeCheckM - , MonadState CheckState TypeCheckM - , MonadError MultipleErrors TypeCheckM - , MonadWriter MultipleErrors TypeCheckM - ) - => Expr - -> TypeCheckM (Expr, SourceType) -inferWithinRecord e = do - TypedValue' _ v t <- infer e - if propertyShouldInstantiate e - then instantiatePolyTypeWithUnknowns v t - else pure (v, t) - --- | --- Determines if a value's type needs to be monomorphized when --- used inside of a record. -propertyShouldInstantiate :: Expr -> Bool -propertyShouldInstantiate = \case - Var{} -> True - Constructor{} -> True - VisibleTypeApp e _ -> propertyShouldInstantiate e - PositionedValue _ _ e -> propertyShouldInstantiate e - _ -> False - -inferLetBinding - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => [Declaration] - -> [Declaration] - -> Expr - -> (Expr -> TypeCheckM TypedValue') - -> TypeCheckM ([Declaration], TypedValue') -inferLetBinding seen [] ret j = (seen, ) <$> withBindingGroupVisible (j ret) -inferLetBinding seen (ValueDecl sa@(ss, _) ident nameKind [] [MkUnguarded (TypedValue checkType val ty)] : rest) ret j = do - moduleName <- unsafeCheckCurrentModule - TypedValue' _ val' ty'' <- warnAndRethrowWithPositionTC ss $ do - ((args, elabTy), kind) <- kindOfWithScopedVars ty - checkTypeKind ty kind - let dict = M.singleton (Qualified (BySourcePos $ spanStart ss) ident) (elabTy, nameKind, Undefined) - ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ elabTy - if checkType - then withScopedTypeVars moduleName args (bindNames dict (check val ty')) - else return (TypedValue' checkType val elabTy) - bindNames (M.singleton (Qualified (BySourcePos $ spanStart ss) ident) (ty'', nameKind, Defined)) - $ inferLetBinding (seen ++ [ValueDecl sa ident nameKind [] [MkUnguarded (TypedValue checkType val' ty'')]]) rest ret j -inferLetBinding seen (ValueDecl sa@(ss, _) ident nameKind [] [MkUnguarded val] : rest) ret j = do - valTy <- freshTypeWithKind kindType - TypedValue' _ val' valTy' <- warnAndRethrowWithPositionTC ss $ do - let dict = M.singleton (Qualified (BySourcePos $ spanStart ss) ident) (valTy, nameKind, Undefined) - bindNames dict $ infer val - warnAndRethrowWithPositionTC ss $ unifyTypes valTy valTy' - bindNames (M.singleton (Qualified (BySourcePos $ spanStart ss) ident) (valTy', nameKind, Defined)) - $ inferLetBinding (seen ++ [ValueDecl sa ident nameKind [] [MkUnguarded val']]) rest ret j -inferLetBinding seen (BindingGroupDeclaration ds : rest) ret j = do - moduleName <- unsafeCheckCurrentModule - SplitBindingGroup untyped typed dict <- typeDictionaryForBindingGroup Nothing . NEL.toList $ fmap (\(i, _, v) -> (i, v)) ds - ds1' <- parU typed $ \e -> checkTypedBindingGroupElement moduleName e dict - ds2' <- forM untyped $ \e -> typeForBindingGroupElement e dict - let ds' = NEL.fromList [(ident, Private, val') | (ident, (val', _)) <- ds1' ++ ds2'] - bindNames dict $ do - makeBindingGroupVisible - inferLetBinding (seen ++ [BindingGroupDeclaration ds']) rest ret j -inferLetBinding _ _ _ _ = internalError "Invalid argument to inferLetBinding" - --- | Infer the types of variables brought into scope by a binder -inferBinder - :: SourceType - -> Binder - -> TypeCheckM (M.Map Ident (SourceSpan, SourceType)) -inferBinder _ NullBinder = return M.empty -inferBinder val (LiteralBinder _ (StringLiteral _)) = unifyTypes val tyString >> return M.empty -inferBinder val (LiteralBinder _ (CharLiteral _)) = unifyTypes val tyChar >> return M.empty -inferBinder val (LiteralBinder _ (NumericLiteral (Left _))) = unifyTypes val tyInt >> return M.empty -inferBinder val (LiteralBinder _ (NumericLiteral (Right _))) = unifyTypes val tyNumber >> return M.empty -inferBinder val (LiteralBinder _ (BooleanLiteral _)) = unifyTypes val tyBoolean >> return M.empty -inferBinder val (VarBinder ss name) = return $ M.singleton name (ss, val) -inferBinder val (ConstructorBinder ss ctor binders) = do - env <- getEnv - case M.lookup ctor (dataConstructors env) of - Just (_, _, ty, _) -> do - (_, fn) <- instantiatePolyTypeWithUnknowns (internalError "Data constructor types cannot contain constraints") ty - fn' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ fn - let (args, ret) = peelArgs fn' - expected = length args - actual = length binders - unless (expected == actual) . throwError . errorMessage' ss $ IncorrectConstructorArity ctor expected actual - unifyTypes ret val - M.unions <$> zipWithM inferBinder (reverse args) binders - _ -> throwError . errorMessage' ss . UnknownName . fmap DctorName $ ctor - where - peelArgs :: Type a -> ([Type a], Type a) - peelArgs = go [] - where - go args (TypeApp _ (TypeApp _ fn arg) ret) | eqType fn tyFunction = go (arg : args) ret - go args ret = (args, ret) -inferBinder val (LiteralBinder _ (ObjectLiteral props)) = do - row <- freshTypeWithKind (kindRow kindType) - rest <- freshTypeWithKind (kindRow kindType) - m1 <- inferRowProperties row rest props - unifyTypes val (srcTypeApp tyRecord row) - return m1 - where - inferRowProperties :: SourceType -> SourceType -> [(PSString, Binder)] -> TypeCheckM (M.Map Ident (SourceSpan, SourceType)) - inferRowProperties nrow row [] = unifyTypes nrow row >> return M.empty - inferRowProperties nrow row ((name, binder):binders) = do - propTy <- freshTypeWithKind kindType - m1 <- inferBinder propTy binder - m2 <- inferRowProperties nrow (srcRCons (Label name) propTy row) binders - return $ m1 `M.union` m2 -inferBinder val (LiteralBinder _ (ArrayLiteral binders)) = do - el <- freshTypeWithKind kindType - m1 <- M.unions <$> traverse (inferBinder el) binders - unifyTypes val (srcTypeApp tyArray el) - return m1 -inferBinder val (NamedBinder ss name binder) = - warnAndRethrowWithPositionTC ss $ do - m <- inferBinder val binder - return $ M.insert name (ss, val) m -inferBinder val (PositionedBinder pos _ binder) = - warnAndRethrowWithPositionTC pos $ inferBinder val binder -inferBinder val (TypedBinder ty binder) = do - (elabTy, kind) <- kindOf ty - checkTypeKind ty kind - ty1 <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ elabTy - unifyTypes val ty1 - inferBinder ty1 binder -inferBinder _ OpBinder{} = - internalError "OpBinder should have been desugared before inferBinder" -inferBinder _ BinaryNoParensBinder{} = - internalError "BinaryNoParensBinder should have been desugared before inferBinder" -inferBinder _ ParensInBinder{} = - internalError "ParensInBinder should have been desugared before inferBinder" - --- | Returns true if a binder requires its argument type to be a monotype. --- | If this is the case, we need to instantiate any polymorphic types before checking binders. -binderRequiresMonotype :: Binder -> Bool -binderRequiresMonotype NullBinder = False -binderRequiresMonotype (VarBinder _ _) = False -binderRequiresMonotype (NamedBinder _ _ b) = binderRequiresMonotype b -binderRequiresMonotype (PositionedBinder _ _ b) = binderRequiresMonotype b -binderRequiresMonotype (TypedBinder ty b) = isMonoType ty || binderRequiresMonotype b -binderRequiresMonotype _ = True - --- | Instantiate polytypes only when necessitated by a binder. -instantiateForBinders - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => [Expr] - -> [CaseAlternative] - -> TypeCheckM ([Expr], [SourceType]) -instantiateForBinders vals cas = unzip <$> zipWithM (\val inst -> do - TypedValue' _ val' ty <- infer val - if inst - then instantiatePolyTypeWithUnknowns val' ty - else return (val', ty)) vals shouldInstantiate - where - shouldInstantiate :: [Bool] - shouldInstantiate = map (any binderRequiresMonotype) . transpose . map caseAlternativeBinders $ cas - --- | --- Check the types of the return values in a set of binders in a case statement --- -checkBinders - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => [SourceType] - -> SourceType - -> [CaseAlternative] - -> TypeCheckM [CaseAlternative] -checkBinders _ _ [] = return [] -checkBinders nvals ret (CaseAlternative binders result : bs) = do - guardWith (errorMessage $ OverlappingArgNames Nothing) $ - let ns = concatMap binderNames binders in length (ordNub ns) == length ns - m1 <- M.unions <$> zipWithM inferBinder nvals binders - r <- bindLocalVariables [ (ss, name, ty, Defined) | (name, (ss, ty)) <- M.toList m1 ] $ - CaseAlternative binders <$> forM result (\ge -> checkGuardedRhs ge ret) - rs <- checkBinders nvals ret bs - return $ r : rs - -checkGuardedRhs - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => GuardedExpr - -> SourceType - -> TypeCheckM GuardedExpr -checkGuardedRhs (GuardedExpr [] rhs) ret = do - rhs' <- TypedValue True <$> (tvToExpr <$> check rhs ret) <*> pure ret - return $ GuardedExpr [] rhs' -checkGuardedRhs (GuardedExpr (ConditionGuard cond : guards) rhs) ret = do - cond' <- withErrorMessageHint ErrorCheckingGuard $ check cond tyBoolean - GuardedExpr guards' rhs' <- checkGuardedRhs (GuardedExpr guards rhs) ret - return $ GuardedExpr (ConditionGuard (tvToExpr cond') : guards') rhs' -checkGuardedRhs (GuardedExpr (PatternGuard binder expr : guards) rhs) ret = do - tv@(TypedValue' _ _ ty) <- infer expr - variables <- inferBinder ty binder - GuardedExpr guards' rhs' <- bindLocalVariables [ (ss, name, bty, Defined) - | (name, (ss, bty)) <- M.toList variables - ] $ - checkGuardedRhs (GuardedExpr guards rhs) ret - return $ GuardedExpr (PatternGuard binder (tvToExpr tv) : guards') rhs' - --- | --- Check the type of a value, rethrowing errors to provide a better error message --- -check - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => Expr - -> SourceType - -> TypeCheckM TypedValue' -check val ty = withErrorMessageHint' val (ErrorCheckingType val ty) $ check' val ty - --- | --- Check the type of a value --- -check' - :: Expr - -> SourceType - -> TypeCheckM TypedValue' -check' val (ForAll ann vis ident mbK ty _) = do - env <- getEnv - mn <- gets checkCurrentModule - scope <- newSkolemScope - sko <- newSkolemConstant - let ss = case val of - PositionedValue pos c _ -> (pos, c) - _ -> NullSourceAnn - sk = skolemize ss ident mbK sko scope ty - -- We should only skolemize types in values when the type variable - -- was actually brought into scope. Otherwise we can end up skolemizing - -- an undefined type variable that happens to clash with the variable we - -- want to skolemize. This can happen due to synonym expansion (see 2542). - skVal - | Just _ <- M.lookup (Qualified (byMaybeModuleName mn) (ProperName ident)) $ types env = - skolemizeTypesInValue ss ident mbK sko scope val - | otherwise = val - val' <- tvToExpr <$> check skVal sk - return $ TypedValue' True val' (ForAll ann vis ident mbK ty (Just scope)) -check' val t@(ConstrainedType _ con@(Constraint _ cls@(Qualified _ (ProperName className)) _ _ _) ty) = do - TypeClassData{ typeClassIsEmpty } <- lookupTypeClass cls - -- An empty class dictionary is never used; see code in `TypeChecker.Entailment` - -- that wraps empty dictionary solutions in `Unused`. - dictName <- if typeClassIsEmpty then pure UnusedIdent else freshIdent ("dict" <> className) - dicts <- newDictionaries [] (Qualified ByNullSourcePos dictName) con - val' <- withBindingGroupVisible $ withTypeClassDictionaries dicts $ check val ty - return $ TypedValue' True (Abs (VarBinder nullSourceSpan dictName) (tvToExpr val')) t -check' val u@(TUnknown _ _) = do - val'@(TypedValue' _ _ ty) <- infer val - -- Don't unify an unknown with an inferred polytype - (val'', ty') <- instantiatePolyTypeWithUnknowns (tvToExpr val') ty - unifyTypes ty' u - return $ TypedValue' True val'' ty' -check' v@(Literal _ (NumericLiteral (Left _))) t | t == tyInt = - return $ TypedValue' True v t -check' v@(Literal _ (NumericLiteral (Right _))) t | t == tyNumber = - return $ TypedValue' True v t -check' v@(Literal _ (StringLiteral _)) t | t == tyString = - return $ TypedValue' True v t -check' v@(Literal _ (CharLiteral _)) t | t == tyChar = - return $ TypedValue' True v t -check' v@(Literal _ (BooleanLiteral _)) t | t == tyBoolean = - return $ TypedValue' True v t -check' (Literal ss (ArrayLiteral vals)) t@(TypeApp _ a ty) = do - unifyTypes a tyArray - array <- Literal ss . ArrayLiteral . map tvToExpr <$> forM vals (`check` ty) - return $ TypedValue' True array t -check' (Abs binder ret) ty@(TypeApp _ (TypeApp _ t argTy) retTy) - | VarBinder ss arg <- binder = do - unifyTypes t tyFunction - ret' <- withBindingGroupVisible $ bindLocalVariables [(ss, arg, argTy, Defined)] $ check ret retTy - return $ TypedValue' True (Abs (VarBinder ss arg) (tvToExpr ret')) ty - | otherwise = internalError "Binder was not desugared" -check' (App f arg) ret = do - f'@(TypedValue' _ _ ft) <- infer f - (retTy, app) <- checkFunctionApplication (tvToExpr f') ft arg - elaborate <- subsumes retTy ret - return $ TypedValue' True (elaborate app) ret -check' v@(Var _ var) ty = do - checkVisibility var - repl <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< lookupVariable $ var - ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ ty - elaborate <- subsumes repl ty' - return $ TypedValue' True (elaborate v) ty' -check' (DeferredDictionary className tys) ty = do - {- - -- Here, we replace a placeholder for a superclass dictionary with a regular - -- TypeClassDictionary placeholder. The reason we do this is that it is necessary to have the - -- correct super instance dictionaries in scope, and these are not available when the type class - -- declaration gets desugared. - -} - dicts <- getTypeClassDictionaries - hints <- getHints - con <- checkConstraint (srcConstraint className [] tys Nothing) - return $ TypedValue' False - (TypeClassDictionary con dicts hints) - ty -check' (TypedValue checkType val ty1) ty2 = do - moduleName <- unsafeCheckCurrentModule - ((args, elabTy1), kind1) <- kindOfWithScopedVars ty1 - (elabTy2, kind2) <- kindOf ty2 - unifyKinds' kind1 kind2 - checkTypeKind ty1 kind1 - ty1' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ elabTy1 - ty2' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ elabTy2 - elaborate <- subsumes ty1' ty2' - val' <- if checkType - then withScopedTypeVars moduleName args $ tvToExpr <$> check val ty1' - else pure val - return $ TypedValue' True (TypedValue checkType (elaborate val') ty1') ty2' -check' (Case vals binders) ret = do - (vals', ts) <- instantiateForBinders vals binders - binders' <- checkBinders ts ret binders - return $ TypedValue' True (Case vals' binders') ret -check' (IfThenElse cond th el) ty = do - cond' <- tvToExpr <$> check cond tyBoolean - th' <- tvToExpr <$> check th ty - el' <- tvToExpr <$> check el ty - return $ TypedValue' True (IfThenElse cond' th' el') ty -check' e@(Literal ss (ObjectLiteral ps)) t@(TypeApp _ obj row) | obj == tyRecord = do - ensureNoDuplicateProperties ps - ps' <- checkProperties e ps row False - return $ TypedValue' True (Literal ss (ObjectLiteral ps')) t -check' (DerivedInstancePlaceholder name strategy) t = do - d <- deriveInstance t name strategy - d' <- tvToExpr <$> check' d t - return $ TypedValue' True d' t -check' e@(ObjectUpdate obj ps) t@(TypeApp _ o row) | o == tyRecord = do - ensureNoDuplicateProperties ps - -- We need to be careful to avoid duplicate labels here. - -- We check _obj_ against the type _t_ with the types in _ps_ replaced with unknowns. - let (propsToCheck, rest) = rowToList row - (removedProps, remainingProps) = partition (\(RowListItem _ p _) -> p `elem` map (Label . fst) ps) propsToCheck - us <- zipWith srcRowListItem (map rowListLabel removedProps) <$> replicateM (length ps) (freshTypeWithKind kindType) - obj' <- tvToExpr <$> check obj (srcTypeApp tyRecord (rowFromList (us ++ remainingProps, rest))) - ps' <- checkProperties e ps row True - return $ TypedValue' True (ObjectUpdate obj' ps') t -check' (Accessor prop val) ty = withErrorMessageHint (ErrorCheckingAccessor val prop) $ do - rest <- freshTypeWithKind (kindRow kindType) - val' <- tvToExpr <$> check val (srcTypeApp tyRecord (srcRCons (Label prop) ty rest)) - return $ TypedValue' True (Accessor prop val') ty -check' v@(Constructor _ c) ty = do - env <- getEnv - case M.lookup c (dataConstructors env) of - Nothing -> throwError . errorMessage . UnknownName . fmap DctorName $ c - Just (_, _, ty1, _) -> do - repl <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty1 - ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty - elaborate <- subsumes repl ty' - return $ TypedValue' True (elaborate v) ty' -check' (Let w ds val) ty = do - (ds', val') <- inferLetBinding [] ds val (`check` ty) - return $ TypedValue' True (Let w ds' (tvToExpr val')) ty -check' val kt@(KindedType _ ty kind) = do - checkTypeKind ty kind - val' <- tvToExpr <$> check' val ty - return $ TypedValue' True val' kt -check' (PositionedValue pos c val) ty = warnAndRethrowWithPositionTC pos $ do - TypedValue' t v ty' <- check' val ty - return $ TypedValue' t (PositionedValue pos c v) ty' -check' val ty = do - TypedValue' _ val' ty' <- infer val - elaborate <- subsumes ty' ty - return $ TypedValue' True (elaborate val') ty - --- | --- Check the type of a collection of named record fields --- --- The @lax@ parameter controls whether or not every record member has to be provided. For object updates, this is not the case. --- -checkProperties - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => Expr - -> [(PSString, Expr)] - -> SourceType - -> Bool - -> TypeCheckM [(PSString, Expr)] -checkProperties expr ps row lax = convert <$> go ps (toRowPair <$> ts') r' where - convert = fmap (fmap tvToExpr) - (ts', r') = rowToList row - toRowPair (RowListItem _ lbl ty) = (lbl, ty) - go [] [] (REmptyKinded _ _) = return [] - go [] [] u@(TUnknown _ _) - | lax = return [] - | otherwise = do unifyTypes u srcREmpty - return [] - go [] [] Skolem{} | lax = return [] - go [] ((p, _): _) _ | lax = return [] - | otherwise = throwError . errorMessage $ PropertyIsMissing p - go ((p,_):_) [] (REmptyKinded _ _) = throwError . errorMessage $ AdditionalProperty $ Label p - go ((p,v):ps') ts r = - case lookup (Label p) ts of - Nothing -> do - (v', ty) <- inferWithinRecord v - rest <- freshTypeWithKind (kindRow kindType) - unifyTypes r (srcRCons (Label p) ty rest) - ps'' <- go ps' ts rest - return $ (p, TypedValue' True v' ty) : ps'' - Just ty -> do - v' <- check v ty - ps'' <- go ps' (delete (Label p, ty) ts) r - return $ (p, v') : ps'' - go _ _ _ = throwError . errorMessage $ ExprDoesNotHaveType expr (srcTypeApp tyRecord row) - --- | Check the type of a function application, rethrowing errors to provide a better error message. --- --- This judgment takes three inputs: --- --- * The expression of the function we are applying --- * The type of that function --- * The expression we are applying it to --- --- and synthesizes two outputs: --- --- * The return type --- * The elaborated expression for the function application (since we might need to --- insert type class dictionaries, etc.) -checkFunctionApplication - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => Expr - -- ^ The function expression - -> SourceType - -- ^ The type of the function - -> Expr - -- ^ The argument expression - -> TypeCheckM (SourceType, Expr) - -- ^ The result type, and the elaborated term -checkFunctionApplication fn fnTy arg = withErrorMessageHint' fn (ErrorInApplication fn fnTy arg) $ do - subst <- gets checkSubstitution - checkFunctionApplication' fn (substituteType subst fnTy) arg - --- | Check the type of a function application -checkFunctionApplication' - :: (MonadSupply TypeCheckM, MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM, MonadWriter MultipleErrors TypeCheckM) - => Expr - -> SourceType - -> Expr - -> TypeCheckM (SourceType, Expr) -checkFunctionApplication' fn (TypeApp _ (TypeApp _ tyFunction' argTy) retTy) arg = do - unifyTypes tyFunction' tyFunction - arg' <- tvToExpr <$> check arg argTy - return (retTy, App fn arg') -checkFunctionApplication' fn (ForAll _ _ ident mbK ty _) arg = do - u <- maybe (internalCompilerError "Unelaborated forall") freshTypeWithKind mbK - insertUnkName' u ident - let replaced = replaceTypeVars ident u ty - checkFunctionApplication fn replaced arg -checkFunctionApplication' fn (KindedType _ ty _) arg = - checkFunctionApplication fn ty arg -checkFunctionApplication' fn (ConstrainedType _ con fnTy) arg = do - dicts <- getTypeClassDictionaries - hints <- getHints - checkFunctionApplication' (App fn (TypeClassDictionary con dicts hints)) fnTy arg -checkFunctionApplication' fn fnTy dict@TypeClassDictionary{} = - return (fnTy, App fn dict) -checkFunctionApplication' fn u arg = do - tv@(TypedValue' _ _ ty) <- do - TypedValue' _ arg' t <- infer arg - (arg'', t') <- instantiatePolyTypeWithUnknowns arg' t - return $ TypedValue' True arg'' t' - ret <- freshTypeWithKind kindType - unifyTypes u (function ty ret) - return (ret, App fn (tvToExpr tv)) - --- | --- Ensure a set of property names and value does not contain duplicate labels --- -ensureNoDuplicateProperties :: (MonadError MultipleErrors TypeCheckM) => [(PSString, Expr)] -> TypeCheckM () -ensureNoDuplicateProperties ps = - let ls = map fst ps in - case ls \\ ordNub ls of - l : _ -> throwError . errorMessage $ DuplicateLabel (Label l) Nothing - _ -> return () - --- | Test if this is an internal value to be excluded from error hints -isInternal :: Expr -> Bool -isInternal = \case - PositionedValue _ _ v -> isInternal v - TypedValue _ v _ -> isInternal v - Constructor _ (Qualified _ name) -> isDictTypeName name - DerivedInstancePlaceholder{} -> True - _ -> False - --- | Introduce a hint only if the given expression is not internal -withErrorMessageHint' - :: (MonadState CheckState TypeCheckM, MonadError MultipleErrors TypeCheckM) - => Expr - -> ErrorMessageHint - -> TypeCheckM a - -> TypeCheckM a -withErrorMessageHint' expr = if isInternal expr then const id else withErrorMessageHint diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Unify.hs b/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Unify.hs deleted file mode 100644 index d47cd91d..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeChecker/Unify.hs +++ /dev/null @@ -1,230 +0,0 @@ --- | --- Functions and instances relating to unification --- -module Language.PureScript.TypeChecker.Unify - ( freshType - , freshTypeWithKind - , solveType - , substituteType - , unknownsInType - , unifyTypes - , unifyRows - , alignRowsWith - , replaceTypeWildcards - , varIfUnknown - ) where - -import Prelude - -import Control.Monad (forM_, void, when) -import Control.Monad.Error.Class (MonadError(..)) -import Control.Monad.State.Class (MonadState(..), gets, modify, state) -import Control.Monad.Writer.Class (MonadWriter(..)) - -import Data.Foldable (traverse_) -import Data.Maybe (fromMaybe) -import Data.IntMap.Lazy qualified as IM -import Data.Text qualified as T - -import Language.PureScript.Crash (internalError) -import Language.PureScript.Environment qualified as E -import Language.PureScript.Errors (ErrorMessageHint(..), SimpleErrorMessage(..), SourceAnn, errorMessage, internalCompilerError, onErrorMessages, rethrow, warnWithPosition, withoutPosition) -import Language.PureScript.TypeChecker.Kinds (elaborateKind, instantiateKind, unifyKinds') -import Language.PureScript.TypeChecker.Monad (CheckState(..), Substitution(..), UnkLevel(..), Unknown, getLocalContext, guardWith, lookupUnkName, withErrorMessageHint, TypeCheckM) -import Language.PureScript.TypeChecker.Skolems (newSkolemConstant, skolemize) -import Language.PureScript.Types (Constraint(..), pattern REmptyKinded, RowListItem(..), SourceType, Type(..), WildcardData(..), alignRowsWith, everythingOnTypes, everywhereOnTypes, everywhereOnTypesM, getAnnForType, mkForAll, rowFromList, srcTUnknown) -import Data.Set qualified as S - --- | Generate a fresh type variable with an unknown kind. Avoid this if at all possible. -freshType :: TypeCheckM SourceType -freshType = state $ \st -> do - let - t = checkNextType st - st' = st { checkNextType = t + 2 - , checkSubstitution = - (checkSubstitution st) { substUnsolved = IM.insert t (UnkLevel (pure t), E.kindType) - . IM.insert (t + 1) (UnkLevel (pure (t + 1)), srcTUnknown t) - . substUnsolved - $ checkSubstitution st - } - } - (srcTUnknown (t + 1), st') - --- | Generate a fresh type variable with a known kind. -freshTypeWithKind :: SourceType -> TypeCheckM SourceType -freshTypeWithKind kind = state $ \st -> do - let - t = checkNextType st - st' = st { checkNextType = t + 1 - , checkSubstitution = - (checkSubstitution st) { substUnsolved = IM.insert t (UnkLevel (pure t), kind) (substUnsolved (checkSubstitution st)) } - } - (srcTUnknown t, st') - --- | Update the substitution to solve a type constraint -solveType :: Int -> SourceType -> TypeCheckM () -solveType u t = rethrow (onErrorMessages withoutPosition) $ do - -- We strip the position so that any errors get rethrown with the position of - -- the original unification constraint. Otherwise errors may arise from arbitrary - -- locations. We don't otherwise have the "correct" position on hand, since it - -- is maintained as part of the type-checker stack. - occursCheck u t - k1 <- elaborateKind t - subst <- gets checkSubstitution - k2 <- maybe (internalCompilerError ("No kind for unification variable ?" <> T.pack (show u))) (pure . substituteType subst . snd) . IM.lookup u . substUnsolved $ subst - t' <- instantiateKind (t, k1) k2 - modify $ \cs -> cs { checkSubstitution = - (checkSubstitution cs) { substType = - IM.insert u t' $ substType $ checkSubstitution cs - } - } - --- | Apply a substitution to a type -substituteType :: Substitution -> SourceType -> SourceType -substituteType sub = everywhereOnTypes go - where - go (TUnknown ann u) = - case IM.lookup u (substType sub) of - Nothing -> TUnknown ann u - Just (TUnknown ann' u1) | u1 == u -> TUnknown ann' u1 - Just t -> substituteType sub t - go other = other - --- | Make sure that an unknown does not occur in a type -occursCheck :: Int -> SourceType -> TypeCheckM () -occursCheck _ TUnknown{} = return () -occursCheck u t = void $ everywhereOnTypesM go t - where - go (TUnknown _ u') | u == u' = throwError . errorMessage . InfiniteType $ t - go other = return other - --- | Compute a list of all unknowns appearing in a type -unknownsInType :: Type a -> [(a, Int)] -unknownsInType t = everythingOnTypes (.) go t [] - where - go :: Type a -> [(a, Int)] -> [(a, Int)] - go (TUnknown ann u) = ((ann, u) :) - go _ = id - --- | Unify two types, updating the current substitution -unifyTypes :: SourceType -> SourceType -> TypeCheckM () -unifyTypes t1 t2 | t1 == t2 = return () -unifyTypes t1 t2 = do - sub <- gets checkSubstitution - withErrorMessageHint (ErrorUnifyingTypes t1 t2) $ unifyTypes'' (substituteType sub t1) (substituteType sub t2) - where - unifyTypes'' t1' t2'= do - cache <- gets unificationCache - when (S.notMember (t1', t2') cache) $ do - modify $ \st -> st { unificationCache = S.insert (t1', t2') cache } - unifyTypes' t1' t2' - unifyTypes' (TUnknown _ u1) (TUnknown _ u2) | u1 == u2 = return () - unifyTypes' (TUnknown _ u) t = solveType u t - unifyTypes' t (TUnknown _ u) = solveType u t - unifyTypes' (ForAll ann1 _ ident1 mbK1 ty1 sc1) (ForAll ann2 _ ident2 mbK2 ty2 sc2) = - case (sc1, sc2) of - (Just sc1', Just sc2') -> do - sko <- newSkolemConstant - let sk1 = skolemize ann1 ident1 mbK1 sko sc1' ty1 - let sk2 = skolemize ann2 ident2 mbK2 sko sc2' ty2 - sk1 `unifyTypes` sk2 - _ -> internalError "unifyTypes: unspecified skolem scope" - unifyTypes' (ForAll ann _ ident mbK ty1 (Just sc)) ty2 = do - sko <- newSkolemConstant - let sk = skolemize ann ident mbK sko sc ty1 - sk `unifyTypes` ty2 - unifyTypes' ForAll{} _ = internalError "unifyTypes: unspecified skolem scope" - unifyTypes' ty f@ForAll{} = f `unifyTypes` ty - unifyTypes' (TypeVar _ v1) (TypeVar _ v2) | v1 == v2 = return () - unifyTypes' ty1@(TypeConstructor _ c1) ty2@(TypeConstructor _ c2) = - guardWith (errorMessage (TypesDoNotUnify ty1 ty2)) (c1 == c2) - unifyTypes' (TypeLevelString _ s1) (TypeLevelString _ s2) | s1 == s2 = return () - unifyTypes' (TypeLevelInt _ n1) (TypeLevelInt _ n2) | n1 == n2 = return () - unifyTypes' (TypeApp _ t3 t4) (TypeApp _ t5 t6) = do - t3 `unifyTypes` t5 - t4 `unifyTypes` t6 - unifyTypes' (KindApp _ t3 t4) (KindApp _ t5 t6) = do - t3 `unifyKinds'` t5 - t4 `unifyTypes` t6 - unifyTypes' (Skolem _ _ _ s1 _) (Skolem _ _ _ s2 _) | s1 == s2 = return () - unifyTypes' (KindedType _ ty1 _) ty2 = ty1 `unifyTypes` ty2 - unifyTypes' ty1 (KindedType _ ty2 _) = ty1 `unifyTypes` ty2 - unifyTypes' r1@RCons{} r2 = unifyRows r1 r2 - unifyTypes' r1 r2@RCons{} = unifyRows r1 r2 - unifyTypes' r1@REmptyKinded{} r2 = unifyRows r1 r2 - unifyTypes' r1 r2@REmptyKinded{} = unifyRows r1 r2 - unifyTypes' (ConstrainedType _ c1 ty1) (ConstrainedType _ c2 ty2) - | constraintClass c1 == constraintClass c2 && constraintData c1 == constraintData c2 = do - traverse_ (uncurry unifyTypes) (constraintArgs c1 `zip` constraintArgs c2) - ty1 `unifyTypes` ty2 - unifyTypes' ty1@ConstrainedType{} ty2 = - throwError . errorMessage $ ConstrainedTypeUnified ty1 ty2 - unifyTypes' t3 t4@ConstrainedType{} = unifyTypes' t4 t3 - unifyTypes' t3 t4 = - throwError . errorMessage $ TypesDoNotUnify t3 t4 - --- | Unify two rows, updating the current substitution --- --- Common labels are identified and unified. Remaining labels and types are unified with a --- trailing row unification variable, if appropriate. -unifyRows :: SourceType -> SourceType -> TypeCheckM () -unifyRows r1 r2 = sequence_ matches *> uncurry unifyTails rest where - unifyTypesWithLabel l t1 t2 = withErrorMessageHint (ErrorInRowLabel l) $ unifyTypes t1 t2 - - (matches, rest) = alignRowsWith unifyTypesWithLabel r1 r2 - - unifyTails :: ([RowListItem SourceAnn], SourceType) -> ([RowListItem SourceAnn], SourceType) -> TypeCheckM () - unifyTails ([], TUnknown _ u) (sd, r) = solveType u (rowFromList (sd, r)) - unifyTails (sd, r) ([], TUnknown _ u) = solveType u (rowFromList (sd, r)) - unifyTails ([], REmptyKinded _ _) ([], REmptyKinded _ _) = return () - unifyTails ([], TypeVar _ v1) ([], TypeVar _ v2) | v1 == v2 = return () - unifyTails ([], Skolem _ _ _ s1 _) ([], Skolem _ _ _ s2 _) | s1 == s2 = return () - unifyTails (sd1, TUnknown a u1) (sd2, TUnknown _ u2) | u1 /= u2 = do - forM_ sd1 $ occursCheck u2 . rowListType - forM_ sd2 $ occursCheck u1 . rowListType - rest' <- freshTypeWithKind =<< elaborateKind (TUnknown a u1) - solveType u1 (rowFromList (sd2, rest')) - solveType u2 (rowFromList (sd1, rest')) - unifyTails _ _ = - throwError . errorMessage $ TypesDoNotUnify r1 r2 - --- | --- Replace type wildcards with unknowns --- -replaceTypeWildcards :: SourceType -> TypeCheckM SourceType -replaceTypeWildcards = everywhereOnTypesM replace - where - replace (TypeWildcard ann wdata) = do - t <- freshType - ctx <- getLocalContext - let err = case wdata of - HoleWildcard n -> Just $ HoleInferredType n t ctx Nothing - UnnamedWildcard -> Just $ WildcardInferredType t ctx - IgnoredWildcard -> Nothing - forM_ err $ warnWithPosition (fst ann) . tell . errorMessage - return t - replace other = return other - --- | --- Replace outermost unsolved unification variables with named type variables --- -varIfUnknown :: [(Unknown, SourceType)] -> SourceType -> TypeCheckM SourceType -varIfUnknown unks ty = do - bn' <- traverse toBinding unks - ty' <- go ty - pure $ mkForAll bn' ty' - where - toName :: Unknown -> TypeCheckM T.Text - toName u = (<> T.pack (show u)) . fromMaybe "t" <$> lookupUnkName u - - toBinding :: (Unknown, SourceType) -> TypeCheckM (SourceAnn, (T.Text, Maybe SourceType)) - toBinding (u, k) = do - u' <- toName u - k' <- go k - pure (getAnnForType ty, (u', Just k')) - - go :: SourceType -> TypeCheckM SourceType - go = everywhereOnTypesM $ \case - (TUnknown ann u) -> - TypeVar ann <$> toName u - t -> pure t diff --git a/claude-help/original-compiler/src/Language/PureScript/TypeClassDictionaries.hs b/claude-help/original-compiler/src/Language/PureScript/TypeClassDictionaries.hs deleted file mode 100644 index 593e8c1a..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/TypeClassDictionaries.hs +++ /dev/null @@ -1,49 +0,0 @@ -module Language.PureScript.TypeClassDictionaries where - -import Prelude - -import GHC.Generics (Generic) -import Control.DeepSeq (NFData) -import Data.Text (Text, pack) - -import Language.PureScript.AST.Declarations.ChainId (ChainId) -import Language.PureScript.Names (Ident, ProperName(..), ProperNameType(..), Qualified, disqualify) -import Language.PureScript.Types (SourceConstraint, SourceType) - --- --- Data representing a type class dictionary which is in scope --- -data TypeClassDictionaryInScope v - = TypeClassDictionaryInScope { - -- | The instance chain - tcdChain :: Maybe ChainId - -- | Index of the instance chain - , tcdIndex :: Integer - -- | The value with which the dictionary can be accessed at runtime - , tcdValue :: v - -- | How to obtain this instance via superclass relationships - , tcdPath :: [(Qualified (ProperName 'ClassName), Integer)] - -- | The name of the type class to which this type class instance applies - , tcdClassName :: Qualified (ProperName 'ClassName) - -- | Quantification of type variables in the instance head and dependencies - , tcdForAll :: [(Text, SourceType)] - -- | The kinds to which this type class instance applies - , tcdInstanceKinds :: [SourceType] - -- | The types to which this type class instance applies - , tcdInstanceTypes :: [SourceType] - -- | Type class dependencies which must be satisfied to construct this dictionary - , tcdDependencies :: Maybe [SourceConstraint] - -- | If this instance was unnamed, the type to use when describing it in - -- error messages - , tcdDescription :: Maybe SourceType - } - deriving (Show, Functor, Foldable, Traversable, Generic) - -instance NFData v => NFData (TypeClassDictionaryInScope v) - -type NamedDict = TypeClassDictionaryInScope (Qualified Ident) - --- | Generate a name for a superclass reference which can be used in --- generated code. -superclassName :: Qualified (ProperName 'ClassName) -> Integer -> Text -superclassName pn index = runProperName (disqualify pn) <> pack (show index) diff --git a/claude-help/original-compiler/src/Language/PureScript/Types.hs b/claude-help/original-compiler/src/Language/PureScript/Types.hs deleted file mode 100644 index 063c1ebc..00000000 --- a/claude-help/original-compiler/src/Language/PureScript/Types.hs +++ /dev/null @@ -1,878 +0,0 @@ --- | --- Data types for types --- -module Language.PureScript.Types where - -import Prelude -import Protolude (ordNub, fromMaybe) - -import Codec.Serialise (Serialise) -import Control.Applicative ((<|>)) -import Control.Arrow (first, second) -import Control.DeepSeq (NFData) -import Control.Lens (Lens', (^.), set) -import Control.Monad ((<=<), (>=>)) -import Data.Aeson ((.:), (.:?), (.!=), (.=)) -import Data.Aeson qualified as A -import Data.Aeson.Types qualified as A -import Data.Foldable (fold, foldl') -import Data.IntSet qualified as IS -import Data.List (sortOn) -import Data.Maybe (isJust) -import Data.Text (Text) -import Data.Text qualified as T -import GHC.Generics (Generic) - -import Language.PureScript.AST.SourcePos (pattern NullSourceAnn, SourceAnn, SourceSpan) -import Language.PureScript.Constants.Prim qualified as C -import Language.PureScript.Names (OpName, OpNameType(..), ProperName, ProperNameType(..), Qualified, coerceProperName) -import Language.PureScript.Label (Label) -import Language.PureScript.PSString (PSString) - -type SourceType = Type SourceAnn -type SourceConstraint = Constraint SourceAnn - --- | --- An identifier for the scope of a skolem variable --- -newtype SkolemScope = SkolemScope { runSkolemScope :: Int } - deriving (Show, Eq, Ord, A.ToJSON, A.FromJSON, Generic) - -instance NFData SkolemScope -instance Serialise SkolemScope - --- | --- Describes how a TypeWildcard should be presented to the user during --- type checking: holes (?foo) are always emitted as errors, whereas unnamed --- wildcards (_) default to warnings, but are ignored entirely if they are --- contained by a binding with a complete (wildcard-free) type signature. --- -data WildcardData = HoleWildcard Text | UnnamedWildcard | IgnoredWildcard - deriving (Show, Eq, Ord, Generic) - -instance NFData WildcardData -instance Serialise WildcardData - -data TypeVarVisibility - = TypeVarVisible - | TypeVarInvisible - deriving (Show, Eq, Ord, Generic) - -instance NFData TypeVarVisibility -instance Serialise TypeVarVisibility - -typeVarVisibilityPrefix :: TypeVarVisibility -> Text -typeVarVisibilityPrefix = \case - TypeVarVisible -> "@" - TypeVarInvisible -> mempty - --- | --- The type of types --- -data Type a - -- | A unification variable of type Type - = TUnknown a Int - -- | A named type variable - | TypeVar a Text - -- | A type-level string - | TypeLevelString a PSString - -- | A type-level natural - | TypeLevelInt a Integer - -- | A type wildcard, as would appear in a partial type synonym - | TypeWildcard a WildcardData - -- | A type constructor - | TypeConstructor a (Qualified (ProperName 'TypeName)) - -- | A type operator. This will be desugared into a type constructor during the - -- "operators" phase of desugaring. - | TypeOp a (Qualified (OpName 'TypeOpName)) - -- | A type application - | TypeApp a (Type a) (Type a) - -- | Explicit kind application - | KindApp a (Type a) (Type a) - -- | Forall quantifier - | ForAll a TypeVarVisibility Text (Maybe (Type a)) (Type a) (Maybe SkolemScope) - -- | A type with a set of type class constraints - | ConstrainedType a (Constraint a) (Type a) - -- | A skolem constant - | Skolem a Text (Maybe (Type a)) Int SkolemScope - -- | An empty row - | REmpty a - -- | A non-empty row - | RCons a Label (Type a) (Type a) - -- | A type with a kind annotation - | KindedType a (Type a) (Type a) - -- | Binary operator application. During the rebracketing phase of desugaring, - -- this data constructor will be removed. - | BinaryNoParensType a (Type a) (Type a) (Type a) - -- | Explicit parentheses. During the rebracketing phase of desugaring, this - -- data constructor will be removed. - -- - -- Note: although it seems this constructor is not used, it _is_ useful, - -- since it prevents certain traversals from matching. - | ParensInType a (Type a) - deriving (Show, Generic, Functor, Foldable, Traversable) - -instance NFData a => NFData (Type a) -instance Serialise a => Serialise (Type a) - -srcTUnknown :: Int -> SourceType -srcTUnknown = TUnknown NullSourceAnn - -srcTypeVar :: Text -> SourceType -srcTypeVar = TypeVar NullSourceAnn - -srcTypeLevelString :: PSString -> SourceType -srcTypeLevelString = TypeLevelString NullSourceAnn - -srcTypeLevelInt :: Integer -> SourceType -srcTypeLevelInt = TypeLevelInt NullSourceAnn - -srcTypeWildcard :: SourceType -srcTypeWildcard = TypeWildcard NullSourceAnn UnnamedWildcard - -srcTypeConstructor :: Qualified (ProperName 'TypeName) -> SourceType -srcTypeConstructor = TypeConstructor NullSourceAnn - -srcTypeApp :: SourceType -> SourceType -> SourceType -srcTypeApp = TypeApp NullSourceAnn - -srcKindApp :: SourceType -> SourceType -> SourceType -srcKindApp = KindApp NullSourceAnn - -srcForAll :: TypeVarVisibility -> Text -> Maybe SourceType -> SourceType -> Maybe SkolemScope -> SourceType -srcForAll = ForAll NullSourceAnn - -srcConstrainedType :: SourceConstraint -> SourceType -> SourceType -srcConstrainedType = ConstrainedType NullSourceAnn - -srcREmpty :: SourceType -srcREmpty = REmpty NullSourceAnn - -srcRCons :: Label -> SourceType -> SourceType -> SourceType -srcRCons = RCons NullSourceAnn - -srcKindedType :: SourceType -> SourceType -> SourceType -srcKindedType = KindedType NullSourceAnn - -pattern REmptyKinded :: forall a. a -> Maybe (Type a) -> Type a -pattern REmptyKinded ann mbK <- (toREmptyKinded -> Just (ann, mbK)) - -toREmptyKinded :: forall a. Type a -> Maybe (a, Maybe (Type a)) -toREmptyKinded (REmpty ann) = Just (ann, Nothing) -toREmptyKinded (KindApp _ (REmpty ann) k) = Just (ann, Just k) -toREmptyKinded _ = Nothing - -isREmpty :: forall a. Type a -> Bool -isREmpty = isJust . toREmptyKinded - --- | Additional data relevant to type class constraints -data ConstraintData - = PartialConstraintData [[Text]] Bool - -- ^ Data to accompany a Partial constraint generated by the exhaustivity checker. - -- It contains (rendered) binder information for those binders which were - -- not matched, and a flag indicating whether the list was truncated or not. - -- Note: we use 'Text' here because using 'Binder' would introduce a cyclic - -- dependency in the module graph. - deriving (Show, Eq, Ord, Generic) - -instance NFData ConstraintData -instance Serialise ConstraintData - --- | A typeclass constraint -data Constraint a = Constraint - { constraintAnn :: a - -- ^ constraint annotation - , constraintClass :: Qualified (ProperName 'ClassName) - -- ^ constraint class name - , constraintKindArgs :: [Type a] - -- ^ kind arguments - , constraintArgs :: [Type a] - -- ^ type arguments - , constraintData :: Maybe ConstraintData - -- ^ additional data relevant to this constraint - } deriving (Show, Generic, Functor, Foldable, Traversable) - -instance NFData a => NFData (Constraint a) -instance Serialise a => Serialise (Constraint a) - -srcConstraint :: Qualified (ProperName 'ClassName) -> [SourceType] -> [SourceType] -> Maybe ConstraintData -> SourceConstraint -srcConstraint = Constraint NullSourceAnn - -mapConstraintArgs :: ([Type a] -> [Type a]) -> Constraint a -> Constraint a -mapConstraintArgs f c = c { constraintArgs = f (constraintArgs c) } - -overConstraintArgs :: Functor f => ([Type a] -> f [Type a]) -> Constraint a -> f (Constraint a) -overConstraintArgs f c = (\args -> c { constraintArgs = args }) <$> f (constraintArgs c) - -mapConstraintArgsAll :: ([Type a] -> [Type a]) -> Constraint a -> Constraint a -mapConstraintArgsAll f c = - c { constraintKindArgs = f (constraintKindArgs c) - , constraintArgs = f (constraintArgs c) - } - -overConstraintArgsAll :: Applicative f => ([Type a] -> f [Type a]) -> Constraint a -> f (Constraint a) -overConstraintArgsAll f c = - (\a b -> c { constraintKindArgs = a, constraintArgs = b }) - <$> f (constraintKindArgs c) - <*> f (constraintArgs c) - -constraintDataToJSON :: ConstraintData -> A.Value -constraintDataToJSON (PartialConstraintData bs trunc) = - A.object - [ "contents" .= (bs, trunc) - ] - -constraintToJSON :: (a -> A.Value) -> Constraint a -> A.Value -constraintToJSON annToJSON Constraint {..} = - A.object - [ "constraintAnn" .= annToJSON constraintAnn - , "constraintClass" .= constraintClass - , "constraintKindArgs" .= fmap (typeToJSON annToJSON) constraintKindArgs - , "constraintArgs" .= fmap (typeToJSON annToJSON) constraintArgs - , "constraintData" .= fmap constraintDataToJSON constraintData - ] - -typeVarVisToJSON :: TypeVarVisibility -> A.Value -typeVarVisToJSON = \case - TypeVarVisible -> A.toJSON ("TypeVarVisible" :: Text) - TypeVarInvisible -> A.toJSON ("TypeVarInvisible" :: Text) - -typeToJSON :: forall a. (a -> A.Value) -> Type a -> A.Value -typeToJSON annToJSON ty = - case ty of - TUnknown a b -> - variant "TUnknown" a b - TypeVar a b -> - variant "TypeVar" a b - TypeLevelString a b -> - variant "TypeLevelString" a b - TypeLevelInt a b -> - variant "TypeLevelInt" a b - TypeWildcard a b -> - variant "TypeWildcard" a b - TypeConstructor a b -> - variant "TypeConstructor" a b - TypeOp a b -> - variant "TypeOp" a b - TypeApp a b c -> - variant "TypeApp" a (go b, go c) - KindApp a b c -> - variant "KindApp" a (go b, go c) - ForAll a b c d e f -> - variant "ForAll" a $ A.object - [ "visibility" .= b - , "identifier" .= c - , "kind" .= fmap go d - , "type" .= go e - , "skolem" .= f - ] - ConstrainedType a b c -> - variant "ConstrainedType" a (constraintToJSON annToJSON b, go c) - Skolem a b c d e -> - variant "Skolem" a (b, go <$> c, d, e) - REmpty a -> - nullary "REmpty" a - RCons a b c d -> - variant "RCons" a (b, go c, go d) - KindedType a b c -> - variant "KindedType" a (go b, go c) - BinaryNoParensType a b c d -> - variant "BinaryNoParensType" a (go b, go c, go d) - ParensInType a b -> - variant "ParensInType" a (go b) - where - go :: Type a -> A.Value - go = typeToJSON annToJSON - - variant :: A.ToJSON b => String -> a -> b -> A.Value - variant tag ann contents = - A.object - [ "tag" .= tag - , "annotation" .= annToJSON ann - , "contents" .= contents - ] - - nullary :: String -> a -> A.Value - nullary tag ann = - A.object - [ "tag" .= tag - , "annotation" .= annToJSON ann - ] - -instance A.ToJSON WildcardData where - toJSON = \case - HoleWildcard name -> A.String name - UnnamedWildcard -> A.Null - IgnoredWildcard -> A.object [ "ignored" .= True ] - -instance A.ToJSON a => A.ToJSON (Type a) where - toJSON = typeToJSON A.toJSON - -instance A.ToJSON a => A.ToJSON (Constraint a) where - toJSON = constraintToJSON A.toJSON - -instance A.ToJSON ConstraintData where - toJSON = constraintDataToJSON - -instance A.ToJSON TypeVarVisibility where - toJSON = typeVarVisToJSON - -constraintDataFromJSON :: A.Value -> A.Parser ConstraintData -constraintDataFromJSON = A.withObject "PartialConstraintData" $ \o -> do - (bs, trunc) <- o .: "contents" - pure $ PartialConstraintData bs trunc - -constraintFromJSON :: forall a. A.Parser a -> (A.Value -> A.Parser a) -> A.Value -> A.Parser (Constraint a) -constraintFromJSON defaultAnn annFromJSON = A.withObject "Constraint" $ \o -> do - constraintAnn <- (o .: "constraintAnn" >>= annFromJSON) <|> defaultAnn - constraintClass <- o .: "constraintClass" - constraintKindArgs <- o .:? "constraintKindArgs" .!= [] >>= traverse (typeFromJSON defaultAnn annFromJSON) - constraintArgs <- o .: "constraintArgs" >>= traverse (typeFromJSON defaultAnn annFromJSON) - constraintData <- o .: "constraintData" >>= traverse constraintDataFromJSON - pure $ Constraint {..} - -typeVarVisFromJSON :: A.Value -> A.Parser TypeVarVisibility -typeVarVisFromJSON v = do - v' <- A.parseJSON v - case v' of - "TypeVarVisible" -> pure TypeVarVisible - "TypeVarInvisible" -> pure TypeVarInvisible - _ -> fail $ "Unrecognized TypeVarVisibility: " <> v' - -typeFromJSON :: forall a. A.Parser a -> (A.Value -> A.Parser a) -> A.Value -> A.Parser (Type a) -typeFromJSON defaultAnn annFromJSON = A.withObject "Type" $ \o -> do - tag <- o .: "tag" - a <- (o .: "annotation" >>= annFromJSON) <|> defaultAnn - let - contents :: A.FromJSON b => A.Parser b - contents = o .: "contents" - case tag of - "TUnknown" -> - TUnknown a <$> contents - "TypeVar" -> - TypeVar a <$> contents - "TypeLevelString" -> - TypeLevelString a <$> contents - "TypeLevelInt" -> - TypeLevelInt a <$> contents - "TypeWildcard" -> do - b <- contents <|> pure UnnamedWildcard - pure $ TypeWildcard a b - "TypeConstructor" -> - TypeConstructor a <$> contents - "TypeOp" -> - TypeOp a <$> contents - "TypeApp" -> do - (b, c) <- contents - TypeApp a <$> go b <*> go c - "KindApp" -> do - (b, c) <- contents - KindApp a <$> go b <*> go c - "ForAll" -> do - let - asObject = do - f <- contents - v <- f .: "visibility" - i <- f .: "identifier" - k <- f .:? "kind" - t <- f .: "type" - s <- f .: "skolem" - ForAll a v i <$> traverse go k <*> go t <*> pure s - - withoutMbKind = do - (b, c, d) <- contents - ForAll a TypeVarInvisible b Nothing <$> go c <*> pure d - - withMbKind = do - (b, c, d, e) <- contents - ForAll a TypeVarInvisible b <$> (Just <$> go c) <*> go d <*> pure e - asObject <|> withMbKind <|> withoutMbKind - "ConstrainedType" -> do - (b, c) <- contents - ConstrainedType a <$> constraintFromJSON defaultAnn annFromJSON b <*> go c - "Skolem" -> do - (b, c, d, e) <- contents - c' <- traverse go c - pure $ Skolem a b c' d e - "REmpty" -> - pure $ REmpty a - "RCons" -> do - (b, c, d) <- contents - RCons a b <$> go c <*> go d - "KindedType" -> do - (b, c) <- contents - KindedType a <$> go b <*> go c - "BinaryNoParensType" -> do - (b, c, d) <- contents - BinaryNoParensType a <$> go b <*> go c <*> go d - "ParensInType" -> do - b <- contents - ParensInType a <$> go b - -- Backwards compatibility for kinds - "KUnknown" -> - TUnknown a <$> contents - "Row" -> - TypeApp a (TypeConstructor a C.Row) <$> (go =<< contents) - "FunKind" -> do - (b, c) <- contents - TypeApp a . TypeApp a (TypeConstructor a C.Function) <$> go b <*> go c - "NamedKind" -> - TypeConstructor a <$> contents - other -> - fail $ "Unrecognised tag: " ++ other - where - go :: A.Value -> A.Parser (Type a) - go = typeFromJSON defaultAnn annFromJSON - --- These overlapping instances exist to preserve compatibility for common --- instances which have a sensible default for missing annotations. -instance {-# OVERLAPPING #-} A.FromJSON (Type SourceAnn) where - parseJSON = typeFromJSON (pure NullSourceAnn) A.parseJSON - -instance {-# OVERLAPPING #-} A.FromJSON (Type ()) where - parseJSON = typeFromJSON (pure ()) A.parseJSON - -instance {-# OVERLAPPING #-} A.FromJSON a => A.FromJSON (Type a) where - parseJSON = typeFromJSON (fail "Invalid annotation") A.parseJSON - -instance {-# OVERLAPPING #-} A.FromJSON (Constraint SourceAnn) where - parseJSON = constraintFromJSON (pure NullSourceAnn) A.parseJSON - -instance {-# OVERLAPPING #-} A.FromJSON (Constraint ()) where - parseJSON = constraintFromJSON (pure ()) A.parseJSON - -instance {-# OVERLAPPING #-} A.FromJSON a => A.FromJSON (Constraint a) where - parseJSON = constraintFromJSON (fail "Invalid annotation") A.parseJSON - -instance A.FromJSON ConstraintData where - parseJSON = constraintDataFromJSON - -instance A.FromJSON WildcardData where - parseJSON = \case - A.String name -> pure $ HoleWildcard name - A.Object _ -> pure IgnoredWildcard - A.Null -> pure UnnamedWildcard - _ -> fail "Unrecognized WildcardData" - -instance A.FromJSON TypeVarVisibility where - parseJSON = typeVarVisFromJSON - -data RowListItem a = RowListItem - { rowListAnn :: a - , rowListLabel :: Label - , rowListType :: Type a - } deriving (Show, Generic, Functor, Foldable, Traversable) - -srcRowListItem :: Label -> SourceType -> RowListItem SourceAnn -srcRowListItem = RowListItem NullSourceAnn - --- | Convert a row to a list of pairs of labels and types -rowToList :: Type a -> ([RowListItem a], Type a) -rowToList = go where - go (RCons ann name ty row) = - first (RowListItem ann name ty :) (rowToList row) - go r = ([], r) - --- | Convert a row to a list of pairs of labels and types, sorted by the labels. -rowToSortedList :: Type a -> ([RowListItem a], Type a) -rowToSortedList = first (sortOn rowListLabel) . rowToList - --- | Convert a list of labels and types to a row -rowFromList :: ([RowListItem a], Type a) -> Type a -rowFromList (xs, r) = foldr (\(RowListItem ann name ty) -> RCons ann name ty) r xs - --- | Align two rows of types, splitting them into three parts: --- --- * Those types which appear in both rows --- * Those which appear only on the left --- * Those which appear only on the right --- --- Note: importantly, we preserve the order of the types with a given label. -alignRowsWith - :: (Label -> Type a -> Type a -> r) - -> Type a - -> Type a - -> ([r], (([RowListItem a], Type a), ([RowListItem a], Type a))) -alignRowsWith f ty1 ty2 = go s1 s2 where - (s1, tail1) = rowToSortedList ty1 - (s2, tail2) = rowToSortedList ty2 - - go [] r = ([], (([], tail1), (r, tail2))) - go r [] = ([], ((r, tail1), ([], tail2))) - go lhs@(RowListItem a1 l1 t1 : r1) rhs@(RowListItem a2 l2 t2 : r2) = - case compare l1 l2 of - LT -> (second . first . first) (RowListItem a1 l1 t1 :) (go r1 rhs) - GT -> (second . second . first) (RowListItem a2 l2 t2 :) (go lhs r2) - EQ -> first (f l1 t1 t2 :) (go r1 r2) - --- | Check whether a type is a monotype -isMonoType :: Type a -> Bool -isMonoType ForAll{} = False -isMonoType (ParensInType _ t) = isMonoType t -isMonoType (KindedType _ t _) = isMonoType t -isMonoType _ = True - --- | Universally quantify a type -mkForAll :: [(a, (Text, Maybe (Type a)))] -> Type a -> Type a -mkForAll args ty = foldr (\(ann, (arg, mbK)) t -> ForAll ann TypeVarInvisible arg mbK t Nothing) ty args - --- | Replace a type variable, taking into account variable shadowing -replaceTypeVars :: Text -> Type a -> Type a -> Type a -replaceTypeVars v r = replaceAllTypeVars [(v, r)] - --- | Replace named type variables with types -replaceAllTypeVars :: [(Text, Type a)] -> Type a -> Type a -replaceAllTypeVars = go [] where - go :: [Text] -> [(Text, Type a)] -> Type a -> Type a - go _ m (TypeVar ann v) = fromMaybe (TypeVar ann v) (v `lookup` m) - go bs m (TypeApp ann t1 t2) = TypeApp ann (go bs m t1) (go bs m t2) - go bs m (KindApp ann t1 t2) = KindApp ann (go bs m t1) (go bs m t2) - go bs m (ForAll ann vis v mbK t sco) - | v `elem` keys = go bs (filter ((/= v) . fst) m) $ ForAll ann vis v mbK' t sco - | v `elem` usedVars = - let v' = genPureName v (keys ++ bs ++ usedVars) - t' = go bs [(v, TypeVar ann v')] t - in ForAll ann vis v' mbK' (go (v' : bs) m t') sco - | otherwise = ForAll ann vis v mbK' (go (v : bs) m t) sco - where - mbK' = go bs m <$> mbK - keys = map fst m - usedVars = concatMap (usedTypeVariables . snd) m - go bs m (ConstrainedType ann c t) = ConstrainedType ann (mapConstraintArgsAll (map (go bs m)) c) (go bs m t) - go bs m (RCons ann name' t r) = RCons ann name' (go bs m t) (go bs m r) - go bs m (KindedType ann t k) = KindedType ann (go bs m t) (go bs m k) - go bs m (BinaryNoParensType ann t1 t2 t3) = BinaryNoParensType ann (go bs m t1) (go bs m t2) (go bs m t3) - go bs m (ParensInType ann t) = ParensInType ann (go bs m t) - go _ _ ty = ty - -genPureName :: Text -> [Text] -> Text -genPureName orig inUse = try' 0 - where - try' :: Integer -> Text - try' n | (orig <> T.pack (show n)) `elem` inUse = try' (n + 1) - | otherwise = orig <> T.pack (show n) - --- | Add visible type abstractions to top-level foralls. -addVisibility :: [(Text, TypeVarVisibility)] -> Type a -> Type a -addVisibility v = go where - go (ForAll ann vis arg mbK ty sco) = case lookup arg v of - Just vis' -> - ForAll ann vis' arg mbK (go ty) sco - Nothing -> - ForAll ann vis arg mbK (go ty) sco - go (ParensInType ann ty) = ParensInType ann (go ty) - go ty = ty - --- | Collect all type variables appearing in a type -usedTypeVariables :: Type a -> [Text] -usedTypeVariables = ordNub . everythingOnTypes (++) go where - go (TypeVar _ v) = [v] - go _ = [] - --- | Collect all free type variables appearing in a type -freeTypeVariables :: Type a -> [Text] -freeTypeVariables = ordNub . fmap snd . sortOn fst . go 0 [] where - -- Tracks kind levels so that variables appearing in kind annotations are listed first. - go :: Int -> [Text] -> Type a -> [(Int, Text)] - go lvl bound (TypeVar _ v) | v `notElem` bound = [(lvl, v)] - go lvl bound (TypeApp _ t1 t2) = go lvl bound t1 ++ go lvl bound t2 - go lvl bound (KindApp _ t1 t2) = go lvl bound t1 ++ go (lvl - 1) bound t2 - go lvl bound (ForAll _ _ v mbK t _) = foldMap (go (lvl - 1) bound) mbK ++ go lvl (v : bound) t - go lvl bound (ConstrainedType _ c t) = foldMap (go (lvl - 1) bound) (constraintKindArgs c) ++ foldMap (go lvl bound) (constraintArgs c) ++ go lvl bound t - go lvl bound (RCons _ _ t r) = go lvl bound t ++ go lvl bound r - go lvl bound (KindedType _ t k) = go lvl bound t ++ go (lvl - 1) bound k - go lvl bound (BinaryNoParensType _ t1 t2 t3) = go lvl bound t1 ++ go lvl bound t2 ++ go lvl bound t3 - go lvl bound (ParensInType _ t) = go lvl bound t - go _ _ _ = [] - --- | Collect a complete set of kind-annotated quantifiers at the front of a type. -completeBinderList :: Type a -> Maybe ([(a, (Text, Type a))], Type a) -completeBinderList = go [] - where - go acc = \case - ForAll _ _ _ Nothing _ _ -> Nothing - ForAll ann _ var (Just k) ty _ -> go ((ann, (var, k)) : acc) ty - ty -> Just (reverse acc, ty) - --- | Universally quantify over all type variables appearing free in a type -quantify :: Type a -> Type a -quantify ty = foldr (\arg t -> ForAll (getAnnForType ty) TypeVarInvisible arg Nothing t Nothing) ty $ freeTypeVariables ty - --- | Move all universal quantifiers to the front of a type -moveQuantifiersToFront :: a -> Type a -> Type a -moveQuantifiersToFront syntheticAnn = go [] [] - where - go qs cs = \case - ForAll ann vis q mbK ty sco -> do - let - cArgs :: [Text] = cs >>= constraintArgs . snd >>= freeTypeVariables - (q'', ty') - | q `elem` cArgs = do - let q' = genPureName q $ cArgs <> freeTypeVariables ty - (q', replaceTypeVars q (TypeVar syntheticAnn q') ty) - | otherwise = - (q, ty) - go ((ann, q'', sco, mbK, vis) : qs) cs ty' - ConstrainedType ann c ty -> - go qs ((ann, c) : cs) ty - ty -> - foldl (\ty' (ann, q, sco, mbK, vis) -> ForAll ann vis q mbK ty' sco) (foldl (\ty' (ann, c) -> ConstrainedType ann c ty') ty cs) qs - --- | Check if a type contains `forall` -containsForAll :: Type a -> Bool -containsForAll = everythingOnTypes (||) go where - go :: Type a -> Bool - go ForAll{} = True - go _ = False - -unknowns :: Type a -> IS.IntSet -unknowns = everythingOnTypes (<>) go where - go :: Type a -> IS.IntSet - go (TUnknown _ u) = IS.singleton u - go _ = mempty - --- | Check if a type contains unknowns in a position that is relevant to --- constraint solving. (Kinds are not.) -containsUnknowns :: Type a -> Bool -containsUnknowns = everythingOnTypes (||) go . eraseKindApps where - go :: Type a -> Bool - go TUnknown{} = True - go _ = False - -eraseKindApps :: Type a -> Type a -eraseKindApps = everywhereOnTypes $ \case - KindApp _ ty _ -> ty - ConstrainedType ann con ty -> - ConstrainedType ann (con { constraintKindArgs = [] }) ty - Skolem ann name _ i sc -> - Skolem ann name Nothing i sc - other -> other - -eraseForAllKindAnnotations :: Type a -> Type a -eraseForAllKindAnnotations = removeAmbiguousVars . removeForAllKinds - where - removeForAllKinds = everywhereOnTypes $ \case - ForAll ann vis arg _ ty sco -> - ForAll ann vis arg Nothing ty sco - other -> other - - removeAmbiguousVars = everywhereOnTypes $ \case - fa@(ForAll _ _ arg _ ty _) - | arg `elem` freeTypeVariables ty -> fa - | otherwise -> ty - other -> other - -unapplyTypes :: Type a -> (Type a, [Type a], [Type a]) -unapplyTypes = goTypes [] - where - goTypes acc (TypeApp _ a b) = goTypes (b : acc) a - goTypes acc a = let (ty, kinds) = goKinds [] a in (ty, kinds, acc) - - goKinds acc (KindApp _ a b) = goKinds (b : acc) a - goKinds acc a = (a, acc) - -unapplyConstraints :: Type a -> ([Constraint a], Type a) -unapplyConstraints = go [] - where - go acc (ConstrainedType _ con ty) = go (con : acc) ty - go acc ty = (reverse acc, ty) - --- | Construct the type of an instance declaration from its parts. Used in --- error messages describing unnamed instances. -srcInstanceType - :: SourceSpan - -> [(Text, SourceType)] - -> Qualified (ProperName 'ClassName) - -> [SourceType] - -> SourceType -srcInstanceType ss vars className tys - = setAnnForType (ss, []) - . flip (foldr $ \(tv, k) ty -> srcForAll TypeVarInvisible tv (Just k) ty Nothing) vars - . flip (foldl' srcTypeApp) tys - $ srcTypeConstructor $ coerceProperName <$> className - -everywhereOnTypes :: (Type a -> Type a) -> Type a -> Type a -everywhereOnTypes f = go where - go (TypeApp ann t1 t2) = f (TypeApp ann (go t1) (go t2)) - go (KindApp ann t1 t2) = f (KindApp ann (go t1) (go t2)) - go (ForAll ann vis arg mbK ty sco) = f (ForAll ann vis arg (go <$> mbK) (go ty) sco) - go (ConstrainedType ann c ty) = f (ConstrainedType ann (mapConstraintArgsAll (map go) c) (go ty)) - go (Skolem ann name mbK i sc) = f (Skolem ann name (go <$> mbK) i sc) - go (RCons ann name ty rest) = f (RCons ann name (go ty) (go rest)) - go (KindedType ann ty k) = f (KindedType ann (go ty) (go k)) - go (BinaryNoParensType ann t1 t2 t3) = f (BinaryNoParensType ann (go t1) (go t2) (go t3)) - go (ParensInType ann t) = f (ParensInType ann (go t)) - go other = f other - -everywhereOnTypesM :: Monad m => (Type a -> m (Type a)) -> Type a -> m (Type a) -everywhereOnTypesM f = go where - go (TypeApp ann t1 t2) = (TypeApp ann <$> go t1 <*> go t2) >>= f - go (KindApp ann t1 t2) = (KindApp ann <$> go t1 <*> go t2) >>= f - go (ForAll ann vis arg mbK ty sco) = (ForAll ann vis arg <$> traverse go mbK <*> go ty <*> pure sco) >>= f - go (ConstrainedType ann c ty) = (ConstrainedType ann <$> overConstraintArgsAll (mapM go) c <*> go ty) >>= f - go (Skolem ann name mbK i sc) = (Skolem ann name <$> traverse go mbK <*> pure i <*> pure sc) >>= f - go (RCons ann name ty rest) = (RCons ann name <$> go ty <*> go rest) >>= f - go (KindedType ann ty k) = (KindedType ann <$> go ty <*> go k) >>= f - go (BinaryNoParensType ann t1 t2 t3) = (BinaryNoParensType ann <$> go t1 <*> go t2 <*> go t3) >>= f - go (ParensInType ann t) = (ParensInType ann <$> go t) >>= f - go other = f other -{-# INLINE everywhereOnTypesM #-} - -everywhereOnTypesTopDownM :: Monad m => (Type a -> m (Type a)) -> Type a -> m (Type a) -everywhereOnTypesTopDownM f = go <=< f where - go (TypeApp ann t1 t2) = TypeApp ann <$> (f t1 >>= go) <*> (f t2 >>= go) - go (KindApp ann t1 t2) = KindApp ann <$> (f t1 >>= go) <*> (f t2 >>= go) - go (ForAll ann vis arg mbK ty sco) = ForAll ann vis arg <$> traverse (f >=> go) mbK <*> (f ty >>= go) <*> pure sco - go (ConstrainedType ann c ty) = ConstrainedType ann <$> overConstraintArgsAll (mapM (go <=< f)) c <*> (f ty >>= go) - go (Skolem ann name mbK i sc) = Skolem ann name <$> traverse (f >=> go) mbK <*> pure i <*> pure sc - go (RCons ann name ty rest) = RCons ann name <$> (f ty >>= go) <*> (f rest >>= go) - go (KindedType ann ty k) = KindedType ann <$> (f ty >>= go) <*> (f k >>= go) - go (BinaryNoParensType ann t1 t2 t3) = BinaryNoParensType ann <$> (f t1 >>= go) <*> (f t2 >>= go) <*> (f t3 >>= go) - go (ParensInType ann t) = ParensInType ann <$> (f t >>= go) - go other = pure other -{-# INLINE everywhereOnTypesTopDownM #-} - -everythingOnTypes :: (r -> r -> r) -> (Type a -> r) -> Type a -> r -everythingOnTypes (<+>) f = go where - go t@(TypeApp _ t1 t2) = f t <+> go t1 <+> go t2 - go t@(KindApp _ t1 t2) = f t <+> go t1 <+> go t2 - go t@(ForAll _ _ _ (Just k) ty _) = f t <+> go k <+> go ty - go t@(ForAll _ _ _ _ ty _) = f t <+> go ty - go t@(ConstrainedType _ c ty) = foldl (<+>) (f t) (map go (constraintKindArgs c) ++ map go (constraintArgs c)) <+> go ty - go t@(Skolem _ _ (Just k) _ _) = f t <+> go k - go t@(RCons _ _ ty rest) = f t <+> go ty <+> go rest - go t@(KindedType _ ty k) = f t <+> go ty <+> go k - go t@(BinaryNoParensType _ t1 t2 t3) = f t <+> go t1 <+> go t2 <+> go t3 - go t@(ParensInType _ t1) = f t <+> go t1 - go other = f other -{-# INLINE everythingOnTypes #-} - -everythingWithContextOnTypes :: s -> r -> (r -> r -> r) -> (s -> Type a -> (s, r)) -> Type a -> r -everythingWithContextOnTypes s0 r0 (<+>) f = go' s0 where - go' s t = let (s', r) = f s t in r <+> go s' t - go s (TypeApp _ t1 t2) = go' s t1 <+> go' s t2 - go s (KindApp _ t1 t2) = go' s t1 <+> go' s t2 - go s (ForAll _ _ _ (Just k) ty _) = go' s k <+> go' s ty - go s (ForAll _ _ _ _ ty _) = go' s ty - go s (ConstrainedType _ c ty) = foldl (<+>) r0 (map (go' s) (constraintKindArgs c) ++ map (go' s) (constraintArgs c)) <+> go' s ty - go s (Skolem _ _ (Just k) _ _) = go' s k - go s (RCons _ _ ty rest) = go' s ty <+> go' s rest - go s (KindedType _ ty k) = go' s ty <+> go' s k - go s (BinaryNoParensType _ t1 t2 t3) = go' s t1 <+> go' s t2 <+> go' s t3 - go s (ParensInType _ t1) = go' s t1 - go _ _ = r0 -{-# INLINE everythingWithContextOnTypes #-} - -annForType :: Lens' (Type a) a -annForType k (TUnknown a b) = (\z -> TUnknown z b) <$> k a -annForType k (TypeVar a b) = (\z -> TypeVar z b) <$> k a -annForType k (TypeLevelString a b) = (\z -> TypeLevelString z b) <$> k a -annForType k (TypeLevelInt a b) = (\z -> TypeLevelInt z b) <$> k a -annForType k (TypeWildcard a b) = (\z -> TypeWildcard z b) <$> k a -annForType k (TypeConstructor a b) = (\z -> TypeConstructor z b) <$> k a -annForType k (TypeOp a b) = (\z -> TypeOp z b) <$> k a -annForType k (TypeApp a b c) = (\z -> TypeApp z b c) <$> k a -annForType k (KindApp a b c) = (\z -> KindApp z b c) <$> k a -annForType k (ForAll a b c d e f) = (\z -> ForAll z b c d e f) <$> k a -annForType k (ConstrainedType a b c) = (\z -> ConstrainedType z b c) <$> k a -annForType k (Skolem a b c d e) = (\z -> Skolem z b c d e) <$> k a -annForType k (REmpty a) = REmpty <$> k a -annForType k (RCons a b c d) = (\z -> RCons z b c d) <$> k a -annForType k (KindedType a b c) = (\z -> KindedType z b c) <$> k a -annForType k (BinaryNoParensType a b c d) = (\z -> BinaryNoParensType z b c d) <$> k a -annForType k (ParensInType a b) = (\z -> ParensInType z b) <$> k a - -getAnnForType :: Type a -> a -getAnnForType = (^. annForType) - -setAnnForType :: a -> Type a -> Type a -setAnnForType = set annForType - -instance Eq (Type a) where - (==) = eqType - -instance Ord (Type a) where - compare = compareType - -eqType :: Type a -> Type b -> Bool -eqType (TUnknown _ a) (TUnknown _ a') = a == a' -eqType (TypeVar _ a) (TypeVar _ a') = a == a' -eqType (TypeLevelString _ a) (TypeLevelString _ a') = a == a' -eqType (TypeLevelInt _ a) (TypeLevelInt _ a') = a == a' -eqType (TypeWildcard _ a) (TypeWildcard _ a') = a == a' -eqType (TypeConstructor _ a) (TypeConstructor _ a') = a == a' -eqType (TypeOp _ a) (TypeOp _ a') = a == a' -eqType (TypeApp _ a b) (TypeApp _ a' b') = eqType a a' && eqType b b' -eqType (KindApp _ a b) (KindApp _ a' b') = eqType a a' && eqType b b' -eqType (ForAll _ _ a b c d) (ForAll _ _ a' b' c' d') = a == a' && eqMaybeType b b' && eqType c c' && d == d' -eqType (ConstrainedType _ a b) (ConstrainedType _ a' b') = eqConstraint a a' && eqType b b' -eqType (Skolem _ a b c d) (Skolem _ a' b' c' d') = a == a' && eqMaybeType b b' && c == c' && d == d' -eqType (REmpty _) (REmpty _) = True -eqType (RCons _ a b c) (RCons _ a' b' c') = a == a' && eqType b b' && eqType c c' -eqType (KindedType _ a b) (KindedType _ a' b') = eqType a a' && eqType b b' -eqType (BinaryNoParensType _ a b c) (BinaryNoParensType _ a' b' c') = eqType a a' && eqType b b' && eqType c c' -eqType (ParensInType _ a) (ParensInType _ a') = eqType a a' -eqType _ _ = False - -eqMaybeType :: Maybe (Type a) -> Maybe (Type b) -> Bool -eqMaybeType (Just a) (Just b) = eqType a b -eqMaybeType Nothing Nothing = True -eqMaybeType _ _ = False - -compareType :: Type a -> Type b -> Ordering -compareType (TUnknown _ a) (TUnknown _ a') = compare a a' -compareType (TypeVar _ a) (TypeVar _ a') = compare a a' -compareType (TypeLevelString _ a) (TypeLevelString _ a') = compare a a' -compareType (TypeLevelInt _ a) (TypeLevelInt _ a') = compare a a' -compareType (TypeWildcard _ a) (TypeWildcard _ a') = compare a a' -compareType (TypeConstructor _ a) (TypeConstructor _ a') = compare a a' -compareType (TypeOp _ a) (TypeOp _ a') = compare a a' -compareType (TypeApp _ a b) (TypeApp _ a' b') = compareType a a' <> compareType b b' -compareType (KindApp _ a b) (KindApp _ a' b') = compareType a a' <> compareType b b' -compareType (ForAll _ _ a b c d) (ForAll _ _ a' b' c' d') = compare a a' <> compareMaybeType b b' <> compareType c c' <> compare d d' -compareType (ConstrainedType _ a b) (ConstrainedType _ a' b') = compareConstraint a a' <> compareType b b' -compareType (Skolem _ a b c d) (Skolem _ a' b' c' d') = compare a a' <> compareMaybeType b b' <> compare c c' <> compare d d' -compareType (REmpty _) (REmpty _) = EQ -compareType (RCons _ a b c) (RCons _ a' b' c') = compare a a' <> compareType b b' <> compareType c c' -compareType (KindedType _ a b) (KindedType _ a' b') = compareType a a' <> compareType b b' -compareType (BinaryNoParensType _ a b c) (BinaryNoParensType _ a' b' c') = compareType a a' <> compareType b b' <> compareType c c' -compareType (ParensInType _ a) (ParensInType _ a') = compareType a a' -compareType typ typ' = - compare (orderOf typ) (orderOf typ') - where - orderOf :: Type a -> Int - orderOf TUnknown{} = 0 - orderOf TypeVar{} = 1 - orderOf TypeLevelString{} = 2 - orderOf TypeLevelInt{} = 3 - orderOf TypeWildcard{} = 4 - orderOf TypeConstructor{} = 5 - orderOf TypeOp{} = 6 - orderOf TypeApp{} = 7 - orderOf KindApp{} = 8 - orderOf ForAll{} = 9 - orderOf ConstrainedType{} = 10 - orderOf Skolem{} = 11 - orderOf REmpty{} = 12 - orderOf RCons{} = 13 - orderOf KindedType{} = 14 - orderOf BinaryNoParensType{} = 15 - orderOf ParensInType{} = 16 - -compareMaybeType :: Maybe (Type a) -> Maybe (Type b) -> Ordering -compareMaybeType (Just a) (Just b) = compareType a b -compareMaybeType Nothing Nothing = EQ -compareMaybeType Nothing _ = LT -compareMaybeType _ _ = GT - -instance Eq (Constraint a) where - (==) = eqConstraint - -instance Ord (Constraint a) where - compare = compareConstraint - -eqConstraint :: Constraint a -> Constraint b -> Bool -eqConstraint (Constraint _ a b c d) (Constraint _ a' b' c' d') = a == a' && and (zipWith eqType b b') && and (zipWith eqType c c') && d == d' - -compareConstraint :: Constraint a -> Constraint b -> Ordering -compareConstraint (Constraint _ a b c d) (Constraint _ a' b' c' d') = compare a a' <> fold (zipWith compareType b b') <> fold (zipWith compareType c c') <> compare d d' diff --git a/claude-help/original-compiler/src/README.md b/claude-help/original-compiler/src/README.md deleted file mode 100644 index 87a685ae..00000000 --- a/claude-help/original-compiler/src/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Original Compiler - -This is the original PureScript compiler, written in Haskell. It is used as a reference implementation for the new Rust-based compiler. The goal of the new compiler is to be faster and more memory efficient than the original, while still producing the same output for the same input. - -If you are stuck implementing a feature in the new compiler, you can refer to the original compiler's source code to see how it is implemented. - - -Academic papers on the compiler can be found at https://discourse.purescript.org/t/academic-theoretical-basis-of-the-purescript-type-system/748 - - - - -this was the last task: -keep removing elements from SKIP_FAILING_FIXTURES and implement the missing features. Look inside `claude-help` for help on how to implement them \ No newline at end of file diff --git a/claude-help/original-compiler/src/System/IO/UTF8.hs b/claude-help/original-compiler/src/System/IO/UTF8.hs deleted file mode 100644 index 9ac916cf..00000000 --- a/claude-help/original-compiler/src/System/IO/UTF8.hs +++ /dev/null @@ -1,32 +0,0 @@ -module System.IO.UTF8 where - -import Prelude - -import Data.ByteString qualified as BS -import Data.ByteString.Lazy qualified as BSL -import Data.ByteString.Search qualified as BSS -import Data.ByteString.UTF8 qualified as UTF8 -import Data.Text (Text) -import Data.Text.Encoding qualified as TE -import Protolude (ordNub) - --- | Unfortunately ByteString's readFile does not convert line endings on --- Windows, so we have to do it ourselves -fixCRLF :: BS.ByteString -> BS.ByteString -fixCRLF = BSL.toStrict . BSS.replace "\r\n" ("\n" :: BS.ByteString) - -readUTF8FilesT :: [FilePath] -> IO [(FilePath, Text)] -readUTF8FilesT = - traverse (\inFile -> (inFile, ) <$> readUTF8FileT inFile) . ordNub - -readUTF8FileT :: FilePath -> IO Text -readUTF8FileT inFile = - fmap (TE.decodeUtf8 . fixCRLF) (BS.readFile inFile) - -writeUTF8FileT :: FilePath -> Text -> IO () -writeUTF8FileT inFile text = - BS.writeFile inFile (TE.encodeUtf8 text) - -readUTF8File :: FilePath -> IO String -readUTF8File inFile = - fmap (UTF8.toString . fixCRLF) (BS.readFile inFile) diff --git a/editors/code/dev.sh b/editors/code/dev.sh new file mode 100755 index 00000000..8e2cc548 --- /dev/null +++ b/editors/code/dev.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Dev script: build, launch VS Code with extension, and watch for changes. +# Usage: ./editors/code/dev.sh [path/to/file.purs] + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +EXT_DIR="$ROOT/editors/code" + +FILE="${1:-}" + +# --- Initial builds --- +echo "==> Building pfc (cargo build)..." +(cd "$ROOT" && cargo build) + +echo "==> Building extension (tsc)..." +(cd "$EXT_DIR" && npm run build) + +# --- Put debug binary on PATH so the extension finds "pfc" --- +export PATH="$ROOT/target/debug:$PATH" + +# --- Launch VS Code --- +VSCODE_ARGS=(--extensionDevelopmentPath "$EXT_DIR") +if [ -n "$FILE" ]; then + VSCODE_ARGS+=("$FILE") +fi + +echo "==> Launching VS Code..." +code "${VSCODE_ARGS[@]}" & +VSCODE_PID=$! + +# --- Background watchers --- +PIDS=() + +cleanup() { + echo "" + echo "==> Shutting down watchers..." + for pid in "${PIDS[@]}"; do + kill "$pid" 2>/dev/null || true + done + wait 2>/dev/null +} +trap cleanup EXIT INT TERM + +# Watch TypeScript extension source +echo "==> Starting tsc --watch..." +(cd "$EXT_DIR" && npx tsc -w --preserveWatchOutput) & +PIDS+=($!) + +# Watch Rust source and rebuild +echo "==> Starting cargo watch..." +(cd "$ROOT" && cargo watch -w src -s 'cargo build 2>&1 && echo "==> pfc rebuilt. Reload VS Code window (Cmd+Shift+P → Reload Window) to restart LSP."') & +PIDS+=($!) + +echo "" +echo "Watchers running. Press Ctrl+C to stop." +echo "After Rust rebuilds, reload the VS Code window to restart the LSP server." +echo "" + +# Wait for any child to exit +wait diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json new file mode 100644 index 00000000..995778fe --- /dev/null +++ b/editors/code/package-lock.json @@ -0,0 +1,121 @@ +{ + "name": "pfc-lsp", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pfc-lsp", + "version": "0.0.1", + "dependencies": { + "vscode-languageclient": "^9.0.1" + }, + "devDependencies": { + "@types/vscode": "^1.75.0", + "typescript": "^5.0.0" + }, + "engines": { + "vscode": "^1.75.0" + } + }, + "node_modules/@types/vscode": { + "version": "1.109.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.109.0.tgz", + "integrity": "sha512-0Pf95rnwEIwDbmXGC08r0B4TQhAbsHQ5UyTIgVgoieDe4cOnf92usuR5dEczb6bTKEp7ziZH4TV1TRGPPCExtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "license": "MIT", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + } + } +} diff --git a/editors/code/package.json b/editors/code/package.json new file mode 100644 index 00000000..13ce8f0d --- /dev/null +++ b/editors/code/package.json @@ -0,0 +1,57 @@ +{ + "name": "pfc-lsp", + "displayName": "PureScript Fast Compiler", + "description": "VS Code client for the pfc language server", + "version": "0.0.1", + "publisher": "pfc", + "engines": { + "vscode": "^1.75.0" + }, + "categories": [ + "Programming Languages" + ], + "activationEvents": [ + "onLanguage:purescript" + ], + "main": "./out/extension.js", + "contributes": { + "languages": [ + { + "id": "purescript", + "aliases": [ + "PureScript", + "purescript" + ], + "extensions": [ + ".purs" + ] + } + ], + "configuration": { + "title": "PureScript Fast Compiler", + "properties": { + "pfc.serverPath": { + "type": "string", + "default": "pfc", + "description": "Path to the pfc binary" + }, + "pfc.sourcesCommand": { + "type": "string", + "default": "spago sources", + "description": "Shell command that outputs PureScript source file paths (one per line). Example: find src .spago/p -name '*.purs'" + } + } + } + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w" + }, + "dependencies": { + "vscode-languageclient": "^9.0.1" + }, + "devDependencies": { + "@types/vscode": "^1.75.0", + "typescript": "^5.0.0" + } +} diff --git a/editors/code/run.sh b/editors/code/run.sh new file mode 100755 index 00000000..3604cd64 --- /dev/null +++ b/editors/code/run.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run script: build and launch VS Code with extension (no watchers). +# Usage: ./editors/code/run.sh [path/to/file.purs] + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +EXT_DIR="$ROOT/editors/code" + +FILE="${1:-}" + +# --- Build --- +echo "==> Building pfc (cargo build)..." +(cd "$ROOT" && cargo build) + +echo "==> Building extension (tsc)..." +(cd "$EXT_DIR" && npm run build) + +# --- Put debug binary on PATH so the extension finds "pfc" --- +export PATH="$ROOT/target/debug:$PATH" + +# --- Launch VS Code --- +VSCODE_ARGS=(--extensionDevelopmentPath "$EXT_DIR") +if [ -n "$FILE" ]; then + VSCODE_ARGS+=("$FILE") +fi + +echo "==> Launching VS Code..." +code "${VSCODE_ARGS[@]}" diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts new file mode 100644 index 00000000..e6736d24 --- /dev/null +++ b/editors/code/src/extension.ts @@ -0,0 +1,41 @@ +import * as vscode from "vscode"; +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, +} from "vscode-languageclient/node"; + +let client: LanguageClient | undefined; + +export function activate(context: vscode.ExtensionContext) { + const config = vscode.workspace.getConfiguration("pfc"); + const serverPath = config.get("serverPath", "pfc"); + const sourcesCommand = config.get("sourcesCommand", ""); + + const args = ["lsp"]; + if (sourcesCommand) { + args.push("--sources-cmd", sourcesCommand); + } + + const serverOptions: ServerOptions = { + command: serverPath, + args, + }; + + const clientOptions: LanguageClientOptions = { + documentSelector: [{ scheme: "file", language: "purescript" }], + }; + + client = new LanguageClient( + "pfc", + "PureScript Fast Compiler", + serverOptions, + clientOptions + ); + + client.start(); +} + +export function deactivate(): Thenable | undefined { + return client?.stop(); +} diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json new file mode 100644 index 00000000..a649e457 --- /dev/null +++ b/editors/code/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/src/ast.rs b/src/ast.rs index 6f3709c2..61ab4f4b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -835,10 +835,7 @@ impl Converter { items .iter() .map(|i| match i { - cst::Import::Value(n) - | cst::Import::Type(n, _) - | cst::Import::TypeOp(n) - | cst::Import::Class(n) => *n, + i => i.name(), }) .collect(), ), @@ -846,10 +843,7 @@ impl Converter { let hidden: HashSet = items .iter() .map(|i| match i { - cst::Import::Value(n) - | cst::Import::Type(n, _) - | cst::Import::TypeOp(n) - | cst::Import::Class(n) => *n, + i => i.name(), }) .collect(); // Build allowed = all names minus hidden @@ -914,22 +908,22 @@ impl Converter { for item in items { match item { cst::Import::Type(name, _) => { - let sym = *name; + let sym = name.value; self.types.insert(sym, prim_site.clone()); self.types .insert(qualified_symbol(prim_sym, sym), prim_site.clone()); } cst::Import::Class(name) => { - let sym = *name; + let sym = name.value; self.classes.insert(sym, prim_site.clone()); self.classes .insert(qualified_symbol(prim_sym, sym), prim_site.clone()); } cst::Import::Value(name) => { - self.values.insert(*name, prim_site.clone()); + self.values.insert(name.value, prim_site.clone()); } cst::Import::TypeOp(name) => { - self.types.insert(*name, prim_site.clone()); + self.types.insert(name.value, prim_site.clone()); } } } @@ -941,10 +935,7 @@ impl Converter { let hidden: HashSet = items .iter() .map(|i| match i { - cst::Import::Value(n) - | cst::Import::Type(n, _) - | cst::Import::TypeOp(n) - | cst::Import::Class(n) => *n, + i => i.name(), }) .collect(); for name in &[ @@ -1008,10 +999,7 @@ impl Converter { let hidden: HashSet = items .iter() .map(|i| match i { - cst::Import::Value(n) - | cst::Import::Type(n, _) - | cst::Import::TypeOp(n) - | cst::Import::Class(n) => *n, + i => i.name(), }) .collect(); self.import_all_except(module_exports, &hidden, qualifier, &site); @@ -1031,10 +1019,10 @@ impl Converter { for item in items { match item { cst::Import::Value(n) => { - vops.insert(*n); + vops.insert(n.value); } cst::Import::TypeOp(n) => { - tops.insert(*n); + tops.insert(n.value); } _ => {} } @@ -1046,14 +1034,14 @@ impl Converter { let hidden_vops: HashSet = items .iter() .filter_map(|i| match i { - cst::Import::Value(n) => Some(*n), + cst::Import::Value(n) => Some(n.value), _ => None, }) .collect(); let hidden_tops: HashSet = items .iter() .filter_map(|i| match i { - cst::Import::TypeOp(n) => Some(*n), + cst::Import::TypeOp(n) => Some(n.value), _ => None, }) .collect(); @@ -1262,19 +1250,19 @@ impl Converter { ) { match item { cst::Import::Value(name) => { - let key = Self::maybe_qualify(*name, qualifier); - let origin = Self::value_origin_site(exports, *name, site); + let key = Self::maybe_qualify(name.value, qualifier); + let origin = Self::value_origin_site(exports, name.value, site); self.values.insert(key, origin); } cst::Import::Type(name, members) => { - let key = Self::maybe_qualify(*name, qualifier); - let origin = Self::type_origin_site(exports, *name, site); + let key = Self::maybe_qualify(name.value, qualifier); + let origin = Self::type_origin_site(exports, name.value, site); self.types.insert(key, origin); // Import constructors if (..) or explicit list if let Some(members) = members { let qi = QualifiedIdent { module: None, - name: *name, + name: name.value, }; if let Some(ctors) = exports.data_constructors.get(&qi) { match members { @@ -1288,8 +1276,8 @@ impl Converter { } cst::DataMembers::Explicit(names) => { for n in names { - let k = Self::maybe_qualify(*n, qualifier); - let ctor_origin = Self::value_origin_site(exports, *n, site); + let k = Self::maybe_qualify(n.value, qualifier); + let ctor_origin = Self::value_origin_site(exports, n.value, site); self.values.insert(k, ctor_origin); } } @@ -1298,20 +1286,20 @@ impl Converter { } } cst::Import::TypeOp(name) => { - let key = Self::maybe_qualify(*name, qualifier); - let origin = Self::value_origin_site(exports, *name, site); + let key = Self::maybe_qualify(name.value, qualifier); + let origin = Self::value_origin_site(exports, name.value, site); self.values.insert(key, origin); } cst::Import::Class(name) => { - let key = Self::maybe_qualify(*name, qualifier); - let origin = Self::class_origin_site(exports, *name, site); + let key = Self::maybe_qualify(name.value, qualifier); + let origin = Self::class_origin_site(exports, name.value, site); self.classes.insert(key, origin); // Import class methods for (method_name, _) in &exports.class_methods { // Check if this method belongs to the imported class let qi = QualifiedIdent { module: None, - name: *name, + name: name.value, }; if exports.class_methods.get(method_name).map(|(cn, _)| cn) == Some(&qi) { let k = Self::maybe_qualify(method_name.name, qualifier); @@ -2935,6 +2923,7 @@ impl Converter { binders, guarded, where_clause, + .. } => { self.push_scope(); for b in binders { @@ -3015,7 +3004,7 @@ impl Converter { where_clause: ast_where, } } - cst::Decl::TypeSignature { span, name, ty } => Decl::TypeSignature { + cst::Decl::TypeSignature { span, name, ty, .. } => Decl::TypeSignature { span: *span, name: name.clone(), ty: self.convert_type_expr(ty), @@ -3029,6 +3018,7 @@ impl Converter { is_role_decl, kind_type, type_var_kind_anns, + .. } => Decl::Data { span: *span, name: name.clone(), @@ -3057,6 +3047,7 @@ impl Converter { type_vars, ty, type_var_kind_anns, + .. } => Decl::TypeAlias { span: *span, name: name.clone(), @@ -3074,6 +3065,7 @@ impl Converter { constructor, ty, type_var_kind_anns, + .. } => Decl::Newtype { span: *span, name: name.clone(), @@ -3095,6 +3087,7 @@ impl Converter { is_kind_sig, kind_type, type_var_kind_anns, + .. } => Decl::Class { span: *span, constraints: constraints @@ -3129,6 +3122,7 @@ impl Converter { types, members, chain, + .. } => Decl::Instance { span: *span, name: name.clone(), @@ -3149,6 +3143,7 @@ impl Converter { target, operator, is_type, + .. } => { let target_def = if *is_type { self.resolve_type(target, *span) @@ -3165,12 +3160,12 @@ impl Converter { is_type: *is_type, } } - cst::Decl::Foreign { span, name, ty } => Decl::Foreign { + cst::Decl::Foreign { span, name, ty, .. } => Decl::Foreign { span: *span, name: name.clone(), ty: self.convert_type_expr(ty), }, - cst::Decl::ForeignData { span, name, kind } => Decl::ForeignData { + cst::Decl::ForeignData { span, name, kind, .. } => Decl::ForeignData { span: *span, name: name.clone(), kind: self.convert_type_expr(kind), @@ -3182,6 +3177,7 @@ impl Converter { constraints, class_name, types, + .. } => { // Use lenient class resolution for derive declarations — the typechecker // handles derive classes specially (e.g. Newtype, Eq, Ord) and they may diff --git a/src/codegen/js.rs b/src/codegen/js.rs index c5d42460..82ab482f 100644 --- a/src/codegen/js.rs +++ b/src/codegen/js.rs @@ -302,7 +302,7 @@ fn is_exported(ctx: &CodegenCtx, name: Symbol) -> bool { } } Export::Type(_, Some(DataMembers::Explicit(ctors))) => { - if ctors.contains(&name) { + if ctors.iter().any(|c| c.value == name) { return true; } } diff --git a/src/cst.rs b/src/cst.rs index 6dec05f2..2e23704e 100644 --- a/src/cst.rs +++ b/src/cst.rs @@ -17,6 +17,31 @@ use std::fmt::Display; use crate::span::Span; use crate::lexer::token::Ident; +/// A source comment +#[derive(Debug, Clone, PartialEq)] +pub enum Comment { + /// `-- text` + Line(String), + /// `{- text -}` + Block(String), + /// `-- | text` (documentation comment) + Doc(String), +} + +impl Comment { + /// Returns true if this is a doc-comment (`-- | ...`) + pub fn is_doc(&self) -> bool { + matches!(self, Comment::Doc(_)) + } + + /// Get the text content of this comment + pub fn text(&self) -> &str { + match self { + Comment::Line(s) | Comment::Block(s) | Comment::Doc(s) => s, + } + } +} + /// Module with full span information #[derive(Debug, Clone, PartialEq)] pub struct Module { @@ -25,6 +50,8 @@ pub struct Module { pub exports: Option>, pub imports: Vec, pub decls: Vec, + /// All comments in the module source, in order of appearance (comment, span) + pub comments: Vec<(Comment, Span)>, } /// Module name (potentially qualified: Data.Array) @@ -67,7 +94,7 @@ pub enum Export { #[derive(Debug, Clone, PartialEq)] pub enum DataMembers { All, // (..) - Explicit(Vec), // (Foo, Bar) + Explicit(Vec>), // (Foo, Bar) } /// Import declaration @@ -89,10 +116,28 @@ pub enum ImportList { /// Single import #[derive(Debug, Clone, PartialEq)] pub enum Import { - Value(Ident), - Type(Ident, Option), - TypeOp(Ident), - Class(Ident), + Value(Spanned), + Type(Spanned, Option), + TypeOp(Spanned), + Class(Spanned), +} + +impl Import { + /// Get the unqualified name of this import item. + pub fn name(&self) -> Ident { + match self { + Import::Value(n) | Import::Type(n, _) | Import::TypeOp(n) | Import::Class(n) => { + n.value + } + } + } + + /// Get the spanned name of this import item. + pub fn spanned_name(&self) -> &Spanned { + match self { + Import::Value(n) | Import::Type(n, _) | Import::TypeOp(n) | Import::Class(n) => n, + } + } } /// What kind of kind signature this Decl::Data represents (if any) @@ -120,6 +165,8 @@ pub enum Decl { binders: Vec, guarded: GuardedExpr, where_clause: Vec, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Type signature: foo :: Int -> Int @@ -127,6 +174,8 @@ pub enum Decl { span: Span, name: Spanned, ty: TypeExpr, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Data declaration: data Foo a = Bar a | Baz @@ -143,6 +192,8 @@ pub enum Decl { kind_type: Option>, /// Kind annotations on type parameters (e.g., `(p :: Test)` → Some(TypeExpr for Test)) type_var_kind_anns: Vec>>, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Type synonym: type Foo = Bar @@ -153,6 +204,8 @@ pub enum Decl { ty: TypeExpr, /// Kind annotations on type parameters (e.g., `(a :: Type -> Type)`) type_var_kind_anns: Vec>>, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Newtype: newtype Foo = Foo Bar @@ -164,6 +217,8 @@ pub enum Decl { ty: TypeExpr, /// Kind annotations on type parameters type_var_kind_anns: Vec>>, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Type class declaration: class Eq a where ... @@ -180,6 +235,8 @@ pub enum Decl { kind_type: Option>, /// Kind annotations on type parameters (e.g., `class C (a :: Type -> Type)`) type_var_kind_anns: Vec>>, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Instance declaration: instance Eq Int where ... @@ -192,6 +249,8 @@ pub enum Decl { members: Vec, /// True if this instance is a continuation of an instance chain (preceded by `else`) chain: bool, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Fixity declaration: infixl 6 add as + @@ -203,6 +262,8 @@ pub enum Decl { target: QualifiedIdent, operator: Spanned, is_type: bool, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Foreign value import: foreign import foo :: Type @@ -210,6 +271,8 @@ pub enum Decl { span: Span, name: Spanned, ty: TypeExpr, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Foreign data import: foreign import data Foo :: Kind @@ -217,6 +280,8 @@ pub enum Decl { span: Span, name: Spanned, kind: TypeExpr, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, /// Derive instance declaration: derive instance Eq MyType @@ -227,9 +292,47 @@ pub enum Decl { constraints: Vec, class_name: QualifiedIdent, types: Vec, + /// Doc-comments (`-- | ...`) preceding this declaration + doc_comments: Vec, }, } +impl Decl { + /// Get the doc-comments attached to this declaration. + pub fn doc_comments(&self) -> &[Comment] { + match self { + Decl::Value { doc_comments, .. } + | Decl::TypeSignature { doc_comments, .. } + | Decl::Data { doc_comments, .. } + | Decl::TypeAlias { doc_comments, .. } + | Decl::Newtype { doc_comments, .. } + | Decl::Class { doc_comments, .. } + | Decl::Instance { doc_comments, .. } + | Decl::Fixity { doc_comments, .. } + | Decl::Foreign { doc_comments, .. } + | Decl::ForeignData { doc_comments, .. } + | Decl::Derive { doc_comments, .. } => doc_comments, + } + } + + /// Set the doc-comments on this declaration. + pub fn set_doc_comments(&mut self, comments: Vec) { + match self { + Decl::Value { doc_comments, .. } + | Decl::TypeSignature { doc_comments, .. } + | Decl::Data { doc_comments, .. } + | Decl::TypeAlias { doc_comments, .. } + | Decl::Newtype { doc_comments, .. } + | Decl::Class { doc_comments, .. } + | Decl::Instance { doc_comments, .. } + | Decl::Fixity { doc_comments, .. } + | Decl::Foreign { doc_comments, .. } + | Decl::ForeignData { doc_comments, .. } + | Decl::Derive { doc_comments, .. } => *doc_comments = comments, + } + } +} + /// Functional dependency: a b -> c #[derive(Debug, Clone, PartialEq)] pub struct FunDep { @@ -243,6 +346,8 @@ pub struct DataConstructor { pub span: Span, pub name: Spanned, pub fields: Vec, + /// Doc-comments preceding this constructor + pub doc_comments: Vec, } /// Class member (method signature) @@ -251,6 +356,8 @@ pub struct ClassMember { pub span: Span, pub name: Spanned, pub ty: TypeExpr, + /// Doc-comments preceding this class member + pub doc_comments: Vec, } /// Operator associativity diff --git a/src/lexer/layout.rs b/src/lexer/layout.rs index 79a316d0..6f1ddf48 100644 --- a/src/lexer/layout.rs +++ b/src/lexer/layout.rs @@ -41,10 +41,13 @@ fn layout_delim_for(token: &Token) -> LayoutDelim { } } -/// Preprocess raw tokens: convert to Token, skip newlines/comments, +/// Preprocess raw tokens: convert to Token, skip newlines, collect comments, /// and combine `UpperIdent "." Do/Ado` into `QualifiedDo`/`QualifiedAdo`. -fn preprocess_tokens(raw_tokens: Vec<(RawToken, Span)>) -> Vec<(Token, Span)> { - // Phase 1: convert raw tokens, skip newlines and comments +fn preprocess_tokens( + raw_tokens: Vec<(RawToken, Span)>, + comments_out: &mut Vec<(crate::cst::Comment, Span)>, +) -> Vec<(Token, Span)> { + // Phase 1: convert raw tokens, skip newlines and collect comments let mut tokens: Vec<(Token, Span)> = Vec::new(); for (raw_token, span) in raw_tokens { if matches!(raw_token, RawToken::Newline) { @@ -53,11 +56,20 @@ fn preprocess_tokens(raw_tokens: Vec<(RawToken, Span)>) -> Vec<(Token, Span)> { let Some(token) = raw_token.to_token() else { continue; }; - if matches!( - token, - Token::LineComment(_) | Token::BlockComment(_) | Token::DocComment(_) - ) { - continue; + match &token { + Token::LineComment(s) => { + comments_out.push((crate::cst::Comment::Line(s.clone()), span)); + continue; + } + Token::DocComment(s) => { + comments_out.push((crate::cst::Comment::Doc(s.clone()), span)); + continue; + } + Token::BlockComment(s) => { + comments_out.push((crate::cst::Comment::Block(s.clone()), span)); + continue; + } + _ => {} } tokens.push((token, span)); } @@ -102,8 +114,12 @@ fn preprocess_tokens(raw_tokens: Vec<(RawToken, Span)>) -> Vec<(Token, Span)> { /// - Tokens at a greater column are continuations (no action). /// - 'in' keyword explicitly closes 'let' layout blocks. /// - Closing delimiters ) ] } close all implicit layout blocks until matching opener. -pub fn process_layout(raw_tokens: Vec<(RawToken, Span)>, source: &str) -> Vec { - let tokens = preprocess_tokens(raw_tokens); +pub fn process_layout( + raw_tokens: Vec<(RawToken, Span)>, + source: &str, +) -> (Vec, Vec<(crate::cst::Comment, Span)>) { + let mut comments = Vec::new(); + let tokens = preprocess_tokens(raw_tokens, &mut comments); let mut result = Vec::new(); let mut stack: Vec = vec![]; let mut pending_layout: Option = None; @@ -424,7 +440,7 @@ pub fn process_layout(raw_tokens: Vec<(RawToken, Span)>, source: &str) -> Vec>, + pub comments: Vec>, +} + /// Main lexer entry point: lex and process layout -pub fn lex(source: &str) -> Result>, LexError> { +pub fn lex(source: &str) -> Result { // Step 1: Raw lexing with Logos let raw_tokens = lex_raw(source)?; - // Step 2: Layout processing - let tokens = process_layout(raw_tokens, source); + // Step 2: Layout processing (also collects comments) + let (tokens, comments) = process_layout(raw_tokens, source); // Step 3: Merge adjacent Tilde + Operator tokens (e.g., ~ > → ~>) // Logos lexes ~ with higher priority than operators, so ~> becomes two tokens. @@ -26,10 +32,17 @@ pub fn lex(source: &str) -> Result>, LexError> { let tokens = resolve_qualified_names(tokens); // Step 5: Convert to spanned tokens - Ok(tokens + let tokens = tokens .into_iter() .map(|(tok, span)| Spanned::new(tok, span)) - .collect()) + .collect(); + + let comments = comments + .into_iter() + .map(|(comment, span)| Spanned::new(comment, span)) + .collect(); + + Ok(LexResult { tokens, comments }) } /// Merge adjacent Tilde tokens with following Operator/Tilde tokens into a single Operator. diff --git a/src/lib.rs b/src/lib.rs index 67c53580..9a8bb50b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub mod typechecker; pub mod build; pub mod js_ffi; pub mod codegen; +pub mod lsp; // Re-export main types pub use lexer::{Token, lex}; diff --git a/src/lsp/handlers/completion.rs b/src/lsp/handlers/completion.rs new file mode 100644 index 00000000..b1f5b3ea --- /dev/null +++ b/src/lsp/handlers/completion.rs @@ -0,0 +1,341 @@ +use std::collections::HashSet; + +use tower_lsp::jsonrpc::Result; +use tower_lsp::lsp_types::*; + +use crate::cst::{self, ImportList}; +use crate::interner; +use crate::lsp::utils::find_definition::position_to_offset; + +use super::super::Backend; + +impl Backend { + pub(crate) async fn handle_completion( + &self, + params: CompletionParams, + ) -> Result> { + if !self.ready.load(std::sync::atomic::Ordering::SeqCst) { + return Ok(None); + } + + let uri = params.text_document_position.text_document.uri; + let pos = params.text_document_position.position; + + let source = { + let files = self.files.read().await; + files.get(&uri.to_string()).map(|f| f.source.clone()) + }; + let source = match source { + Some(s) => s, + None => return Ok(None), + }; + + let offset = match position_to_offset(&source, pos.line, pos.character) { + Some(o) => o, + None => return Ok(None), + }; + + // Extract the identifier prefix at the cursor position + let prefix = extract_prefix(&source, offset); + if prefix.is_empty() { + return Ok(None); + } + + let module = match crate::parser::parse(&source) { + Ok(m) => m, + Err(_) => return Ok(None), + }; + + // Collect what's already imported in this module + let current_module_name = interner::resolve_module_name(&module.name.value.parts); + let already_imported = collect_imported_names(&module); + + // Find insert position for new imports (after last import, or after module header) + let import_insert_line = find_import_insert_line(&source, &module); + + let registry = self.registry.read().await; + let mut items = Vec::new(); + let mut seen = HashSet::new(); + + // 1. Local declarations from the current module + for decl in &module.decls { + if let Some(name_sym) = decl_name(decl) { + let name = match interner::resolve(name_sym) { + Some(n) => n.to_string(), + None => continue, + }; + if !name.starts_with(&prefix) { + continue; + } + if seen.contains(&name) { + continue; + } + seen.insert(name.clone()); + + let (kind, detail) = local_decl_info(decl); + + items.push(CompletionItem { + label: name, + kind: Some(kind), + detail, + sort_text: Some(format!("0{}", items.len())), + ..Default::default() + }); + } + } + + // 2. Already-imported names (higher priority than unimported) + // 3. All exported values from all modules in the registry + for (mod_path, mod_exports) in registry.iter_all() { + let mod_name = interner::resolve_module_name(mod_path); + if mod_name == current_module_name { + continue; + } + + for (qi, scheme) in &mod_exports.values { + let name = match interner::resolve(qi.name) { + Some(n) => n.to_string(), + None => continue, + }; + if !name.starts_with(&prefix) { + continue; + } + if seen.contains(&name) { + continue; + } + seen.insert(name.clone()); + + let type_str = format!("{}", scheme.ty); + let is_imported = already_imported.contains(&name); + let is_constructor = name.starts_with(|c: char| c.is_uppercase()); + + let kind = if is_constructor { + CompletionItemKind::CONSTRUCTOR + } else { + CompletionItemKind::FUNCTION + }; + + // Imported items sort before unimported + let sort_prefix = if is_imported { "1" } else { "2" }; + + let mut item = CompletionItem { + label: name.clone(), + kind: Some(kind), + detail: Some(format!("{mod_name} :: {type_str}")), + sort_text: Some(format!("{sort_prefix}{}", items.len())), + ..Default::default() + }; + + // Auto-import: add additional_text_edits if not already imported + if !is_imported { + if let Some(edit) = build_import_edit( + &mod_name, + &name, + is_constructor, + &module, + &source, + import_insert_line, + ) { + item.additional_text_edits = Some(vec![edit]); + } + } + + items.push(item); + } + + // Also add type constructors + for (type_qi, ctor_names) in &mod_exports.data_constructors { + for ctor_qi in ctor_names { + let ctor_name = match interner::resolve(ctor_qi.name) { + Some(n) => n.to_string(), + None => continue, + }; + if !ctor_name.starts_with(&prefix) { + continue; + } + if seen.contains(&ctor_name) { + continue; + } + // Only add if the constructor has a value entry (it's exported) + if !mod_exports.values.contains_key(ctor_qi) { + continue; + } + // Already handled in the values loop above + } + let _ = type_qi; + } + } + + Ok(Some(CompletionResponse::List(CompletionList { + is_incomplete: items.len() > 100, + items, + }))) + } +} + +/// Extract the identifier prefix before the cursor position. +fn extract_prefix(source: &str, offset: usize) -> String { + let before = &source[..offset]; + let start = before + .rfind(|c: char| !c.is_alphanumeric() && c != '_' && c != '\'') + .map(|i| i + 1) + .unwrap_or(0); + before[start..].to_string() +} + +/// Collect all names that are already imported (or locally defined) in the module. +fn collect_imported_names(module: &cst::Module) -> HashSet { + let mut names = HashSet::new(); + + // Local declarations + for decl in &module.decls { + if let Some(sym) = decl_name(decl) { + if let Some(n) = interner::resolve(sym) { + names.insert(n.to_string()); + } + } + } + + // Imported names + for import_decl in &module.imports { + match &import_decl.imports { + Some(ImportList::Explicit(items)) => { + for item in items { + if let Some(n) = interner::resolve(item.name()) { + names.insert(n.to_string()); + } + } + } + Some(ImportList::Hiding(_)) | None => { + // `import M` or `import M hiding (...)` — we treat everything as potentially imported + // For simplicity, we don't expand these here; the name might still need importing + // explicitly if the user wants it. We'll be conservative and not auto-import + // if there's a bare `import M` for that module. + } + } + } + + names +} + +/// Get the name symbol from a declaration. +fn decl_name(decl: &cst::Decl) -> Option { + match decl { + cst::Decl::Value { name, .. } + | cst::Decl::TypeSignature { name, .. } + | cst::Decl::Data { name, .. } + | cst::Decl::TypeAlias { name, .. } + | cst::Decl::Newtype { name, .. } + | cst::Decl::Class { name, .. } + | cst::Decl::Foreign { name, .. } + | cst::Decl::ForeignData { name, .. } => Some(name.value), + _ => None, + } +} + +/// Get completion item kind and optional detail for a local declaration. +fn local_decl_info(decl: &cst::Decl) -> (CompletionItemKind, Option) { + match decl { + cst::Decl::Value { .. } | cst::Decl::Foreign { .. } => { + (CompletionItemKind::FUNCTION, None) + } + cst::Decl::TypeSignature { .. } => (CompletionItemKind::FUNCTION, None), + cst::Decl::Data { .. } | cst::Decl::ForeignData { .. } => { + (CompletionItemKind::CLASS, Some("data".to_string())) + } + cst::Decl::Newtype { .. } => (CompletionItemKind::CLASS, Some("newtype".to_string())), + cst::Decl::TypeAlias { .. } => (CompletionItemKind::CLASS, Some("type alias".to_string())), + cst::Decl::Class { .. } => (CompletionItemKind::INTERFACE, Some("class".to_string())), + _ => (CompletionItemKind::TEXT, None), + } +} + +/// Find the line number (0-indexed) where new imports should be inserted. +/// This is after the last existing import, or after the module header if no imports exist. +fn find_import_insert_line(source: &str, module: &cst::Module) -> u32 { + if let Some(last_import) = module.imports.last() { + // Find the line number of the end of the last import + let end = last_import.span.end; + source[..end].chars().filter(|&c| c == '\n').count() as u32 + 1 + } else { + // After module header line + let header_end = module.name.span.end; + source[..header_end].chars().filter(|&c| c == '\n').count() as u32 + 1 + } +} + +/// Build a TextEdit that adds an import statement for the given name from the given module. +/// Returns None if the module already has a bare import for that module. +fn build_import_edit( + mod_name: &str, + name: &str, + _is_constructor: bool, + module: &cst::Module, + source: &str, + import_insert_line: u32, +) -> Option { + // Check if there's already an explicit import from this module that we can extend + for import_decl in &module.imports { + let import_mod_name = interner::resolve_module_name(&import_decl.module.parts); + if import_mod_name == mod_name { + match &import_decl.imports { + Some(ImportList::Explicit(items)) => { + // Extend the existing explicit import list + // Find the closing paren position + let last_item = items.last()?; + let last_span = last_item.spanned_name().span; + // Insert after the last item, before the closing paren + // We need to find where in the source the `)` is after the last item + let after_last = &source[last_span.end..]; + let close_paren_offset = after_last.find(')')? + last_span.end; + let insert_offset = close_paren_offset; + let (line, col) = offset_to_position(source, insert_offset); + return Some(TextEdit { + range: Range { + start: Position { line, character: col }, + end: Position { line, character: col }, + }, + new_text: format!(", {name}"), + }); + } + Some(ImportList::Hiding(_)) | None => { + // Already has a bare import or hiding import — name is likely available + return None; + } + } + } + } + + // No existing import for this module — add a new import line + Some(TextEdit { + range: Range { + start: Position { + line: import_insert_line, + character: 0, + }, + end: Position { + line: import_insert_line, + character: 0, + }, + }, + new_text: format!("import {mod_name} ({name})\n"), + }) +} + +/// Convert a byte offset to (line, character) in LSP 0-indexed coordinates. +fn offset_to_position(source: &str, offset: usize) -> (u32, u32) { + let mut line = 0u32; + let mut col = 0u32; + for (i, c) in source.char_indices() { + if i == offset { + return (line, col); + } + if c == '\n' { + line += 1; + col = 0; + } else { + col += 1; + } + } + (line, col) +} diff --git a/src/lsp/handlers/definition.rs b/src/lsp/handlers/definition.rs new file mode 100644 index 00000000..b969c868 --- /dev/null +++ b/src/lsp/handlers/definition.rs @@ -0,0 +1,255 @@ +use std::sync::atomic::Ordering; + +use tower_lsp::jsonrpc::Result; +use tower_lsp::lsp_types::*; + +use crate::lsp::utils::find_definition::position_to_offset; +use crate::lsp::utils::resolve::{self, DefinitionSite, Namespace}; + +use super::super::Backend; + +impl Backend { + pub(crate) async fn handle_goto_definition( + &self, + params: GotoDefinitionParams, + ) -> Result> { + if !self.ready.load(Ordering::SeqCst) { + return Ok(None); + } + + let uri = params.text_document_position_params.text_document.uri; + let pos = params.text_document_position_params.position; + + // Get the source for this file + let source = { + let files = self.files.read().await; + files.get(&uri.to_string()).map(|f| f.source.clone()) + }; + let source = match source { + Some(s) => s, + None => return Ok(None), + }; + + // Convert LSP position to byte offset + let offset = match position_to_offset(&source, pos.line, pos.character) { + Some(o) => o, + None => return Ok(None), + }; + + // Parse the current file to get CST + let module = match crate::parser::parse(&source) { + Ok(m) => m, + Err(_) => return Ok(None), + }; + + // Check if the cursor is on an import item — these have spans now + if let Some(result) = self.find_import_item_definition(&module, offset).await { + return Ok(Some(result)); + } + + // Resolve names using the project-wide resolution exports + let exports = self.resolution_exports.read().await; + let resolved = resolve::resolve_names(&module, &exports); + drop(exports); + + // Look up what's at the cursor + let resolved_name = match resolved.lookup_at(offset) { + Some(r) => r.clone(), + None => return Ok(None), + }; + + match &resolved_name.definition { + DefinitionSite::Local(span) | DefinitionSite::LocalVar(span) => { + if let Some(loc) = span_to_location(&uri, &source, *span) { + return Ok(Some(GotoDefinitionResponse::Scalar(loc))); + } + } + DefinitionSite::Imported(module_sym) => { + // Look up the module's file URI + let module_name = crate::interner::resolve(*module_sym) + .unwrap_or_default(); + + let target_uri = { + let mf = self.module_file_map.read().await; + mf.get(&module_name).cloned() + }; + + if let Some(target_uri) = target_uri { + let def_index = self.def_index.read().await; + let key = (module_name.clone(), resolved_name.src_symbol); + let def_loc = match resolved_name.namespace { + Namespace::Value => def_index.values.get(&key) + .or_else(|| def_index.constructors.get(&key)), + Namespace::Type | Namespace::Class | Namespace::TypeOperator => { + def_index.types.get(&key) + .or_else(|| def_index.constructors.get(&key)) + } + }; + + // If direct lookup fails, try re-export fallback + // (e.g. `add` imported from Prelude but defined in Data.Semiring) + let def_loc_owned; + let def_loc = if def_loc.is_some() { + def_loc + } else { + match def_index.find_reexport(resolved_name.src_symbol, resolved_name.namespace) { + Some((reexport_module, loc)) => { + // Update target URI to the actual defining module + let reexport_uri = { + let mf = self.module_file_map.read().await; + mf.get(reexport_module).cloned() + }; + if let Some(reexport_uri) = reexport_uri { + let target_source = { + let sm = self.source_map.read().await; + sm.get(&reexport_uri).cloned() + }; + if let Some(target_source) = target_source { + if let Ok(parsed_uri) = Url::parse(&reexport_uri) { + if let Some(loc) = span_to_location(&parsed_uri, &target_source, loc.span) { + return Ok(Some(GotoDefinitionResponse::Scalar(loc))); + } + } + } + } + def_loc_owned = loc.clone(); + Some(&def_loc_owned) + } + None => None, + } + }; + + if let Some(def_loc) = def_loc { + let target_source = { + let sm = self.source_map.read().await; + sm.get(&target_uri).cloned() + }; + + if let Some(target_source) = target_source { + if let Ok(parsed_uri) = Url::parse(&target_uri) { + if let Some(loc) = span_to_location(&parsed_uri, &target_source, def_loc.span) { + return Ok(Some(GotoDefinitionResponse::Scalar(loc))); + } + } + } + } + } + } + DefinitionSite::Prim => { + // Prim types have no source location + } + } + + Ok(None) + } + + /// Check if the cursor offset falls on an import item name. + /// If so, look up the definition in the import's source module. + async fn find_import_item_definition( + &self, + module: &crate::cst::Module, + offset: usize, + ) -> Option { + use crate::cst::{Import, ImportList, DataMembers}; + + for import_decl in &module.imports { + // Quick bounds check: is offset within this import's span? + if offset < import_decl.span.start || offset >= import_decl.span.end { + continue; + } + + let items = match &import_decl.imports { + Some(ImportList::Explicit(items)) | Some(ImportList::Hiding(items)) => items, + None => continue, + }; + + // Check each import item's span + for item in items { + let spanned = item.spanned_name(); + if offset >= spanned.span.start && offset < spanned.span.end { + let symbol = spanned.value; + let namespace = match item { + Import::Value(_) => Namespace::Value, + Import::Type(_, _) => Namespace::Type, + Import::TypeOp(_) => Namespace::TypeOperator, + Import::Class(_) => Namespace::Class, + }; + let module_name = crate::interner::resolve_module_name(&import_decl.module.parts); + return self.resolve_import_symbol(&module_name, symbol, namespace).await; + } + + // Also check DataMembers::Explicit constructor names + if let Import::Type(_, Some(DataMembers::Explicit(ctors))) = item { + for ctor in ctors { + if offset >= ctor.span.start && offset < ctor.span.end { + let module_name = crate::interner::resolve_module_name(&import_decl.module.parts); + return self.resolve_import_symbol(&module_name, ctor.value, Namespace::Value).await; + } + } + } + } + } + None + } + + /// Resolve a symbol from an import to its definition location. + async fn resolve_import_symbol( + &self, + module_name: &str, + symbol: crate::interner::Symbol, + namespace: Namespace, + ) -> Option { + let def_index = self.def_index.read().await; + let key = (module_name.to_string(), symbol); + + // Try direct lookup in the import source module + let def_loc = match namespace { + Namespace::Value => def_index.values.get(&key) + .or_else(|| def_index.constructors.get(&key)), + Namespace::Type | Namespace::Class | Namespace::TypeOperator => { + def_index.types.get(&key) + .or_else(|| def_index.constructors.get(&key)) + } + }; + + // Fall back to re-export search (e.g. Prelude re-exports from Data.Semiring) + let reexport; + let (target_module, def_loc) = if let Some(loc) = def_loc { + (module_name.to_string(), loc) + } else { + reexport = def_index.find_reexport(symbol, namespace)?; + (reexport.0.clone(), reexport.1) + }; + + let target_uri = { + let mf = self.module_file_map.read().await; + mf.get(&target_module).cloned() + }?; + + let target_source = { + let sm = self.source_map.read().await; + sm.get(&target_uri).cloned() + }?; + + let parsed_uri = Url::parse(&target_uri).ok()?; + let loc = span_to_location(&parsed_uri, &target_source, def_loc.span)?; + Some(GotoDefinitionResponse::Scalar(loc)) + } +} + +fn span_to_location(uri: &Url, source: &str, span: crate::span::Span) -> Option { + let (start, end) = span.to_pos(source)?; + Some(Location { + uri: uri.clone(), + range: Range { + start: Position { + line: start.line.saturating_sub(1) as u32, + character: start.column.saturating_sub(1) as u32, + }, + end: Position { + line: end.line.saturating_sub(1) as u32, + character: end.column.saturating_sub(1) as u32, + }, + }, + }) +} diff --git a/src/lsp/handlers/diagnostics.rs b/src/lsp/handlers/diagnostics.rs new file mode 100644 index 00000000..cf397610 --- /dev/null +++ b/src/lsp/handlers/diagnostics.rs @@ -0,0 +1,116 @@ +use std::fmt::Display; +use std::sync::atomic::Ordering; + +use tower_lsp::lsp_types::*; + +use super::super::{Backend, FileState}; + +impl Backend { + pub(crate) async fn info(&self, message: M) { + self.client + .log_message(MessageType::INFO, message) + .await; + } + + pub(crate) async fn on_change(&self, uri: Url, source: String) { + { + let mut files = self.files.write().await; + files.insert( + uri.to_string(), + FileState { + source: source.clone(), + module_name: None, + }, + ); + } + + // Don't publish diagnostics until sources are loaded + if !self.ready.load(Ordering::SeqCst) { + return; + } + + let module = match crate::parser::parse(&source) { + Ok(module) => { + let module_name = format!("{}", module.name.value); + { + let mut files = self.files.write().await; + if let Some(fs) = files.get_mut(&uri.to_string()) { + fs.module_name = Some(module_name); + } + } + module + } + Err(err) => { + let range = error_to_range(&err, &source); + let diagnostics = vec![Diagnostic { + range, + severity: Some(DiagnosticSeverity::ERROR), + code: Some(NumberOrString::String(err.code())), + source: Some("pfc".to_string()), + message: err.get_message(), + ..Default::default() + }]; + self.client + .publish_diagnostics(uri, diagnostics, None) + .await; + return; + } + }; + + // Type-check against the registry + let registry = self.registry.read().await; + let check_result = crate::typechecker::check_module_with_registry(&module, ®istry); + + let diagnostics: Vec = check_result + .errors + .iter() + .map(|err| { + let span = err.span(); + let range = match span.to_pos(&source) { + Some((start, end)) => Range { + start: Position { + line: start.line.saturating_sub(1) as u32, + character: start.column.saturating_sub(1) as u32, + }, + end: Position { + line: end.line.saturating_sub(1) as u32, + character: end.column.saturating_sub(1) as u32, + }, + }, + None => Range::default(), + }; + Diagnostic { + range, + severity: Some(DiagnosticSeverity::ERROR), + code: Some(NumberOrString::String(format!("TypeError.{}", err.code()))), + source: Some("pfc".to_string()), + message: format!("{err}"), + ..Default::default() + } + }) + .collect(); + + self.client + .publish_diagnostics(uri, diagnostics, None) + .await; + } +} + +fn error_to_range(err: &crate::diagnostics::CompilerError, source: &str) -> Range { + match err.get_span() { + Some(span) => match span.to_pos(source) { + Some((start, end)) => Range { + start: Position { + line: start.line.saturating_sub(1) as u32, + character: start.column.saturating_sub(1) as u32, + }, + end: Position { + line: end.line.saturating_sub(1) as u32, + character: end.column.saturating_sub(1) as u32, + }, + }, + None => Range::default(), + }, + None => Range::default(), + } +} diff --git a/src/lsp/handlers/hover.rs b/src/lsp/handlers/hover.rs new file mode 100644 index 00000000..c4a67cf9 --- /dev/null +++ b/src/lsp/handlers/hover.rs @@ -0,0 +1,486 @@ +use std::sync::atomic::Ordering; + +use tower_lsp::jsonrpc::Result; +use tower_lsp::lsp_types::*; + +use crate::cst::{self, unqualified_ident, Comment, Decl}; +use crate::interner; +use crate::lsp::utils::find_definition::position_to_offset; +use crate::lsp::utils::resolve::{self, DefinitionSite, Namespace}; + +use super::super::Backend; + +/// Info about what the cursor is on: either a resolved reference or a declaration name. +enum HoverTarget { + /// Cursor is on a reference to a name (resolved by resolve_names). + Reference(resolve::ResolvedName), + /// Cursor is on a value declaration name (the definition site itself). + ValueDeclaration(interner::Symbol), + /// Cursor is on a type/data declaration name. + TypeDeclaration(interner::Symbol), +} + +impl Backend { + pub(crate) async fn handle_hover(&self, params: HoverParams) -> Result> { + if !self.ready.load(Ordering::SeqCst) { + return Ok(None); + } + + let uri = params.text_document_position_params.text_document.uri; + let pos = params.text_document_position_params.position; + + let source = { + let files = self.files.read().await; + files.get(&uri.to_string()).map(|f| f.source.clone()) + }; + let source = match source { + Some(s) => s, + None => return Ok(None), + }; + + let offset = match position_to_offset(&source, pos.line, pos.character) { + Some(o) => o, + None => return Ok(None), + }; + + let module = match crate::parser::parse(&source) { + Ok(m) => m, + Err(_) => return Ok(None), + }; + + // Check if cursor is on an import item + if let Some(hover) = self.hover_import_item(&module, offset).await { + return Ok(Some(hover)); + } + + // Try resolve_names first (for references), then check declaration sites + let exports = self.resolution_exports.read().await; + let resolved = resolve::resolve_names(&module, &exports); + drop(exports); + + let target = if let Some(r) = resolved.lookup_at(offset) { + HoverTarget::Reference(r.clone()) + } else if let Some((sym, is_type)) = find_decl_name_at_offset(&module.decls, offset) { + if is_type { + HoverTarget::TypeDeclaration(sym) + } else { + HoverTarget::ValueDeclaration(sym) + } + } else { + // Fallback: check span_types for record labels and other spans + // that resolve_names doesn't track + return self.hover_span_type(&module, &source, offset).await; + }; + + let (symbol, name_str, type_str, namespace) = match &target { + HoverTarget::Reference(resolved_name) => { + let name_str = interner::resolve(resolved_name.src_symbol).unwrap_or_default(); + + let type_str = match &resolved_name.definition { + DefinitionSite::Local(_) => { + self.get_local_type(&module, resolved_name.src_symbol, &source).await + } + DefinitionSite::LocalVar(local_span) => { + self.get_local_var_type(&module, *local_span).await + } + DefinitionSite::Imported(module_sym) => { + let ty = self.get_imported_type(*module_sym, &name_str).await; + if ty.is_none() && matches!(resolved_name.namespace, Namespace::Type | Namespace::Class) { + // For imported types/classes, show kind from source module CST + self.get_imported_kind(*module_sym, &name_str).await + } else { + ty + } + } + DefinitionSite::Prim => match resolved_name.namespace { + Namespace::Type | Namespace::Class => Some("Type".to_string()), + _ => None, + }, + }; + + match type_str { + Some(s) => (resolved_name.src_symbol, name_str, s, resolved_name.namespace), + None => return Ok(None), + } + } + HoverTarget::ValueDeclaration(sym) => { + let name_str = interner::resolve(*sym).unwrap_or_default(); + let type_str = self.get_local_type(&module, *sym, &source).await; + match type_str { + Some(s) => (*sym, name_str, s, Namespace::Value), + None => return Ok(None), + } + } + HoverTarget::TypeDeclaration(sym) => { + let name_str = interner::resolve(*sym).unwrap_or_default(); + let kind_str = self.get_local_kind(&module, *sym).await + .unwrap_or_else(|| "Type".to_string()); + (*sym, name_str, kind_str, Namespace::Type) + } + }; + + // Look up doc-comments: local CST first, then imported module + let doc_comments = find_doc_comments(&module.decls, symbol); + let imported_docs = if doc_comments.is_empty() { + if let HoverTarget::Reference(resolved_name) = &target { + if let DefinitionSite::Imported(module_sym) = &resolved_name.definition { + let module_name = interner::resolve(*module_sym).unwrap_or_default(); + self.get_imported_doc_comments(&module_name, symbol).await + } else { + Vec::new() + } + } else { + Vec::new() + } + } else { + Vec::new() + }; + + // Build markdown content + let mut markdown = format!("```purescript\n{name_str} :: {type_str}\n```"); + + if !doc_comments.is_empty() { + markdown.push_str("\n\n---\n\n"); + for comment in &doc_comments { + if let Comment::Doc(text) = comment { + markdown.push_str(text.trim()); + markdown.push('\n'); + } + } + } else if !imported_docs.is_empty() { + markdown.push_str("\n\n---\n\n"); + for doc in &imported_docs { + markdown.push_str(doc.trim()); + markdown.push('\n'); + } + } + + let _ = namespace; + + Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: markdown, + }), + range: None, + })) + } + + async fn hover_span_type(&self, module: &cst::Module, source: &str, offset: usize) -> Result> { + let registry = self.registry.read().await; + let check_result = crate::typechecker::check_module_for_ide(module, ®istry); + for (span, ty) in &check_result.span_types { + if offset >= span.start && offset < span.end { + let label = &source[span.start..span.end]; + let type_str = format!("{ty}"); + let markdown = format!("```purescript\n{label} :: {type_str}\n```"); + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: markdown, + }), + range: None, + })); + } + } + Ok(None) + } + + async fn get_local_var_type(&self, module: &cst::Module, span: crate::span::Span) -> Option { + let registry = self.registry.read().await; + let check_result = crate::typechecker::check_module_for_ide(module, ®istry); + check_result.span_types.get(&span).map(|ty| format!("{ty}")) + } + + async fn get_local_type(&self, module: &cst::Module, symbol: interner::Symbol, source: &str) -> Option { + let registry = self.registry.read().await; + let check_result = crate::typechecker::check_module_with_registry(module, ®istry); + if let Some(ty) = check_result.types.get(&symbol) { + return Some(format!("{ty}")); + } + // Fall back to CST type signatures for declarations not in CheckResult.types + // (foreign imports, class methods, etc.) + find_cst_type_signature(&module.decls, symbol, source) + } + + async fn hover_import_item( + &self, + module: &cst::Module, + offset: usize, + ) -> Option { + use crate::cst::{Import, ImportList}; + + for import_decl in &module.imports { + if offset < import_decl.span.start || offset >= import_decl.span.end { + continue; + } + let items = match &import_decl.imports { + Some(ImportList::Explicit(items)) | Some(ImportList::Hiding(items)) => items, + None => continue, + }; + for item in items { + let spanned = item.spanned_name(); + if offset >= spanned.span.start && offset < spanned.span.end { + let symbol = spanned.value; + let name_str = interner::resolve(symbol).unwrap_or_default(); + let module_name = interner::resolve_module_name(&import_decl.module.parts); + let type_str = self.get_imported_type_by_name(&module_name, &name_str).await; + let type_str = match type_str { + Some(t) => t, + None => match item { + Import::Type(_, _) | Import::Class(_) => { + self.get_imported_kind_by_name(&module_name, &name_str).await + .unwrap_or_else(|| "Type".to_string()) + } + _ => "unknown".to_string(), + }, + }; + // Look up doc-comments from the source module + let doc_comments = self.get_imported_doc_comments(&module_name, symbol).await; + let mut markdown = format!("```purescript\n{name_str} :: {type_str}\n```"); + if !doc_comments.is_empty() { + markdown.push_str("\n\n---\n\n"); + for doc in &doc_comments { + markdown.push_str(doc.trim()); + markdown.push('\n'); + } + } + return Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: markdown, + }), + range: None, + }); + } + } + } + None + } + + async fn get_imported_type_by_name(&self, module_name: &str, name_str: &str) -> Option { + let module_parts: Vec = module_name + .split('.') + .map(|s| interner::intern(s)) + .collect(); + let registry = self.registry.read().await; + let mod_exports = registry.lookup(&module_parts)?; + let qi = unqualified_ident(name_str); + mod_exports + .values + .get(&qi) + .map(|scheme| format!("{}", scheme.ty)) + } + + async fn get_imported_doc_comments(&self, module_name: &str, symbol: interner::Symbol) -> Vec { + // Find the source file for this module and parse it to extract doc-comments + let target_uri = { + let mf = self.module_file_map.read().await; + mf.get(module_name).cloned() + }; + let target_uri = match target_uri { + Some(u) => u, + None => return Vec::new(), + }; + let target_source = { + let sm = self.source_map.read().await; + sm.get(&target_uri).cloned() + }; + let target_source = match target_source { + Some(s) => s, + None => return Vec::new(), + }; + let target_module = match crate::parser::parse(&target_source) { + Ok(m) => m, + Err(_) => return Vec::new(), + }; + find_doc_comments(&target_module.decls, symbol) + .into_iter() + .filter_map(|c| { + if let cst::Comment::Doc(text) = c { + Some(text) + } else { + None + } + }) + .collect() + } + + async fn get_local_kind(&self, module: &cst::Module, symbol: interner::Symbol) -> Option { + let registry = self.registry.read().await; + let check_result = crate::typechecker::check_module_with_registry(module, ®istry); + if let Some(kind) = check_result.exports.class_type_kinds.get(&symbol) { + return Some(format!("{kind}")); + } + if let Some(kind) = check_result.exports.type_kinds.get(&symbol) { + return Some(format!("{kind}")); + } + None + } + + async fn get_imported_type(&self, module_sym: interner::Symbol, name_str: &str) -> Option { + let module_name = interner::resolve(module_sym).unwrap_or_default(); + let module_parts: Vec = module_name + .split('.') + .map(|s| interner::intern(s)) + .collect(); + + let registry = self.registry.read().await; + let mod_exports = registry.lookup(&module_parts)?; + let qi = unqualified_ident(name_str); + mod_exports + .values + .get(&qi) + .map(|scheme| format!("{}", scheme.ty)) + } + + async fn get_imported_kind(&self, module_sym: interner::Symbol, name_str: &str) -> Option { + let module_name = interner::resolve(module_sym).unwrap_or_default(); + self.get_imported_kind_by_name(&module_name, name_str).await + } + + async fn get_imported_kind_by_name(&self, module_name: &str, name_str: &str) -> Option { + let module_parts: Vec = module_name + .split('.') + .map(|s| interner::intern(s)) + .collect(); + let name_sym = interner::intern(name_str); + + // Try registry first (has inferred kinds from kind checker) + { + let registry = self.registry.read().await; + if let Some(mod_exports) = registry.lookup(&module_parts) { + if let Some(kind) = mod_exports.class_type_kinds.get(&name_sym) { + return Some(format!("{kind}")); + } + if let Some(kind) = mod_exports.type_kinds.get(&name_sym) { + return Some(format!("{kind}")); + } + } + } + + // Fall back to CST kind annotation + let target_uri = { + let mf = self.module_file_map.read().await; + mf.get(module_name).cloned() + }?; + let target_source = { + let sm = self.source_map.read().await; + sm.get(&target_uri).cloned() + }?; + let target_module = crate::parser::parse(&target_source).ok()?; + find_cst_kind(&target_module.decls, name_str, &target_source) + } +} + +/// Check if the offset falls on a declaration name (the definition site itself). +/// Returns (symbol, is_type_decl). +fn find_decl_name_at_offset(decls: &[Decl], offset: usize) -> Option<(interner::Symbol, bool)> { + for decl in decls { + let name_info = match decl { + Decl::Value { name, .. } => Some((name.value, name.span, false)), + Decl::TypeSignature { name, .. } => Some((name.value, name.span, false)), + Decl::Data { name, .. } => Some((name.value, name.span, true)), + Decl::TypeAlias { name, .. } => Some((name.value, name.span, true)), + Decl::Newtype { name, .. } => Some((name.value, name.span, true)), + Decl::Class { name, members, .. } => { + // Check class name + if offset >= name.span.start && offset < name.span.end { + return Some((name.value, true)); + } + // Check class member names + for member in members { + if offset >= member.name.span.start && offset < member.name.span.end { + return Some((member.name.value, false)); + } + } + None + } + Decl::Foreign { name, .. } => Some((name.value, name.span, false)), + Decl::ForeignData { name, .. } => Some((name.value, name.span, true)), + _ => None, + }; + if let Some((sym, span, is_type)) = name_info { + if offset >= span.start && offset < span.end { + return Some((sym, is_type)); + } + } + } + None +} + +/// Extract a type signature string from the CST for declarations not in CheckResult.types +/// (foreign imports, class methods, type signatures without corresponding values). +fn find_cst_type_signature(decls: &[Decl], symbol: interner::Symbol, source: &str) -> Option { + for decl in decls { + match decl { + Decl::Foreign { name, ty, .. } if name.value == symbol => { + let span = ty.span(); + return Some(source[span.start..span.end].to_string()); + } + Decl::TypeSignature { name, ty, .. } if name.value == symbol => { + let span = ty.span(); + return Some(source[span.start..span.end].to_string()); + } + Decl::Class { members, .. } => { + for member in members { + if member.name.value == symbol { + let span = member.ty.span(); + return Some(source[span.start..span.end].to_string()); + } + } + } + _ => {} + } + } + None +} + +/// Find doc-comments attached to a declaration with the given name. +fn find_doc_comments(decls: &[Decl], symbol: interner::Symbol) -> Vec { + for decl in decls { + // Check class members + if let Decl::Class { members, .. } = decl { + for member in members { + if member.name.value == symbol && !member.doc_comments.is_empty() { + return member.doc_comments.clone(); + } + } + } + + let decl_name = match decl { + Decl::Value { name, .. } + | Decl::TypeSignature { name, .. } + | Decl::Data { name, .. } + | Decl::TypeAlias { name, .. } + | Decl::Newtype { name, .. } + | Decl::Class { name, .. } + | Decl::Foreign { name, .. } + | Decl::ForeignData { name, .. } => Some(name.value), + _ => None, + }; + if decl_name == Some(symbol) { + let docs = decl.doc_comments(); + if !docs.is_empty() { + return docs.to_vec(); + } + } + } + Vec::new() +} + +/// Extract a kind annotation string from a source module's CST for a type/class/foreign-data declaration. +fn find_cst_kind(decls: &[Decl], name_str: &str, source: &str) -> Option { + let target_sym = interner::intern(name_str); + for decl in decls { + match decl { + Decl::ForeignData { name, kind, .. } if name.value == target_sym => { + let span = kind.span(); + return Some(source[span.start..span.end].to_string()); + } + _ => {} + } + } + // Default for classes and data types without explicit kind + Some("Type".to_string()) +} diff --git a/src/lsp/handlers/load_sources.rs b/src/lsp/handlers/load_sources.rs new file mode 100644 index 00000000..5803c1fc --- /dev/null +++ b/src/lsp/handlers/load_sources.rs @@ -0,0 +1,233 @@ +use std::collections::HashMap; +use std::sync::atomic::Ordering; + +use tower_lsp::lsp_types::*; + +use crate::build::BuildOptions; +use crate::lsp::utils::find_definition::DefinitionIndex; + +use super::super::Backend; + +impl Backend { + pub(crate) async fn load_sources(&self) { + let cmd = match &self.sources_cmd { + Some(cmd) => cmd.clone(), + None => { + self.ready.store(true, Ordering::SeqCst); + return; + } + }; + + // Create a progress token for the loading spinner + let token = NumberOrString::String("pfc-loading".to_string()); + let _ = self + .client + .send_request::(WorkDoneProgressCreateParams { + token: token.clone(), + }) + .await; + + self.client + .send_notification::(ProgressParams { + token: token.clone(), + value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin( + WorkDoneProgressBegin { + title: "Loading PureScript sources".to_string(), + message: Some(format!("Running: {cmd}")), + cancellable: Some(false), + percentage: None, + }, + )), + }) + .await; + + let client = self.client.clone(); + let registry = self.registry.clone(); + let def_index = self.def_index.clone(); + let resolution_exports = self.resolution_exports.clone(); + let module_file_map = self.module_file_map.clone(); + let source_map = self.source_map.clone(); + let ready = self.ready.clone(); + let progress_token = token.clone(); + + tokio::task::spawn_blocking(move || { + // Run the shell command to get source globs + let output = match std::process::Command::new("sh") + .arg("-c") + .arg(&cmd) + .output() + { + Ok(output) => output, + Err(e) => { + log::error!("Failed to run sources command: {e}"); + ready.store(true, Ordering::SeqCst); + return; + } + }; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + log::error!("Sources command failed: {stderr}"); + ready.store(true, Ordering::SeqCst); + return; + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let globs: Vec = stdout + .lines() + .filter(|l| !l.is_empty()) + .map(|l| l.to_string()) + .collect(); + + let rt = tokio::runtime::Handle::current(); + + // Report progress: resolving globs + rt.block_on(async { + client + .send_notification::(ProgressParams { + token: progress_token.clone(), + value: ProgressParamsValue::WorkDone(WorkDoneProgress::Report( + WorkDoneProgressReport { + message: Some(format!( + "Resolving {} glob patterns...", + globs.len() + )), + cancellable: Some(false), + percentage: None, + }, + )), + }) + .await; + }); + + // Resolve globs to file paths + let mut sources: Vec<(String, String)> = Vec::new(); + for pattern in &globs { + match glob::glob(pattern) { + Ok(entries) => { + for entry in entries.flatten() { + if entry.extension().map_or(false, |ext| ext == "purs") { + match std::fs::read_to_string(&entry) { + Ok(source) => { + let abs_path = entry + .canonicalize() + .unwrap_or_else(|_| entry.clone()); + sources.push(( + abs_path.to_string_lossy().into_owned(), + source, + )); + } + Err(e) => { + log::warn!("Failed to read {}: {e}", entry.display()) + } + } + } + } + } + Err(e) => log::warn!("Invalid glob pattern {pattern}: {e}"), + } + } + + // Report progress: building + rt.block_on(async { + client + .send_notification::(ProgressParams { + token: progress_token.clone(), + value: ProgressParamsValue::WorkDone(WorkDoneProgress::Report( + WorkDoneProgressReport { + message: Some(format!( + "Type-checking {} source files...", + sources.len() + )), + cancellable: Some(false), + percentage: None, + }, + )), + }) + .await; + }); + + // Build with no codegen to populate the registry + let source_refs: Vec<(&str, &str)> = sources + .iter() + .map(|(p, s)| (p.as_str(), s.as_str())) + .collect(); + + let options = BuildOptions { + output_dir: None, + ..Default::default() + }; + + let (result, new_registry) = crate::build::build_from_sources_with_options( + &source_refs, + &None, + None, + &options, + ); + + let error_count: usize = result.modules.iter().map(|m| m.type_errors.len()).sum(); + let module_count = result.modules.len(); + let error_module_count = result + .modules + .iter() + .filter(|m| !m.type_errors.is_empty()) + .count(); + + // Build definition index and resolution exports from parsed sources + let mut index = DefinitionIndex::new(); + let mut smap = HashMap::new(); + let mut mfmap = HashMap::new(); + let mut parsed_modules = Vec::new(); + for (path, source) in &sources { + if let Ok(module) = crate::parser::parse(source) { + index.add_module(&module, path); + let mod_name = format!("{}", module.name.value); + let file_uri = Url::from_file_path(path) + .map(|u| u.to_string()) + .unwrap_or_default(); + mfmap.insert(mod_name, file_uri.clone()); + parsed_modules.push(module); + smap.insert(file_uri, source.clone()); + } else { + smap.insert( + Url::from_file_path(path) + .map(|u| u.to_string()) + .unwrap_or_default(), + source.clone(), + ); + } + } + + let exports = crate::lsp::utils::resolve::ResolutionExports::new(&parsed_modules); + + // Store the registry, index, source map and mark as ready + rt.block_on(async { + let mut reg = registry.write().await; + *reg = new_registry; + let mut idx = def_index.write().await; + *idx = index; + let mut re = resolution_exports.write().await; + *re = exports; + let mut mf = module_file_map.write().await; + *mf = mfmap; + let mut sm = source_map.write().await; + *sm = smap; + ready.store(true, Ordering::SeqCst); + + // End progress + client + .send_notification::(ProgressParams { + token: progress_token, + value: ProgressParamsValue::WorkDone(WorkDoneProgress::End( + WorkDoneProgressEnd { + message: Some(format!( + "Loaded {module_count} modules ({error_count} errors in {error_module_count} modules)" + )), + }, + )), + }) + .await; + }); + }); + } +} diff --git a/src/lsp/handlers/mod.rs b/src/lsp/handlers/mod.rs new file mode 100644 index 00000000..cf604ba7 --- /dev/null +++ b/src/lsp/handlers/mod.rs @@ -0,0 +1,5 @@ +mod completion; +mod definition; +mod diagnostics; +mod hover; +mod load_sources; diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs new file mode 100644 index 00000000..a1faf57f --- /dev/null +++ b/src/lsp/mod.rs @@ -0,0 +1,128 @@ +mod handlers; +pub mod utils; + +use std::collections::HashMap; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; + +use tokio::sync::RwLock; +use tower_lsp::jsonrpc::Result; +use tower_lsp::lsp_types::*; +use tower_lsp::{Client, LanguageServer, LspService, Server}; + +use crate::typechecker::registry::ModuleRegistry; +use crate::lsp::utils::resolve::ResolutionExports; + +use utils::find_definition::DefinitionIndex; + +pub(crate) struct FileState { + pub source: String, + pub module_name: Option, +} + +pub struct Backend { + pub(crate) client: Client, + pub(crate) files: Arc>>, + pub(crate) registry: Arc>, + pub(crate) def_index: Arc>, + pub(crate) resolution_exports: Arc>, + /// Maps module name symbol → file URI for cross-module go-to-def + pub(crate) module_file_map: Arc>>, + /// Maps file URI → source content for loaded project files + pub(crate) source_map: Arc>>, + pub(crate) sources_cmd: Option, + pub(crate) ready: Arc, +} + +#[tower_lsp::async_trait] +impl LanguageServer for Backend { + async fn initialize(&self, _: InitializeParams) -> Result { + Ok(InitializeResult { + capabilities: ServerCapabilities { + text_document_sync: Some(TextDocumentSyncCapability::Kind( + TextDocumentSyncKind::FULL, + )), + definition_provider: Some(OneOf::Left(true)), + hover_provider: Some(HoverProviderCapability::Simple(true)), + completion_provider: Some(CompletionOptions { + trigger_characters: Some(vec![".".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + server_info: Some(ServerInfo { + name: "pfc".to_string(), + version: Some(env!("CARGO_PKG_VERSION").to_string()), + }), + }) + } + + async fn initialized(&self, _: InitializedParams) { + self.info("pfc language server initialized").await; + self.load_sources().await; + } + + async fn shutdown(&self) -> Result<()> { + Ok(()) + } + + async fn did_open(&self, params: DidOpenTextDocumentParams) { + self.on_change(params.text_document.uri, params.text_document.text) + .await; + } + + async fn did_change(&self, params: DidChangeTextDocumentParams) { + if let Some(change) = params.content_changes.into_iter().next() { + self.on_change(params.text_document.uri, change.text).await; + } + } + + async fn did_save(&self, params: DidSaveTextDocumentParams) { + if let Some(text) = params.text { + self.on_change(params.text_document.uri, text).await; + } + } + + async fn goto_definition( + &self, + params: GotoDefinitionParams, + ) -> Result> { + self.handle_goto_definition(params).await + } + + async fn hover(&self, params: HoverParams) -> Result> { + self.handle_hover(params).await + } + + async fn completion(&self, params: CompletionParams) -> Result> { + self.handle_completion(params).await + } +} + +impl Backend { + pub fn new(client: Client, sources_cmd: Option) -> Self { + Backend { + client, + files: Arc::new(RwLock::new(HashMap::new())), + registry: Arc::new(RwLock::new(ModuleRegistry::new())), + def_index: Arc::new(RwLock::new(DefinitionIndex::new())), + resolution_exports: Arc::new(RwLock::new(ResolutionExports::empty())), + module_file_map: Arc::new(RwLock::new(HashMap::new())), + source_map: Arc::new(RwLock::new(HashMap::new())), + sources_cmd, + ready: Arc::new(AtomicBool::new(false)), + } + } +} + +pub fn run_server(sources_cmd: Option) { + let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime"); + rt.block_on(async { + let stdin = tokio::io::stdin(); + let stdout = tokio::io::stdout(); + + let (service, socket) = LspService::new(|client| Backend::new(client, sources_cmd)); + + Server::new(stdin, stdout, socket).serve(service).await; + }); +} diff --git a/src/lsp/utils/find_definition.rs b/src/lsp/utils/find_definition.rs new file mode 100644 index 00000000..9c067ec1 --- /dev/null +++ b/src/lsp/utils/find_definition.rs @@ -0,0 +1,1472 @@ +use std::collections::HashMap; + +use crate::cst::*; +use crate::interner::Symbol; +use crate::span::Span; + +/// What kind of reference we found at the cursor +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RefKind { + Value, + Constructor, + Type, +} + +/// A reference found at the cursor position +#[derive(Debug, Clone)] +pub struct IdentAtCursor { + pub name: QualifiedIdent, + pub kind: RefKind, + pub span: Span, +} + +/// A definition location in a source file +#[derive(Debug, Clone)] +pub struct DefLocation { + pub file_path: String, + pub span: Span, +} + +/// Index of definitions across all loaded modules. +/// Keys are (module_name_string, unqualified_name_symbol). +#[derive(Debug, Clone, Default)] +pub struct DefinitionIndex { + pub values: HashMap<(String, Symbol), DefLocation>, + pub types: HashMap<(String, Symbol), DefLocation>, + pub constructors: HashMap<(String, Symbol), DefLocation>, +} + +impl DefinitionIndex { + pub fn new() -> Self { + Self::default() + } + + /// Collect definitions from a parsed module and add them to the index. + pub fn add_module(&mut self, module: &Module, file_path: &str) { + let module_name = format!("{}", module.name.value); + + for decl in &module.decls { + match decl { + Decl::TypeSignature { name, .. } => { + // Type signatures take precedence — insert first + self.values.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + } + Decl::Value { name, .. } => { + // Don't overwrite TypeSignature entry + self.values.entry((module_name.clone(), name.value)) + .or_insert(DefLocation { + file_path: file_path.to_string(), + span: name.span, + }); + } + Decl::Data { + name, + constructors, + kind_sig, + is_role_decl, + .. + } => { + if *kind_sig == KindSigSource::None && !is_role_decl { + self.types.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + for ctor in constructors { + self.constructors.insert( + (module_name.clone(), ctor.name.value), + DefLocation { + file_path: file_path.to_string(), + span: ctor.name.span, + }, + ); + } + } + } + Decl::TypeAlias { name, .. } => { + self.types.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + } + Decl::Newtype { + name, constructor, .. + } => { + self.types.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + self.constructors.insert( + (module_name.clone(), constructor.value), + DefLocation { + file_path: file_path.to_string(), + span: constructor.span, + }, + ); + } + Decl::Class { name, members, .. } => { + self.types.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + for member in members { + self.values.insert( + (module_name.clone(), member.name.value), + DefLocation { + file_path: file_path.to_string(), + span: member.name.span, + }, + ); + } + } + Decl::Foreign { name, .. } => { + self.values.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + } + Decl::ForeignData { name, .. } => { + self.types.insert( + (module_name.clone(), name.value), + DefLocation { + file_path: file_path.to_string(), + span: name.span, + }, + ); + } + Decl::Fixity { operator, target, is_type, .. } => { + // For operators, try to resolve to the target function's definition + let target_key = (module_name.clone(), target.name); + let target_span = if *is_type { + self.types.get(&target_key).map(|l| l.span) + } else { + self.values.get(&target_key).map(|l| l.span) + }; + let span = target_span.unwrap_or(operator.span); + let map = if *is_type { &mut self.types } else { &mut self.values }; + map.insert( + (module_name.clone(), operator.value), + DefLocation { + file_path: file_path.to_string(), + span, + }, + ); + } + _ => {} + } + } + } + + /// Look up a definition for a reference found at the cursor. + /// `current_module` is the module name of the file being edited. + /// `imports` maps qualified/unqualified names to their source module. + pub fn find( + &self, + ident: &IdentAtCursor, + current_module: &str, + import_map: &ImportMap, + ) -> Option<&DefLocation> { + let name = ident.name.name; + + // If qualified (e.g. Data.Maybe.Just), resolve the qualifier + if let Some(qualifier) = ident.name.module { + // Look up what module this qualifier maps to + if let Some(real_module) = import_map.qualifier_to_module.get(&qualifier) { + let key = (real_module.clone(), name); + return match ident.kind { + RefKind::Value => self.values.get(&key), + RefKind::Constructor => self.constructors.get(&key), + RefKind::Type => self.types.get(&key), + }; + } + } + + // Try current module first + let local_key = (current_module.to_string(), name); + let result = match ident.kind { + RefKind::Value => self.values.get(&local_key), + RefKind::Constructor => self.constructors.get(&local_key), + RefKind::Type => self.types.get(&local_key), + }; + if result.is_some() { + return result; + } + + // Try imported modules + let candidates = match ident.kind { + RefKind::Value => import_map.value_modules.get(&name), + RefKind::Constructor => import_map.ctor_modules.get(&name), + RefKind::Type => import_map.type_modules.get(&name), + }; + if let Some(module_name) = candidates { + let key = (module_name.clone(), name); + return match ident.kind { + RefKind::Value => self.values.get(&key), + RefKind::Constructor => self.constructors.get(&key), + RefKind::Type => self.types.get(&key), + }; + } + + None + } + + /// Search all modules for a symbol definition. Used as fallback when the + /// import source module doesn't directly define the symbol (re-exports). + pub fn find_reexport( + &self, + symbol: Symbol, + namespace: crate::lsp::utils::resolve::Namespace, + ) -> Option<(&String, &DefLocation)> { + use crate::lsp::utils::resolve::Namespace; + match namespace { + Namespace::Value => self + .values + .iter() + .find(|((_, s), _)| *s == symbol) + .map(|((m, _), loc)| (m, loc)) + .or_else(|| { + self.constructors + .iter() + .find(|((_, s), _)| *s == symbol) + .map(|((m, _), loc)| (m, loc)) + }), + Namespace::Type | Namespace::Class | Namespace::TypeOperator => self + .types + .iter() + .find(|((_, s), _)| *s == symbol) + .map(|((m, _), loc)| (m, loc)) + .or_else(|| { + self.constructors + .iter() + .find(|((_, s), _)| *s == symbol) + .map(|((m, _), loc)| (m, loc)) + }), + } + } +} + +/// Maps imported names to their source modules. +#[derive(Debug, Clone, Default)] +pub struct ImportMap { + /// qualifier symbol → full module name string (e.g. intern("M") → "Data.Maybe") + pub qualifier_to_module: HashMap, + /// unqualified value name → source module name + pub value_modules: HashMap, + /// unqualified constructor name → source module name + pub ctor_modules: HashMap, + /// unqualified type name → source module name + pub type_modules: HashMap, +} + +impl ImportMap { + /// Build an import map from a module's import declarations and the definition index. + pub fn from_imports(imports: &[ImportDecl], index: &DefinitionIndex) -> Self { + let mut map = ImportMap::default(); + + for import in imports { + let module_name = format!("{}", import.module); + + // Record qualifier mapping + if let Some(ref qual) = import.qualified { + // `import Data.Maybe as M` → qualifier is M's first part + if let Some(q) = qual.parts.first() { + map.qualifier_to_module.insert(*q, module_name.clone()); + } + } + + // For unqualified imports, record which names come from which module. + // If it's `import X as Y` (qualified-only), skip unqualified registration. + let is_qualified_only = import.qualified.is_some(); + if is_qualified_only { + // Still need to register for qualified lookups, but not unqualified + // unless there's an explicit import list + if import.imports.is_none() { + continue; + } + } + + match &import.imports { + None => { + // `import Data.Maybe` — all exports are available unqualified + for ((mod_name, sym), _) in &index.values { + if mod_name == &module_name { + map.value_modules.insert(*sym, module_name.clone()); + } + } + for ((mod_name, sym), _) in &index.types { + if mod_name == &module_name { + map.type_modules.insert(*sym, module_name.clone()); + } + } + for ((mod_name, sym), _) in &index.constructors { + if mod_name == &module_name { + map.ctor_modules.insert(*sym, module_name.clone()); + } + } + } + Some(ImportList::Explicit(items)) => { + for item in items { + register_import_item(&mut map, item, &module_name, index); + } + } + Some(ImportList::Hiding(items)) => { + // Import everything except the hidden items + let mut hidden_values = std::collections::HashSet::new(); + let mut hidden_types = std::collections::HashSet::new(); + let hidden_ctors: std::collections::HashSet = std::collections::HashSet::new(); + for item in items { + match item { + Import::Value(name) => { hidden_values.insert(name.value); } + Import::Type(name, _) => { + hidden_types.insert(name.value); + } + Import::Class(name) => { hidden_types.insert(name.value); } + Import::TypeOp(name) => { hidden_types.insert(name.value); } + } + } + for ((mod_name, sym), _) in &index.values { + if mod_name == &module_name && !hidden_values.contains(sym) { + map.value_modules.insert(*sym, module_name.clone()); + } + } + for ((mod_name, sym), _) in &index.types { + if mod_name == &module_name && !hidden_types.contains(sym) { + map.type_modules.insert(*sym, module_name.clone()); + } + } + for ((mod_name, sym), _) in &index.constructors { + if mod_name == &module_name && !hidden_ctors.contains(sym) { + map.ctor_modules.insert(*sym, module_name.clone()); + } + } + } + } + } + + map + } +} + +fn register_import_item( + map: &mut ImportMap, + item: &Import, + module_name: &str, + index: &DefinitionIndex, +) { + match item { + Import::Value(name) => { + map.value_modules + .insert(name.value, module_name.to_string()); + } + Import::Type(name, data_members) => { + map.type_modules + .insert(name.value, module_name.to_string()); + if let Some(DataMembers::All) = data_members { + // Import all constructors for this type from this module + for ((mod_name, sym), _) in &index.constructors { + if mod_name == module_name { + map.ctor_modules.insert(*sym, module_name.to_string()); + } + } + } + } + Import::Class(name) => { + map.type_modules + .insert(name.value, module_name.to_string()); + } + Import::TypeOp(name) => { + map.type_modules + .insert(name.value, module_name.to_string()); + } + } +} + +/// Find the identifier at the given byte offset in a module's CST. +pub fn find_ident_at_offset(module: &Module, offset: usize) -> Option { + let mut best: Option = None; + + for decl in &module.decls { + if let Some(found) = find_in_decl(decl, offset) { + if best + .as_ref() + .map_or(true, |b| span_len(found.span) < span_len(b.span)) + { + best = Some(found); + } + } + } + + best +} + +fn span_len(s: Span) -> usize { + s.end.saturating_sub(s.start) +} + +fn contains(span: Span, offset: usize) -> bool { + if span.end == 0 || span.end <= span.start { + // Span has no valid end — treat as containing everything from start onward + offset >= span.start + } else { + offset >= span.start && offset < span.end + } +} + +fn find_in_decl(decl: &Decl, offset: usize) -> Option { + if !contains(decl.span(), offset) { + return None; + } + + match decl { + Decl::Value { + guarded, + binders, + where_clause, + .. + } => { + for b in binders { + if let Some(r) = find_in_binder(b, offset) { + return Some(r); + } + } + if let Some(r) = find_in_guarded(guarded, offset) { + return Some(r); + } + for lb in where_clause { + if let Some(r) = find_in_let_binding(lb, offset) { + return Some(r); + } + } + None + } + Decl::TypeSignature { ty, .. } => find_in_type_expr(ty, offset), + Decl::Data { constructors, .. } => { + for ctor in constructors { + for field in &ctor.fields { + if let Some(r) = find_in_type_expr(field, offset) { + return Some(r); + } + } + } + None + } + Decl::TypeAlias { ty, .. } => find_in_type_expr(ty, offset), + Decl::Newtype { ty, .. } => find_in_type_expr(ty, offset), + Decl::Class { members, .. } => { + for m in members { + if let Some(r) = find_in_type_expr(&m.ty, offset) { + return Some(r); + } + } + None + } + Decl::Instance { members, types, .. } => { + for ty in types { + if let Some(r) = find_in_type_expr(ty, offset) { + return Some(r); + } + } + for d in members { + if let Some(r) = find_in_decl(d, offset) { + return Some(r); + } + } + None + } + Decl::Foreign { ty, .. } => find_in_type_expr(ty, offset), + Decl::ForeignData { .. } => None, + Decl::Fixity { .. } => None, + Decl::Derive { types, .. } => { + for ty in types { + if let Some(r) = find_in_type_expr(ty, offset) { + return Some(r); + } + } + None + } + } +} + +fn find_in_expr(expr: &Expr, offset: usize) -> Option { + if !contains(expr.span(), offset) { + return None; + } + + match expr { + Expr::Var { span, name, .. } => { + if contains(*span, offset) { + Some(IdentAtCursor { + name: *name, + kind: RefKind::Value, + span: *span, + }) + } else { + None + } + } + Expr::Constructor { span, name, .. } => { + if contains(*span, offset) { + Some(IdentAtCursor { + name: *name, + kind: RefKind::Constructor, + span: *span, + }) + } else { + None + } + } + Expr::Op { left, op, right, .. } => { + if let Some(r) = find_in_expr(left, offset) { + return Some(r); + } + if contains(op.span, offset) { + return Some(IdentAtCursor { + name: op.value, + kind: RefKind::Value, + span: op.span, + }); + } + find_in_expr(right, offset) + } + Expr::OpParens { op, .. } => { + if contains(op.span, offset) { + Some(IdentAtCursor { + name: op.value, + kind: RefKind::Value, + span: op.span, + }) + } else { + None + } + } + Expr::App { func, arg, .. } => { + find_in_expr(func, offset).or_else(|| find_in_expr(arg, offset)) + } + Expr::VisibleTypeApp { func, ty, .. } => { + find_in_expr(func, offset).or_else(|| find_in_type_expr(ty, offset)) + } + Expr::Lambda { + binders, body, .. + } => { + for b in binders { + if let Some(r) = find_in_binder(b, offset) { + return Some(r); + } + } + find_in_expr(body, offset) + } + Expr::If { + cond, + then_expr, + else_expr, + .. + } => find_in_expr(cond, offset) + .or_else(|| find_in_expr(then_expr, offset)) + .or_else(|| find_in_expr(else_expr, offset)), + Expr::Case { exprs, alts, .. } => { + for e in exprs { + if let Some(r) = find_in_expr(e, offset) { + return Some(r); + } + } + for alt in alts { + for b in &alt.binders { + if let Some(r) = find_in_binder(b, offset) { + return Some(r); + } + } + if let Some(r) = find_in_guarded(&alt.result, offset) { + return Some(r); + } + } + None + } + Expr::Let { + bindings, body, .. + } => { + for lb in bindings { + if let Some(r) = find_in_let_binding(lb, offset) { + return Some(r); + } + } + find_in_expr(body, offset) + } + Expr::Do { statements, .. } | Expr::Ado { statements, .. } => { + for stmt in statements { + if let Some(r) = find_in_do_statement(stmt, offset) { + return Some(r); + } + } + if let Expr::Ado { result, .. } = expr { + return find_in_expr(result, offset); + } + None + } + Expr::Record { fields, .. } => { + for f in fields { + if let Some(ref val) = f.value { + if let Some(r) = find_in_expr(val, offset) { + return Some(r); + } + } + } + None + } + Expr::RecordAccess { expr: inner, .. } => find_in_expr(inner, offset), + Expr::RecordUpdate { + expr: inner, + updates, + .. + } => { + if let Some(r) = find_in_expr(inner, offset) { + return Some(r); + } + for u in updates { + if let Some(r) = find_in_expr(&u.value, offset) { + return Some(r); + } + } + None + } + Expr::Parens { expr: inner, .. } => find_in_expr(inner, offset), + Expr::TypeAnnotation { + expr: inner, ty, .. + } => find_in_expr(inner, offset).or_else(|| find_in_type_expr(ty, offset)), + Expr::Literal { lit, .. } => find_in_literal(lit, offset), + Expr::Array { elements, .. } => { + for e in elements { + if let Some(r) = find_in_expr(e, offset) { + return Some(r); + } + } + None + } + Expr::Negate { expr: inner, .. } => find_in_expr(inner, offset), + Expr::AsPattern { name, pattern, .. } => { + find_in_expr(name, offset).or_else(|| find_in_expr(pattern, offset)) + } + Expr::BacktickApp { + func, left, right, .. + } => find_in_expr(left, offset) + .or_else(|| find_in_expr(func, offset)) + .or_else(|| find_in_expr(right, offset)), + Expr::Wildcard { .. } | Expr::Hole { .. } => None, + } +} + +fn find_in_literal(lit: &Literal, offset: usize) -> Option { + match lit { + Literal::Array(exprs) => { + for e in exprs { + if let Some(r) = find_in_expr(e, offset) { + return Some(r); + } + } + None + } + _ => None, + } +} + +fn find_in_type_expr(ty: &TypeExpr, offset: usize) -> Option { + match ty { + TypeExpr::Constructor { span, name } => { + if contains(*span, offset) { + Some(IdentAtCursor { + name: *name, + kind: RefKind::Type, + span: *span, + }) + } else { + None + } + } + TypeExpr::App { + constructor, arg, .. + } => find_in_type_expr(constructor, offset).or_else(|| find_in_type_expr(arg, offset)), + TypeExpr::Function { from, to, .. } => { + find_in_type_expr(from, offset).or_else(|| find_in_type_expr(to, offset)) + } + TypeExpr::Forall { vars, ty: inner, .. } => { + for (_, _, kind) in vars { + if let Some(k) = kind { + if let Some(r) = find_in_type_expr(k, offset) { + return Some(r); + } + } + } + find_in_type_expr(inner, offset) + } + TypeExpr::Constrained { + constraints, + ty: inner, + .. + } => { + for c in constraints { + if contains(c.span, offset) { + if let Some(r) = find_in_constraint(c, offset) { + return Some(r); + } + } + } + find_in_type_expr(inner, offset) + } + TypeExpr::Record { fields, .. } => { + for f in fields { + if let Some(r) = find_in_type_expr(&f.ty, offset) { + return Some(r); + } + } + None + } + TypeExpr::Row { fields, tail, .. } => { + for f in fields { + if let Some(r) = find_in_type_expr(&f.ty, offset) { + return Some(r); + } + } + if let Some(t) = tail { + return find_in_type_expr(t, offset); + } + None + } + TypeExpr::Parens { ty: inner, .. } => find_in_type_expr(inner, offset), + TypeExpr::TypeOp { + left, op, right, .. + } => { + if let Some(r) = find_in_type_expr(left, offset) { + return Some(r); + } + if contains(op.span, offset) { + return Some(IdentAtCursor { + name: op.value, + kind: RefKind::Type, + span: op.span, + }); + } + find_in_type_expr(right, offset) + } + TypeExpr::Kinded { ty: inner, kind, .. } => { + find_in_type_expr(inner, offset).or_else(|| find_in_type_expr(kind, offset)) + } + TypeExpr::Var { .. } + | TypeExpr::Hole { .. } + | TypeExpr::Wildcard { .. } + | TypeExpr::StringLiteral { .. } + | TypeExpr::IntLiteral { .. } + | TypeExpr::ArrayPattern { .. } + | TypeExpr::AsPattern { .. } => None, + } +} + +fn find_in_constraint(c: &Constraint, offset: usize) -> Option { + // The class name in the constraint + // We don't have an exact span for just the class name in a Constraint, + // so use the constraint's span as approximation + for arg in &c.args { + if let Some(r) = find_in_type_expr(arg, offset) { + return Some(r); + } + } + // If not in args, it's probably on the class name + Some(IdentAtCursor { + name: c.class, + kind: RefKind::Type, + span: c.span, + }) +} + +fn find_in_binder(binder: &Binder, offset: usize) -> Option { + match binder { + Binder::Constructor { + span, name, args, .. + } => { + if contains(*span, offset) { + // Check args first (tighter match) + for a in args { + if let Some(r) = find_in_binder(a, offset) { + return Some(r); + } + } + Some(IdentAtCursor { + name: *name, + kind: RefKind::Constructor, + span: *span, + }) + } else { + None + } + } + Binder::Record { fields, .. } => { + for f in fields { + if let Some(ref b) = f.binder { + if let Some(r) = find_in_binder(b, offset) { + return Some(r); + } + } + } + None + } + Binder::As { binder: inner, .. } => find_in_binder(inner, offset), + Binder::Parens { binder: inner, .. } => find_in_binder(inner, offset), + Binder::Array { elements, .. } => { + for e in elements { + if let Some(r) = find_in_binder(e, offset) { + return Some(r); + } + } + None + } + Binder::Op { + left, op, right, .. + } => { + if let Some(r) = find_in_binder(left, offset) { + return Some(r); + } + if contains(op.span, offset) { + return Some(IdentAtCursor { + name: op.value, + kind: RefKind::Value, + span: op.span, + }); + } + find_in_binder(right, offset) + } + Binder::Typed { + binder: inner, + ty, + .. + } => find_in_binder(inner, offset).or_else(|| find_in_type_expr(ty, offset)), + Binder::Wildcard { .. } | Binder::Var { .. } | Binder::Literal { .. } => None, + } +} + +fn find_in_guarded(guarded: &GuardedExpr, offset: usize) -> Option { + match guarded { + GuardedExpr::Unconditional(expr) => find_in_expr(expr, offset), + GuardedExpr::Guarded(guards) => { + for g in guards { + for pat in &g.patterns { + match pat { + GuardPattern::Boolean(e) => { + if let Some(r) = find_in_expr(e, offset) { + return Some(r); + } + } + GuardPattern::Pattern(b, e) => { + if let Some(r) = find_in_binder(b, offset) { + return Some(r); + } + if let Some(r) = find_in_expr(e, offset) { + return Some(r); + } + } + } + } + if let Some(r) = find_in_expr(&g.expr, offset) { + return Some(r); + } + } + None + } + } +} + +fn find_in_let_binding(lb: &LetBinding, offset: usize) -> Option { + match lb { + LetBinding::Value { binder, expr, .. } => { + find_in_binder(binder, offset).or_else(|| find_in_expr(expr, offset)) + } + LetBinding::Signature { ty, .. } => find_in_type_expr(ty, offset), + } +} + +fn find_in_do_statement(stmt: &DoStatement, offset: usize) -> Option { + match stmt { + DoStatement::Bind { binder, expr, .. } => { + find_in_binder(binder, offset).or_else(|| find_in_expr(expr, offset)) + } + DoStatement::Let { bindings, .. } => { + for lb in bindings { + if let Some(r) = find_in_let_binding(lb, offset) { + return Some(r); + } + } + None + } + DoStatement::Discard { expr, .. } => find_in_expr(expr, offset), + } +} + +/// Convert an LSP Position (0-indexed line/col) to a byte offset in source. +pub fn position_to_offset(source: &str, line: u32, character: u32) -> Option { + let mut current_line = 0u32; + let mut current_col = 0u32; + + for (i, c) in source.char_indices() { + if current_line == line && current_col == character { + return Some(i); + } + if c == '\n' { + if current_line == line { + // Past end of this line + return Some(i); + } + current_line += 1; + current_col = 0; + } else { + current_col += 1; + } + } + + // Handle position at end of file + if current_line == line && current_col == character { + return Some(source.len()); + } + + None +} + +/// Find definition of a local variable within the same module. +/// Returns the span of the binding site if found. +pub fn find_local_definition(module: &Module, name: Symbol) -> Option { + for decl in &module.decls { + if let Some(span) = find_name_in_decl(decl, name) { + return Some(span); + } + } + None +} + +fn find_name_in_decl(decl: &Decl, name: Symbol) -> Option { + match decl { + Decl::Value { + name: decl_name, .. + } => { + if decl_name.value == name { + Some(decl_name.span) + } else { + None + } + } + Decl::Data { + name: decl_name, + constructors, + .. + } => { + if decl_name.value == name { + return Some(decl_name.span); + } + for ctor in constructors { + if ctor.name.value == name { + return Some(ctor.name.span); + } + } + None + } + Decl::TypeAlias { + name: decl_name, .. + } => { + if decl_name.value == name { + Some(decl_name.span) + } else { + None + } + } + Decl::Newtype { + name: decl_name, + constructor, + .. + } => { + if decl_name.value == name { + return Some(decl_name.span); + } + if constructor.value == name { + return Some(constructor.span); + } + None + } + Decl::Class { + name: decl_name, + members, + .. + } => { + if decl_name.value == name { + return Some(decl_name.span); + } + for m in members { + if m.name.value == name { + return Some(m.name.span); + } + } + None + } + Decl::Foreign { + name: decl_name, .. + } => { + if decl_name.value == name { + Some(decl_name.span) + } else { + None + } + } + Decl::ForeignData { + name: decl_name, .. + } => { + if decl_name.value == name { + Some(decl_name.span) + } else { + None + } + } + Decl::Instance { members, .. } => { + for d in members { + if let Some(span) = find_name_in_decl(d, name) { + return Some(span); + } + } + None + } + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::interner::intern; + + #[test] + fn test_position_to_offset() { + let src = "hello\nworld\nfoo"; + assert_eq!(position_to_offset(src, 0, 0), Some(0)); + assert_eq!(position_to_offset(src, 0, 3), Some(3)); + assert_eq!(position_to_offset(src, 1, 0), Some(6)); + assert_eq!(position_to_offset(src, 1, 5), Some(11)); + assert_eq!(position_to_offset(src, 2, 0), Some(12)); + assert_eq!(position_to_offset(src, 2, 3), Some(15)); + } + + #[test] + fn test_find_ident_at_offset_var() { + let src = "module Test where\n\nfoo = bar"; + let module = crate::parser::parse(src).expect("parse"); + let bar_offset = src.find("bar").unwrap(); + let result = find_ident_at_offset(&module, bar_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "bar" + ); + } + + #[test] + fn test_find_ident_at_offset_constructor() { + let src = "module Test where\n\nfoo = Just 1"; + let module = crate::parser::parse(src).expect("parse"); + // "Just" starts somewhere around offset 24 + let just_offset = src.find("Just").unwrap(); + let result = find_ident_at_offset(&module, just_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Constructor); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "Just" + ); + } + + #[test] + fn test_find_ident_at_offset_type() { + let src = "module Test where\n\nfoo :: Int -> String\nfoo x = \"\""; + let module = crate::parser::parse(src).expect("parse"); + let int_offset = src.find("Int").unwrap(); + let result = find_ident_at_offset(&module, int_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Type); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "Int" + ); + } + + #[test] + fn test_find_local_definition() { + let src = "module Test where\n\nfoo = 1\n\nbar = foo"; + let module = crate::parser::parse(src).expect("parse"); + let foo_sym = intern("foo"); + let result = find_local_definition(&module, foo_sym); + assert!(result.is_some()); + } + + #[test] + fn test_definition_index_collect() { + let src = "module Test where\n\ndata Color = Red | Green | Blue\n\nfoo = 1"; + let module = crate::parser::parse(src).expect("parse"); + let mut index = DefinitionIndex::new(); + index.add_module(&module, "test.purs"); + + let foo_sym = intern("foo"); + let color_sym = intern("Color"); + let red_sym = intern("Red"); + + assert!(index.values.contains_key(&("Test".to_string(), foo_sym))); + assert!(index.types.contains_key(&("Test".to_string(), color_sym))); + assert!(index + .constructors + .contains_key(&("Test".to_string(), red_sym))); + } + + #[test] + fn test_find_ident_in_case_expr() { + let src = "module Test where\n\nfoo x = case x of\n Just y -> y\n Nothing -> 0"; + let module = crate::parser::parse(src).expect("parse"); + let nothing_offset = src.find("Nothing").unwrap(); + let result = find_ident_at_offset(&module, nothing_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Constructor); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "Nothing" + ); + } + + /// Helper: simulate the goto-definition flow for a cursor position in source. + /// Returns the resolved DefLocation if found. + fn goto_def_in_source( + src: &str, + file_path: &str, + line: u32, + character: u32, + index: &DefinitionIndex, + ) -> Option { + let offset = position_to_offset(src, line, character)?; + let module = crate::parser::parse(src).ok()?; + let ident = find_ident_at_offset(&module, offset)?; + let current_module = format!("{}", module.name.value); + + // Try local first + if ident.name.module.is_none() { + if let Some(span) = find_local_definition(&module, ident.name.name) { + return Some(DefLocation { + file_path: file_path.to_string(), + span, + }); + } + } + + // Try cross-module + let import_map = ImportMap::from_imports(&module.imports, index); + index.find(&ident, ¤t_module, &import_map).cloned() + } + + #[test] + fn test_goto_def_local_value() { + let src = "module Test where\n\nfoo = 1\n\nbar = foo"; + // Click on "foo" in "bar = foo" (line 4, col 6) + let foo_in_bar = src.rfind("foo").unwrap(); + let line = src[..foo_in_bar].matches('\n').count() as u32; + let col = (foo_in_bar - src[..foo_in_bar].rfind('\n').unwrap() - 1) as u32; + + let index = DefinitionIndex::new(); + let result = goto_def_in_source(src, "test.purs", line, col, &index); + assert!(result.is_some(), "should find local definition of foo"); + let loc = result.unwrap(); + assert_eq!(loc.file_path, "test.purs"); + // The span should point to the definition of foo (line 3, "foo = 1") + let (start, _) = loc.span.to_pos(src).unwrap(); + assert_eq!(start.line, 3); // 1-indexed: module=1, blank=2, foo=3 + } + + #[test] + fn test_goto_def_local_data_constructor() { + let src = "module Test where\n\ndata Color = Red | Green | Blue\n\nfoo = Red"; + // Click on "Red" in "foo = Red" + let red_in_foo = src.rfind("Red").unwrap(); + let line = src[..red_in_foo].matches('\n').count() as u32; + let col = (red_in_foo - src[..red_in_foo].rfind('\n').unwrap() - 1) as u32; + + let index = DefinitionIndex::new(); + let result = goto_def_in_source(src, "test.purs", line, col, &index); + assert!(result.is_some(), "should find local constructor Red"); + let loc = result.unwrap(); + let (start, _) = loc.span.to_pos(src).unwrap(); + assert_eq!(start.line, 3); // data Color = Red is on line 3 + } + + #[test] + fn test_goto_def_local_type_in_signature() { + let src = "module Test where\n\ndata Foo = MkFoo\n\nbar :: Foo\nbar = MkFoo"; + // Click on "Foo" in the type signature "bar :: Foo" + let foo_in_sig = src.find("bar :: Foo").unwrap() + "bar :: ".len(); + let line = src[..foo_in_sig].matches('\n').count() as u32; + let col = (foo_in_sig - src[..foo_in_sig].rfind('\n').unwrap() - 1) as u32; + + let index = DefinitionIndex::new(); + let result = goto_def_in_source(src, "test.purs", line, col, &index); + assert!(result.is_some(), "should find local type Foo"); + let loc = result.unwrap(); + let (start, _) = loc.span.to_pos(src).unwrap(); + assert_eq!(start.line, 3); // data Foo on line 3 + } + + #[test] + fn test_goto_def_cross_module_value() { + // Module A defines "helper" + let src_a = "module ModA where\n\nhelper = 1"; + // Module B imports and uses it + let src_b = "module ModB where\n\nimport ModA\n\nfoo = helper"; + + let mut index = DefinitionIndex::new(); + let mod_a = crate::parser::parse(src_a).unwrap(); + index.add_module(&mod_a, "/src/ModA.purs"); + + // Click on "helper" in ModB + let helper_offset = src_b.find("helper").unwrap(); + let line = src_b[..helper_offset].matches('\n').count() as u32; + let col = (helper_offset - src_b[..helper_offset].rfind('\n').unwrap() - 1) as u32; + + let result = goto_def_in_source(src_b, "/src/ModB.purs", line, col, &index); + assert!(result.is_some(), "should find cross-module definition of helper"); + let loc = result.unwrap(); + assert_eq!(loc.file_path, "/src/ModA.purs"); + let (start, _) = loc.span.to_pos(src_a).unwrap(); + assert_eq!(start.line, 3); + } + + #[test] + fn test_goto_def_cross_module_type() { + let src_a = "module ModA where\n\ndata Widget = W"; + let src_b = "module ModB where\n\nimport ModA (Widget(..))\n\nfoo :: Widget\nfoo = W"; + + let mut index = DefinitionIndex::new(); + let mod_a = crate::parser::parse(src_a).unwrap(); + index.add_module(&mod_a, "/src/ModA.purs"); + + // Click on "Widget" in the type signature + let widget_offset = src_b.find("foo :: Widget").unwrap() + "foo :: ".len(); + let line = src_b[..widget_offset].matches('\n').count() as u32; + let col = (widget_offset - src_b[..widget_offset].rfind('\n').unwrap() - 1) as u32; + + let result = goto_def_in_source(src_b, "/src/ModB.purs", line, col, &index); + assert!(result.is_some(), "should find cross-module type Widget"); + let loc = result.unwrap(); + assert_eq!(loc.file_path, "/src/ModA.purs"); + } + + #[test] + fn test_goto_def_cross_module_constructor() { + let src_a = "module ModA where\n\ndata Maybe a = Nothing | Just a"; + let src_b = "module ModB where\n\nimport ModA (Maybe(..))\n\nfoo = Just 1"; + + let mut index = DefinitionIndex::new(); + let mod_a = crate::parser::parse(src_a).unwrap(); + index.add_module(&mod_a, "/src/ModA.purs"); + + // Click on "Just" in "foo = Just 1" + let just_offset = src_b.find("Just").unwrap(); + let line = src_b[..just_offset].matches('\n').count() as u32; + let col = (just_offset - src_b[..just_offset].rfind('\n').unwrap() - 1) as u32; + + let result = goto_def_in_source(src_b, "/src/ModB.purs", line, col, &index); + assert!(result.is_some(), "should find cross-module constructor Just"); + let loc = result.unwrap(); + assert_eq!(loc.file_path, "/src/ModA.purs"); + } + + #[test] + fn test_goto_def_no_result_for_unknown() { + let src = "module Test where\n\nfoo = unknownThing"; + let offset = src.find("unknownThing").unwrap(); + let line = src[..offset].matches('\n').count() as u32; + let col = (offset - src[..offset].rfind('\n').unwrap() - 1) as u32; + + let index = DefinitionIndex::new(); + let result = goto_def_in_source(src, "test.purs", line, col, &index); + // unknownThing is not defined anywhere — should return None + assert!(result.is_none()); + } + + #[test] + fn test_goto_def_not_ready_returns_none_for_whitespace() { + let src = "module Test where\n\nfoo = 1"; + // Click on whitespace (line 1, col 0 = blank line) + let index = DefinitionIndex::new(); + let result = goto_def_in_source(src, "test.purs", 1, 0, &index); + assert!(result.is_none()); + } + + #[test] + fn test_find_ident_in_where_clause() { + let src = "module Test where\n\nfoo = bar\n where\n bar = 1"; + let module = crate::parser::parse(src).expect("parse"); + let bar_offset = src.find("foo = bar").unwrap() + "foo = ".len(); + let result = find_ident_at_offset(&module, bar_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "bar" + ); + assert_eq!(ident.span.start, bar_offset); + assert_eq!(ident.span.end, bar_offset + "bar".len()); + } + + #[test] + fn test_find_ident_in_where_clause_definition() { + // Click on the reference inside the where binding's RHS + let src = "module Test where\n\nbaz = 1\n\nfoo = bar\n where\n bar = baz"; + let module = crate::parser::parse(src).expect("parse"); + let baz_offset = src.rfind("baz").unwrap(); + let result = find_ident_at_offset(&module, baz_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "baz" + ); + assert_eq!(ident.span.start, baz_offset); + assert_eq!(ident.span.end, baz_offset + "baz".len()); + } + + #[test] + fn test_find_ident_in_let_expr() { + let src = "module Test where\n\nfoo = let x = 1 in x"; + let module = crate::parser::parse(src).expect("parse"); + // Click on the "x" after "in" + let x_offset = src.rfind("x").unwrap(); + let result = find_ident_at_offset(&module, x_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "x" + ); + assert_eq!(ident.span.start, x_offset); + assert_eq!(ident.span.end, x_offset + "x".len()); + } + + #[test] + fn test_find_ident_in_do_bind_rhs() { + let src = "module Test where\n\nfoo = do\n x <- bar\n pure x"; + let module = crate::parser::parse(src).expect("parse"); + // Click on "bar" (RHS of bind arrow) + let bar_offset = src.find("bar").unwrap(); + let result = find_ident_at_offset(&module, bar_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "bar" + ); + assert_eq!(ident.span.start, bar_offset); + assert_eq!(ident.span.end, bar_offset + "bar".len()); + } + + #[test] + fn test_find_ident_in_do_discard() { + let src = "module Test where\n\nfoo = do\n bar\n baz"; + let module = crate::parser::parse(src).expect("parse"); + let baz_offset = src.find("baz").unwrap(); + let result = find_ident_at_offset(&module, baz_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "baz" + ); + assert_eq!(ident.span.start, baz_offset); + assert_eq!(ident.span.end, baz_offset + "baz".len()); + } + + #[test] + fn test_find_ident_value_operator() { + let src = "module Test where\n\nfoo = 1 + 2"; + let module = crate::parser::parse(src).expect("parse"); + let plus_offset = src.find('+').unwrap(); + let result = find_ident_at_offset(&module, plus_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Value); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "+" + ); + assert_eq!(ident.span.start, plus_offset); + assert_eq!(ident.span.end, plus_offset + "+".len()); + } + + #[test] + fn test_find_ident_value_operator_operands() { + let src = "module Test where\n\nfoo = bar + baz"; + let module = crate::parser::parse(src).expect("parse"); + // Click on "bar" (left operand) + let bar_offset = src.find("bar").unwrap(); + let result = find_ident_at_offset(&module, bar_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "bar" + ); + assert_eq!(ident.span.start, bar_offset); + assert_eq!(ident.span.end, bar_offset + "bar".len()); + // Click on "baz" (right operand) + let baz_offset = src.find("baz").unwrap(); + let result = find_ident_at_offset(&module, baz_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "baz" + ); + assert_eq!(ident.span.start, baz_offset); + assert_eq!(ident.span.end, baz_offset + "baz".len()); + } + + #[test] + fn test_find_ident_type_operator() { + let src = "module Test where\n\nfoo :: Int ~> String\nfoo = bar"; + let module = crate::parser::parse(src).expect("parse"); + let op_offset = src.find("~>").unwrap(); + let result = find_ident_at_offset(&module, op_offset); + assert!(result.is_some()); + let ident = result.unwrap(); + assert_eq!(ident.kind, RefKind::Type); + assert_eq!( + crate::interner::resolve(ident.name.name).unwrap(), + "~>" + ); + assert_eq!(ident.span.start, op_offset); + assert_eq!(ident.span.end, op_offset + "~>".len()); + } +} diff --git a/src/lsp/utils/mod.rs b/src/lsp/utils/mod.rs new file mode 100644 index 00000000..0e1f6b91 --- /dev/null +++ b/src/lsp/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod find_definition; +pub mod resolve; diff --git a/src/typechecker/resolve.rs b/src/lsp/utils/resolve.rs similarity index 95% rename from src/typechecker/resolve.rs rename to src/lsp/utils/resolve.rs index 03761ed9..50538805 100644 --- a/src/typechecker/resolve.rs +++ b/src/lsp/utils/resolve.rs @@ -67,7 +67,7 @@ impl ResolutionExports { parts: vec![interner::intern("Prim"), interner::intern(sub)], }; let prim_sym = interner::intern(&format!("Prim.{}", sub)); - let prim_exports = super::check::prim_submodule_exports(&prim_mod_name); + let prim_exports = crate::typechecker::check::prim_submodule_exports(&prim_mod_name); all_names_map.insert(prim_sym, module_exports_to_resolved_names(&prim_exports)); } @@ -283,7 +283,7 @@ fn maybe_qualify(name: Symbol, qualifier: Option) -> Symbol { // ===== Module export collection ===== /// Convert a `ModuleExports` (from the typechecker, used for Prim) into a `ModuleResolvedNames`. -fn module_exports_to_resolved_names(exports: &super::registry::ModuleExports) -> ModuleResolvedNames { +fn module_exports_to_resolved_names(exports: &crate::typechecker::registry::ModuleExports) -> ModuleResolvedNames { let mut names = ModuleResolvedNames::new(); for name in exports.values.keys() { names.values.insert(name.name); @@ -407,8 +407,8 @@ fn filter_by_exports( Some(crate::cst::DataMembers::Explicit(names)) => { let mut exported_ctors = Vec::new(); for n in names { - result.values.insert(*n); - exported_ctors.push(*n); + result.values.insert(n.value); + exported_ctors.push(n.value); } if !exported_ctors.is_empty() { result.data_constructors.insert(*name, exported_ctors); @@ -461,7 +461,7 @@ fn filter_by_exports( /// Import all exports from a known module into scope with an optional qualifier. fn import_known_exports_to_scope( - exports: &super::registry::ModuleExports, + exports: &crate::typechecker::registry::ModuleExports, scope: &mut NameScope, qualifier: Option, origin: NameOrigin, @@ -515,7 +515,7 @@ fn import_known_exports_to_scope( /// Import Prim exports into scope (unqualified). Prim types are built-in. fn import_prim_to_scope(scope: &mut NameScope) { - let prim = super::check::prim_exports(); + let prim = crate::typechecker::check::prim_exports(); import_known_exports_to_scope(prim, scope, None, NameOrigin::Prim); } @@ -527,10 +527,10 @@ fn import_prim_module_to_scope( imports: &Option, ) { let owned_exports; - let exports: &super::registry::ModuleExports = if is_prim_module(module) { - super::check::prim_exports() + let exports: &crate::typechecker::registry::ModuleExports = if is_prim_module(module) { + crate::typechecker::check::prim_exports() } else { - owned_exports = super::check::prim_submodule_exports(module); + owned_exports = crate::typechecker::check::prim_submodule_exports(module); &owned_exports }; @@ -547,13 +547,13 @@ fn import_prim_module_to_scope( crate::cst::Import::Value(name) => { scope .values - .insert(maybe_qualify(*name, qualifier), origin.clone()); + .insert(maybe_qualify(name.value, qualifier), origin.clone()); } crate::cst::Import::Type(name, members) => { scope .types - .insert(maybe_qualify(*name, qualifier), origin.clone()); - let name_qi = QualifiedIdent { module: None, name: *name }; + .insert(maybe_qualify(name.value, qualifier), origin.clone()); + let name_qi = QualifiedIdent { module: None, name: name.value }; if let Some(ctors) = exports.data_constructors.get(&name_qi) { match members { Some(crate::cst::DataMembers::All) => { @@ -568,7 +568,7 @@ fn import_prim_module_to_scope( for n in names { scope .values - .insert(maybe_qualify(*n, qualifier), origin.clone()); + .insert(maybe_qualify(n.value, qualifier), origin.clone()); } } None => {} @@ -576,15 +576,15 @@ fn import_prim_module_to_scope( } } crate::cst::Import::TypeOp(name) => { - scope.type_operators.insert(*name, origin.clone()); + scope.type_operators.insert(name.value, origin.clone()); } crate::cst::Import::Class(name) => { scope .classes - .insert(maybe_qualify(*name, qualifier), origin.clone()); + .insert(maybe_qualify(name.value, qualifier), origin.clone()); // Also import class methods for (method, (class, _)) in &exports.class_methods { - if class.name == *name { + if class.name == name.value { scope .values .insert(maybe_qualify(method.name, qualifier), origin.clone()); @@ -640,13 +640,13 @@ fn import_resolved_names_hiding( for item in hidden_items { match item { crate::cst::Import::Value(name) => { - hidden_values.insert(*name); + hidden_values.insert(name.value); } crate::cst::Import::Type(name, members) => { - hidden_types.insert(*name); + hidden_types.insert(name.value); match members { Some(crate::cst::DataMembers::All) => { - if let Some(ctors) = names.data_constructors.get(name) { + if let Some(ctors) = names.data_constructors.get(&name.value) { for ctor in ctors { hidden_values.insert(*ctor); } @@ -654,17 +654,17 @@ fn import_resolved_names_hiding( } Some(crate::cst::DataMembers::Explicit(ctors)) => { for ctor in ctors { - hidden_values.insert(*ctor); + hidden_values.insert(ctor.value); } } None => {} } } crate::cst::Import::TypeOp(name) => { - hidden_type_ops.insert(*name); + hidden_type_ops.insert(name.value); } crate::cst::Import::Class(name) => { - hidden_classes.insert(*name); + hidden_classes.insert(name.value); } } } @@ -707,12 +707,12 @@ fn import_explicit_item( ) { match item { crate::cst::Import::Value(name) => { - scope.values.insert(maybe_qualify(*name, qualifier), origin); + scope.values.insert(maybe_qualify(name.value, qualifier), origin); } crate::cst::Import::Type(name, members) => { scope .types - .insert(maybe_qualify(*name, qualifier), origin.clone()); + .insert(maybe_qualify(name.value, qualifier), origin.clone()); match members { Some(crate::cst::DataMembers::All) => { // We can't enumerate constructors without the registry. @@ -720,23 +720,23 @@ fn import_explicit_item( // data types have a constructor with the same name. scope .values - .insert(maybe_qualify(*name, qualifier), origin.clone()); + .insert(maybe_qualify(name.value, qualifier), origin.clone()); } Some(crate::cst::DataMembers::Explicit(names)) => { for n in names { scope .values - .insert(maybe_qualify(*n, qualifier), origin.clone()); + .insert(maybe_qualify(n.value, qualifier), origin.clone()); } } None => {} } } crate::cst::Import::TypeOp(name) => { - scope.type_operators.insert(*name, origin); + scope.type_operators.insert(name.value, origin); } crate::cst::Import::Class(name) => { - scope.classes.insert(*name, origin); + scope.classes.insert(name.value, origin); } } } @@ -752,15 +752,15 @@ fn import_explicit_item_with_resolution( ) { match item { crate::cst::Import::Value(name) => { - scope.values.insert(maybe_qualify(*name, qualifier), origin); + scope.values.insert(maybe_qualify(name.value, qualifier), origin); } crate::cst::Import::Type(name, members) => { scope .types - .insert(maybe_qualify(*name, qualifier), origin.clone()); + .insert(maybe_qualify(name.value, qualifier), origin.clone()); match members { Some(crate::cst::DataMembers::All) => { - if let Some(ctors) = module_names.data_constructors.get(name) { + if let Some(ctors) = module_names.data_constructors.get(&name.value) { for ctor in ctors { scope .values @@ -772,19 +772,19 @@ fn import_explicit_item_with_resolution( for n in names { scope .values - .insert(maybe_qualify(*n, qualifier), origin.clone()); + .insert(maybe_qualify(n.value, qualifier), origin.clone()); } } None => {} } } crate::cst::Import::TypeOp(name) => { - scope.type_operators.insert(*name, origin); + scope.type_operators.insert(name.value, origin); } crate::cst::Import::Class(name) => { scope .classes - .insert(maybe_qualify(*name, qualifier), origin); + .insert(maybe_qualify(name.value, qualifier), origin); } } } @@ -801,7 +801,7 @@ fn build_module_scope(module: &Module, resolution_exports: &ResolutionExports) - import_prim_to_scope(&mut scope); } // Prim is always available as a qualifier (for Prim.Int, Prim.Boolean, etc.) - let prim = super::check::prim_exports(); + let prim = crate::typechecker::check::prim_exports(); let prim_sym = interner::intern("Prim"); import_known_exports_to_scope(prim, &mut scope, Some(prim_sym), NameOrigin::Prim); @@ -1090,7 +1090,7 @@ fn walk_expr(r: &mut Resolver, expr: &Expr, locals: &LocalScope, type_vars: &Has Expr::Lambda { binders, body, .. } => { let mut inner = locals.clone(); for binder in binders { - collect_binder_names(binder, &mut inner); + collect_binder_names(binder, &mut inner, &mut r.resolutions); walk_binder(r, binder, locals, type_vars); } walk_expr(r, body, &inner, type_vars); @@ -1126,7 +1126,7 @@ fn walk_expr(r: &mut Resolver, expr: &Expr, locals: &LocalScope, type_vars: &Has Expr::Let { bindings, body, .. } => { let mut inner = locals.clone(); for binding in bindings { - collect_let_binding_names(binding, &mut inner); + collect_let_binding_names(binding, &mut inner, &mut r.resolutions); } for binding in bindings { walk_let_binding(r, binding, &inner, type_vars); @@ -1141,7 +1141,7 @@ fn walk_expr(r: &mut Resolver, expr: &Expr, locals: &LocalScope, type_vars: &Has } => { let mut inner = locals.clone(); for stmt in statements { - collect_do_statement_names(stmt, &mut inner); + collect_do_statement_names(stmt, &mut inner, &mut r.resolutions); } walk_do_statements(r, statements, locals, type_vars); walk_expr(r, result, &inner, type_vars); @@ -1293,26 +1293,39 @@ fn walk_type_expr(r: &mut Resolver, ty: &TypeExpr, type_vars: &HashSet) // ===== Binder helpers ===== /// Collect names introduced by a binder into the local scope. -fn collect_binder_names(binder: &Binder, locals: &mut LocalScope) { +/// Also records definition-site resolutions so hover works on binder names. +fn collect_binder_names(binder: &Binder, locals: &mut LocalScope, defs: &mut Vec) { match binder { Binder::Var { name, .. } => { locals.insert(name.value, name.span); + defs.push(ResolvedName { + src_span: name.span, + src_symbol: name.value, + namespace: Namespace::Value, + definition: DefinitionSite::LocalVar(name.span), + }); } Binder::Constructor { args, .. } => { for arg in args { - collect_binder_names(arg, locals); + collect_binder_names(arg, locals, defs); } } Binder::As { name, binder, .. } => { locals.insert(name.value, name.span); - collect_binder_names(binder, locals); + defs.push(ResolvedName { + src_span: name.span, + src_symbol: name.value, + namespace: Namespace::Value, + definition: DefinitionSite::LocalVar(name.span), + }); + collect_binder_names(binder, locals, defs); } Binder::Parens { binder, .. } => { - collect_binder_names(binder, locals); + collect_binder_names(binder, locals, defs); } Binder::Array { elements, .. } => { for e in elements { - collect_binder_names(e, locals); + collect_binder_names(e, locals, defs); } } Binder::Record { fields, .. } => { @@ -1320,19 +1333,25 @@ fn collect_binder_names(binder: &Binder, locals: &mut LocalScope) { match &field.binder { None => { locals.insert(field.label.value, field.label.span); + defs.push(ResolvedName { + src_span: field.label.span, + src_symbol: field.label.value, + namespace: Namespace::Value, + definition: DefinitionSite::LocalVar(field.label.span), + }); } Some(binder) => { - collect_binder_names(binder, locals); + collect_binder_names(binder, locals, defs); } } } } Binder::Op { left, right, .. } => { - collect_binder_names(left, locals); - collect_binder_names(right, locals); + collect_binder_names(left, locals, defs); + collect_binder_names(right, locals, defs); } Binder::Typed { binder, .. } => { - collect_binder_names(binder, locals); + collect_binder_names(binder, locals, defs); } Binder::Wildcard { .. } | Binder::Literal { .. } => {} } @@ -1398,7 +1417,7 @@ fn walk_case_alt( ) { let mut inner = locals.clone(); for binder in &alt.binders { - collect_binder_names(binder, &mut inner); + collect_binder_names(binder, &mut inner, &mut r.resolutions); walk_binder(r, binder, locals, type_vars); } walk_guarded(r, &alt.result, &inner, type_vars); @@ -1424,7 +1443,7 @@ fn walk_guarded( } GuardPattern::Pattern(binder, e) => { walk_expr(r, e, &guard_locals, type_vars); - collect_binder_names(binder, &mut guard_locals); + collect_binder_names(binder, &mut guard_locals, &mut r.resolutions); walk_binder(r, binder, locals, type_vars); } } @@ -1437,9 +1456,9 @@ fn walk_guarded( // ===== Let / do ===== -fn collect_let_binding_names(binding: &LetBinding, locals: &mut LocalScope) { +fn collect_let_binding_names(binding: &LetBinding, locals: &mut LocalScope, defs: &mut Vec) { if let LetBinding::Value { binder, .. } = binding { - collect_binder_names(binder, locals); + collect_binder_names(binder, locals, defs); } } @@ -1460,14 +1479,14 @@ fn walk_let_binding( } } -fn collect_do_statement_names(stmt: &DoStatement, locals: &mut LocalScope) { +fn collect_do_statement_names(stmt: &DoStatement, locals: &mut LocalScope, defs: &mut Vec) { match stmt { DoStatement::Bind { binder, .. } => { - collect_binder_names(binder, locals); + collect_binder_names(binder, locals, defs); } DoStatement::Let { bindings, .. } => { for binding in bindings { - collect_let_binding_names(binding, locals); + collect_let_binding_names(binding, locals, defs); } } DoStatement::Discard { .. } => {} @@ -1486,11 +1505,11 @@ fn walk_do_statements( DoStatement::Bind { binder, expr, .. } => { walk_expr(r, expr, ¤t, type_vars); walk_binder(r, binder, ¤t, type_vars); - collect_binder_names(binder, &mut current); + collect_binder_names(binder, &mut current, &mut r.resolutions); } DoStatement::Let { bindings, .. } => { for binding in bindings { - collect_let_binding_names(binding, &mut current); + collect_let_binding_names(binding, &mut current, &mut r.resolutions); } for binding in bindings { walk_let_binding(r, binding, ¤t, type_vars); @@ -1518,11 +1537,11 @@ fn walk_decl(r: &mut Resolver, decl: &Decl) { } => { let mut body_locals = locals.clone(); for binder in binders { - collect_binder_names(binder, &mut body_locals); + collect_binder_names(binder, &mut body_locals, &mut r.resolutions); walk_binder(r, binder, &locals, &type_vars); } for binding in where_clause { - collect_let_binding_names(binding, &mut body_locals); + collect_let_binding_names(binding, &mut body_locals, &mut r.resolutions); } for binding in where_clause { walk_let_binding(r, binding, &body_locals, &type_vars); diff --git a/src/main.rs b/src/main.rs index fbf0e5f0..21d67265 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,12 @@ enum Commands { #[arg(short, long, default_value = "output")] output: String, }, + /// Start the PureScript language server (LSP over stdio) + Lsp { + /// Shell command that outputs source file paths (one per line) + #[arg(long)] + sources_cmd: Option, + }, } fn main() { @@ -43,6 +49,9 @@ fn main() { .init(); match cli.command { + Commands::Lsp { sources_cmd } => { + purescript_fast_compiler::lsp::run_server(sources_cmd); + } Commands::Compile { globs, output } => { log::debug!("Starting compile with globs: {:?}", globs); diff --git a/src/parser/grammar.lalrpop b/src/parser/grammar.lalrpop index 24741c18..64519523 100644 --- a/src/parser/grammar.lalrpop +++ b/src/parser/grammar.lalrpop @@ -28,6 +28,7 @@ pub Module: Module = { exports, imports, decls, + comments: Vec::new(), } } }; @@ -119,7 +120,7 @@ Export: Export = { DataMembersOpt: Option = { => None, "(" DotDot ")" => Some(DataMembers::All), - "(" > ")" => Some(DataMembers::Explicit(members)), + "(" > ")" => Some(DataMembers::Explicit(members)), }; DotDot: () = { @@ -164,16 +165,11 @@ ImportItems: ImportList = { }; Import: Import = { - => Import::Value(name), - => Import::Type(name, members), - "class" => Import::Class(name), - "type" "(" ")" => Import::TypeOp(op), - "(" ")" => Import::Value(op), - "(" ":" ")" => Import::Value(crate::interner::intern(":")), - "(" "~" ")" => Import::Value(crate::interner::intern("~")), - "type" "(" ":" ")" => Import::TypeOp(crate::interner::intern(":")), - "type" "(" "~" ")" => Import::TypeOp(crate::interner::intern("~")), - "as" => Import::Value(crate::interner::intern("as")), + => Import::Value(name), + => Import::Type(name, members), + "class" => Import::Class(name), + "type" "(" ")" => Import::TypeOp(op), + "(" ")" => Import::Value(op), }; // ===== Declarations ===== @@ -196,6 +192,7 @@ TypeSignature: Decl = { span: Span::new(start, end), name, ty, + doc_comments: Vec::new(), } } }; @@ -210,6 +207,7 @@ ValueDecl: Decl = { binders, guarded: GuardedExpr::Unconditional(Box::new(expr)), where_clause: wc.unwrap_or_default(), + doc_comments: Vec::new(), } }, // Guarded: name binders | guard = expr ... @@ -221,6 +219,7 @@ ValueDecl: Decl = { binders, guarded: GuardedExpr::Guarded(guards), where_clause: vec![], + doc_comments: Vec::new(), } }, }; @@ -374,6 +373,7 @@ DataDecl: Decl = { is_role_decl: false, kind_type: None, type_var_kind_anns, + doc_comments: Vec::new(), } }, // data Foo :: Kind (kind signature) @@ -387,6 +387,7 @@ DataDecl: Decl = { is_role_decl: false, kind_type: Some(Box::new(kind)), type_var_kind_anns: vec![], + doc_comments: Vec::new(), } }, // data Foo (empty declaration, no constructors — FFI types) @@ -402,6 +403,7 @@ DataDecl: Decl = { is_role_decl: false, kind_type: None, type_var_kind_anns, + doc_comments: Vec::new(), } }, }; @@ -412,6 +414,7 @@ DataConstructor: DataConstructor = { span: Span::new(start, end), name, fields, + doc_comments: Vec::new(), } } }; @@ -427,6 +430,7 @@ TypeAliasDecl: Decl = { type_vars, ty, type_var_kind_anns, + doc_comments: Vec::new(), } }, // Negative type-level int alias: type NegOne = -1 @@ -443,6 +447,7 @@ TypeAliasDecl: Decl = { value: -n, }, type_var_kind_anns, + doc_comments: Vec::new(), } }, // Kinded type alias: type Name vars = Type :: Kind @@ -460,6 +465,7 @@ TypeAliasDecl: Decl = { kind: Box::new(kind), }, type_var_kind_anns, + doc_comments: Vec::new(), } }, // type Foo :: Kind (kind signature — reuse Data as placeholder) @@ -473,6 +479,7 @@ TypeAliasDecl: Decl = { is_role_decl: false, kind_type: Some(Box::new(kind)), type_var_kind_anns: vec![], + doc_comments: Vec::new(), } }, // type role Name role1 role2 ... — role declaration @@ -486,6 +493,7 @@ TypeAliasDecl: Decl = { is_role_decl: true, kind_type: None, type_var_kind_anns: vec![], + doc_comments: Vec::new(), } }, }; @@ -502,6 +510,7 @@ NewtypeDecl: Decl = { constructor, ty, type_var_kind_anns, + doc_comments: Vec::new(), } }, // newtype Foo :: Kind (kind signature) @@ -515,6 +524,7 @@ NewtypeDecl: Decl = { is_role_decl: false, kind_type: Some(Box::new(kind)), type_var_kind_anns: vec![], + doc_comments: Vec::new(), } }, }; @@ -528,6 +538,7 @@ ForeignDecl: Decl = { span: Span::new(start, end), name, ty, + doc_comments: Vec::new(), } }, // foreign import data Name :: Kind @@ -536,6 +547,7 @@ ForeignDecl: Decl = { span: Span::new(start, end), name, kind, + doc_comments: Vec::new(), } }, }; @@ -565,6 +577,7 @@ ClassDecl: Decl = { is_kind_sig: true, kind_type: Some(Box::new(kind)), type_var_kind_anns: vec![], + doc_comments: Vec::new(), } }, // class Name args fundeps? body? (no constraints) @@ -581,6 +594,7 @@ ClassDecl: Decl = { is_kind_sig: false, kind_type: None, type_var_kind_anns, + doc_comments: Vec::new(), } }, // class Constraint args <= Name vars fundeps? body? (single constraint) @@ -603,6 +617,7 @@ ClassDecl: Decl = { is_kind_sig: false, kind_type: None, type_var_kind_anns, + doc_comments: Vec::new(), } }, // class (C1, C2) <= Name vars fundeps? body? (multiple constraints) @@ -620,6 +635,7 @@ ClassDecl: Decl = { is_kind_sig: false, kind_type: None, type_var_kind_anns, + doc_comments: Vec::new(), } }, }; @@ -634,6 +650,7 @@ ClassMember: ClassMember = { span: Span::new(start, end), name, ty, + doc_comments: Vec::new(), } } }; @@ -672,6 +689,7 @@ InstanceDecl: Decl = { types: head.2, members: body.unwrap_or_default(), chain: false, + doc_comments: Vec::new(), } }, // Anonymous: instance head body? @@ -684,6 +702,7 @@ InstanceDecl: Decl = { types: head.2, members: body.unwrap_or_default(), chain: false, + doc_comments: Vec::new(), } }, }; @@ -765,6 +784,7 @@ DeriveDecl: Decl = { constraints: head.0, class_name: head.1, types: head.2, + doc_comments: Vec::new(), } }, // derive instance head (anonymous) @@ -776,6 +796,7 @@ DeriveDecl: Decl = { constraints: head.0, class_name: head.1, types: head.2, + doc_comments: Vec::new(), } }, // derive newtype instance name :: head @@ -788,6 +809,7 @@ DeriveDecl: Decl = { constraints: head.0, class_name: head.1, types: head.2, + doc_comments: Vec::new(), } }, // derive newtype instance head (anonymous) @@ -799,6 +821,7 @@ DeriveDecl: Decl = { constraints: head.0, class_name: head.1, types: head.2, + doc_comments: Vec::new(), } }, }; @@ -815,6 +838,7 @@ FixityDecl: Decl = { target, operator: op, is_type: false, + doc_comments: Vec::new(), } }, // infixr 4 type NaturalTransformation as ~> @@ -826,6 +850,7 @@ FixityDecl: Decl = { target, operator: op, is_type: true, + doc_comments: Vec::new(), } }, }; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 209e7f0c..43af3375 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -103,26 +103,129 @@ pub fn extract_class_type_vars(args: Vec) -> (Vec Result { // Step 1: Lex the source - let tokens = lex(source).map_err(|e| CompilerError::LexError { error: e })?; + let lex_result = lex(source).map_err(|e| CompilerError::LexError { error: e })?; // Step 2: Create lexer adapter for LALRPOP - let lexer = LexerAdapter::new(tokens); + let lexer = LexerAdapter::new(lex_result.tokens); // Step 3: Parse with LALRPOP - grammar::ModuleParser::new() + let mut module = grammar::ModuleParser::new() .parse(lexer) - .map_err(|e| CompilerError::SyntaxError { error: e }) + .map_err(|e| CompilerError::SyntaxError { error: e })?; + + // Step 4: Attach comments to the module and distribute doc-comments to declarations + attach_comments(&mut module, lex_result.comments); + + Ok(module) } /// Parse a PureScript expression string into a CST Expr. pub fn parse_expr(source: &str) -> Result { - let tokens = lex(source).map_err(|e| CompilerError::LexError { error: e })?; - let lexer = LexerAdapter::new(tokens); + let lex_result = lex(source).map_err(|e| CompilerError::LexError { error: e })?; + let lexer = LexerAdapter::new(lex_result.tokens); grammar::ExprParser::new() .parse(lexer) .map_err(|e| CompilerError::SyntaxError { error: e }) } +/// Attach comments to the module CST. +/// - All comments are stored on `module.comments` +/// - Doc-comments (`-- | ...`) are distributed to the declaration they precede +fn attach_comments( + module: &mut Module, + comments: Vec>, +) { + use crate::cst::Comment; + use crate::span::Span; + + // Convert from span::Spanned to (Comment, Span) tuples for the module + let comment_pairs: Vec<(Comment, Span)> = comments + .into_iter() + .map(|c| (c.node, c.span)) + .collect(); + + // Store all comments on the module + module.comments = comment_pairs.clone(); + + if module.decls.is_empty() { + return; + } + + // Collect doc-comment positions + let doc_comments: Vec<&(Comment, Span)> = comment_pairs + .iter() + .filter(|(c, _)| c.is_doc()) + .collect(); + + if doc_comments.is_empty() { + return; + } + + // Collect all declaration start positions for the "dominated" check + let decl_starts: Vec = module.decls.iter().map(|d| d.span().start).collect(); + + // For each declaration, find doc-comments that precede it + for (i, decl) in module.decls.iter_mut().enumerate() { + let decl_start = decl_starts[i]; + + // A doc-comment belongs to this decl if it ends before the decl starts + // and no other decl starts between the comment and this decl + let group: Vec = doc_comments + .iter() + .filter(|(_, span)| { + span.end <= decl_start + && !decl_starts.iter().any(|&s| s > span.start && s < decl_start) + }) + .map(|(c, _)| c.clone()) + .collect(); + + if !group.is_empty() { + decl.set_doc_comments(group); + } + } + + // Also distribute to class members and data constructors + for decl in &mut module.decls { + match decl { + crate::cst::Decl::Data { constructors, .. } => { + let ctor_starts: Vec = constructors.iter().map(|c| c.span.start).collect(); + for (i, ctor) in constructors.iter_mut().enumerate() { + let ctor_start = ctor_starts[i]; + let group: Vec = doc_comments + .iter() + .filter(|(_, span)| { + span.end <= ctor_start + && !ctor_starts.iter().any(|&s| s > span.start && s < ctor_start) + }) + .map(|(c, _)| c.clone()) + .collect(); + if !group.is_empty() { + ctor.doc_comments = group; + } + } + } + crate::cst::Decl::Class { members, .. } => { + let member_starts: Vec = members.iter().map(|m| m.span.start).collect(); + for (i, member) in members.iter_mut().enumerate() { + let member_start = member_starts[i]; + let group: Vec = doc_comments + .iter() + .filter(|(_, span)| { + span.end <= member_start + && !member_starts.iter().any(|&s| s > span.start && s < member_start) + }) + .map(|(c, _)| c.clone()) + .collect(); + if !group.is_empty() { + member.doc_comments = group; + } + } + } + _ => {} + } + } +} + #[cfg(test)] mod tests { use crate::cst::*; @@ -132,8 +235,8 @@ mod tests { // ===== Test Helpers ===== fn parse_expr(source: &str) -> Result { - let tokens = lex(source).map_err(|e| CompilerError::LexError { error: e })?; - let lexer = LexerAdapter::new(tokens); + let lex_result = lex(source).map_err(|e| CompilerError::LexError { error: e })?; + let lexer = LexerAdapter::new(lex_result.tokens); grammar::ExprParser::new() .parse(lexer) .map_err(|e| CompilerError::SyntaxError { error: e }) @@ -141,13 +244,40 @@ mod tests { fn parse_type(source: &str) -> Result { // add the correct error span here - let tokens = lex(source).map_err(|e| CompilerError::LexError { error: e })?; - let lexer = LexerAdapter::new(tokens); + let lex_result = lex(source).map_err(|e| CompilerError::LexError { error: e })?; + let lexer = LexerAdapter::new(lex_result.tokens); grammar::TypeExprParser::new() .parse(lexer) .map_err(|e| CompilerError::SyntaxError { error: e }) } + // ===== Comment Tests ===== + + #[test] + fn test_comments_collected_on_module() { + let module = parse("module Main where\n-- line comment\n{- block comment -}\nx = 1").unwrap(); + assert_eq!(module.comments.len(), 2); + assert!(matches!(&module.comments[0].0, Comment::Line(_))); + assert!(matches!(&module.comments[1].0, Comment::Block(_))); + } + + #[test] + fn test_doc_comments_attached_to_decl() { + let module = parse("module Main where\n-- | Adds one\nadd1 :: Int -> Int\nadd1 x = x").unwrap(); + // Doc comment should be on module.comments + assert_eq!(module.comments.len(), 1); + assert!(matches!(&module.comments[0].0, Comment::Doc(_))); + // And attached to the first decl (TypeSignature) + assert_eq!(module.decls[0].doc_comments().len(), 1); + assert!(module.decls[0].doc_comments()[0].is_doc()); + } + + #[test] + fn test_multi_line_doc_comments() { + let module = parse("module Main where\n-- | Line 1\n-- | Line 2\nfoo = 1").unwrap(); + assert_eq!(module.decls[0].doc_comments().len(), 2); + } + // ===== Expression Tests: Literals ===== #[test] diff --git a/src/span.rs b/src/span.rs index bddc2c44..b6457dd4 100644 --- a/src/span.rs +++ b/src/span.rs @@ -36,41 +36,39 @@ impl Span { } } pub fn to_pos(&self, source: &str) -> Option<(SourcePos, SourcePos)> { + if self.start > source.len() || self.end > source.len() || self.start > self.end { + return None; + } + let mut line = 1; - let mut column = 1; - let mut current_pos = 1; - let source_len = source.len(); + let mut col = 1; + let mut start_pos = None; + for (i, c) in source.char_indices() { - if i >= self.start { - break; + if i == self.start { + start_pos = Some(SourcePos { line, column: col }); + } + if i == self.end { + return start_pos.map(|s| (s, SourcePos { line, column: col })); } if c == '\n' { line += 1; - column = 1; + col = 1; } else { - column += 1; + col += 1; } - current_pos = i + c.len_utf8(); - } - let start_pos = SourcePos { line, column }; - // # check current_pos against self.end to avoid iterating past the end of the span - if current_pos > source_len || current_pos > self.end { - return None; } - for (i, c) in source[current_pos..].char_indices() { - if current_pos + i >= self.end { - break; - } - if c == '\n' { - line += 1; - column = 1; - } else { - column += 1; - } + // Handle start/end at source.len() (past last char) + let end_pos = SourcePos { line, column: col }; + if self.start == source.len() && start_pos.is_none() { + start_pos = Some(end_pos); + } + if self.end == source.len() { + return start_pos.map(|s| (s, end_pos)); } - let end_pos = SourcePos { line, column }; - Some((start_pos, end_pos)) + + None } } @@ -81,6 +79,135 @@ pub struct Spanned { pub span: Span, } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_to_pos_single_line() { + let src = "hello world"; + // Span covering "world" (bytes 6..11) + let span = Span::new(6, 11); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 7 }); + assert_eq!(end, SourcePos { line: 1, column: 12 }); + } + + #[test] + fn test_to_pos_start_of_source() { + let src = "abc"; + let span = Span::new(0, 3); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 1 }); + assert_eq!(end, SourcePos { line: 1, column: 4 }); + } + + #[test] + fn test_to_pos_single_char_at_start() { + let src = "abc"; + let span = Span::new(0, 1); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 1 }); + assert_eq!(end, SourcePos { line: 1, column: 2 }); + } + + #[test] + fn test_to_pos_multiline() { + let src = "abc\ndef\nghi"; + // Span covering "def" (bytes 4..7) + let span = Span::new(4, 7); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 2, column: 1 }); + assert_eq!(end, SourcePos { line: 2, column: 4 }); + } + + #[test] + fn test_to_pos_spanning_newline() { + let src = "abc\ndef"; + // Span covering "c\nd" (bytes 2..5) + let span = Span::new(2, 5); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 3 }); + assert_eq!(end, SourcePos { line: 2, column: 2 }); + } + + #[test] + fn test_to_pos_end_of_source() { + let src = "abc"; + // Span covering last char "c" (bytes 2..3) + let span = Span::new(2, 3); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 3 }); + assert_eq!(end, SourcePos { line: 1, column: 4 }); + } + + #[test] + fn test_to_pos_entire_source() { + let src = "ab\ncd"; + let span = Span::new(0, 5); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 1 }); + assert_eq!(end, SourcePos { line: 2, column: 3 }); + } + + #[test] + fn test_to_pos_empty_span() { + let src = "abc"; + let span = Span::new(1, 1); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 2 }); + assert_eq!(end, SourcePos { line: 1, column: 2 }); + } + + #[test] + fn test_to_pos_empty_span_at_start() { + let src = "abc"; + let span = Span::new(0, 0); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 1 }); + assert_eq!(end, SourcePos { line: 1, column: 1 }); + } + + #[test] + fn test_to_pos_out_of_bounds() { + let src = "abc"; + assert!(Span::new(0, 4).to_pos(src).is_none()); + assert!(Span::new(4, 5).to_pos(src).is_none()); + assert!(Span::new(3, 2).to_pos(src).is_none()); + } + + #[test] + fn test_to_pos_multibyte_utf8() { + let src = "a§b"; // § is 2 bytes (0xC2 0xA7) + // "b" starts at byte 3 + let span = Span::new(3, 4); + let (start, end) = span.to_pos(src).unwrap(); + // § occupies 1 column despite being 2 bytes + assert_eq!(start, SourcePos { line: 1, column: 3 }); + assert_eq!(end, SourcePos { line: 1, column: 4 }); + } + + #[test] + fn test_to_pos_on_newline_char() { + let src = "ab\ncd"; + // Span covering just the newline (byte 2..3) + let span = Span::new(2, 3); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 1, column: 3 }); + assert_eq!(end, SourcePos { line: 2, column: 1 }); + } + + #[test] + fn test_to_pos_third_line() { + let src = "a\nb\ncdef"; + // Span covering "cd" (bytes 4..6) + let span = Span::new(4, 6); + let (start, end) = span.to_pos(src).unwrap(); + assert_eq!(start, SourcePos { line: 3, column: 1 }); + assert_eq!(end, SourcePos { line: 3, column: 3 }); + } +} + impl Spanned { pub fn new(node: T, span: Span) -> Self { Self { node, span } diff --git a/src/typechecker/check.rs b/src/typechecker/check.rs index 1bbbf620..a1e1ed68 100644 --- a/src/typechecker/check.rs +++ b/src/typechecker/check.rs @@ -1329,6 +1329,8 @@ pub struct CheckResult { pub types: HashMap, pub errors: Vec, pub exports: ModuleExports, + /// Span→Type map for local variable bindings, for hover support. + pub span_types: HashMap, } // Build the exports for the built-in Prim module. @@ -1337,7 +1339,7 @@ pub struct CheckResult { static PRIM_EXPORTS: std::sync::LazyLock = std::sync::LazyLock::new(prim_exports_inner); -pub(super) fn prim_exports() -> &'static ModuleExports { +pub fn prim_exports() -> &'static ModuleExports { &PRIM_EXPORTS } @@ -1395,7 +1397,7 @@ pub(super) fn is_prim_submodule(module_name: &crate::cst::ModuleName) -> bool { /// Build exports for Prim submodules (Prim.Coerce, Prim.Row, Prim.RowList, etc.). /// These are built-in modules with compiler-magic classes and types. -pub(super) fn prim_submodule_exports(module_name: &crate::cst::ModuleName) -> ModuleExports { +pub fn prim_submodule_exports(module_name: &crate::cst::ModuleName) -> ModuleExports { let mut exports = ModuleExports::default(); let sub = if module_name.parts.len() >= 2 { @@ -1893,8 +1895,17 @@ pub fn tarjan_scc(nodes: &[Symbol], edges: &HashMap>) -> /// and a list of any errors encountered. Checking continues past errors so that /// partial results are available for tooling (e.g. IDE hover types). pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { + check_module_impl(module, registry, false) +} + +pub fn check_module_for_ide(module: &Module, registry: &ModuleRegistry) -> CheckResult { + check_module_impl(module, registry, true) +} + +fn check_module_impl(module: &Module, registry: &ModuleRegistry, collect_span_types: bool) -> CheckResult { let mut ctx = InferCtx::new(); ctx.module_mode = true; + ctx.collect_span_types = collect_span_types; let mut env = Env::new(); let mut signatures: HashMap = HashMap::new(); let mut result_types: HashMap = HashMap::new(); @@ -2048,6 +2059,7 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { // Also register Prim's class_param_counts so Partial etc. are known classes for (class_name, count) in &prim.class_param_counts { class_param_counts.entry(*class_name).or_insert(*count); + ctx.prim_class_names.insert(class_name.name); } } @@ -2096,6 +2108,7 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { } else { registry.lookup(&import_decl.module.parts) }; + let is_prim_source = is_prim_module(&import_decl.module) || is_prim_submodule(&import_decl.module); if let Some(exports) = module_exports { for (class_name, count) in &exports.class_param_counts { match class_param_counts.entry(*class_name) { @@ -2110,6 +2123,26 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { } } } + if is_prim_source { + ctx.prim_class_names.insert(class_name.name); + } else { + // Also track compiler-solved classes re-exported from non-Prim modules. + // These class names match the magic solver in check_instance_depth and + // must be recognized regardless of import source. + let class_str = crate::interner::resolve(class_name.name).unwrap_or_default(); + let is_compiler_solved = matches!( + class_str.as_str(), + "IsSymbol" | "Reflectable" | "Reifiable" + | "Partial" | "Warn" | "Fail" + | "Coercible" + | "Lacks" | "Cons" | "Nub" | "Union" | "RowToList" + | "CompareSymbol" | "Append" | "Compare" + | "Add" | "Mul" | "ToString" + ); + if is_compiler_solved { + ctx.prim_class_names.insert(class_name.name); + } + } } for (class_name, fd) in &exports.class_fundeps { ctx.class_fundeps @@ -2453,16 +2486,16 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { let allowed_type_names: Option> = match &import_decl.imports { Some(crate::cst::ImportList::Explicit(items)) => { let names: HashSet = items.iter().filter_map(|item| match item { - crate::cst::Import::Type(name, _) => Some(*name), - crate::cst::Import::Class(name) => Some(*name), + crate::cst::Import::Type(name, _) => Some(name.value), + crate::cst::Import::Class(name) => Some(name.value), _ => None, }).collect(); Some(names) } Some(crate::cst::ImportList::Hiding(items)) => { let hidden: HashSet = items.iter().filter_map(|item| match item { - crate::cst::Import::Type(name, _) => Some(*name), - crate::cst::Import::Class(name) => Some(*name), + crate::cst::Import::Type(name, _) => Some(name.value), + crate::cst::Import::Class(name) => Some(name.value), _ => None, }).collect(); let names: HashSet = exported_type_names.iter() @@ -3555,6 +3588,8 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { // Track class type parameter count for arity checking class_param_counts.insert(qi(name.value), type_vars.len()); known_classes.insert(qi(name.value)); + // A locally-defined class shadows any Prim magic class with the same name + ctx.prim_class_names.remove(&name.value); } // Check for duplicate type arguments @@ -3660,8 +3695,9 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { // Reject user-written Coercible instances (compiler-solved only) { - let cn_str = crate::interner::resolve(class_name.name).unwrap_or_default(); - if cn_str == "Coercible" { + if crate::interner::symbol_eq(class_name.name, "Coercible") + && ctx.prim_class_names.contains(&class_name.name) + { errors.push(TypeError::InvalidCoercibleInstanceDeclaration { span: *span }); continue; } @@ -5384,9 +5420,9 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { // Explicitly imported value — check it's not a class method // in the source module (e.g. `import Prelude (f)` where f // is a class method should not count). - let qi_sym = qi(*sym); + let qi_sym = qi(sym.value); if !module_exports.class_methods.contains_key(&qi_sym) { - set.insert(*sym); + set.insert(sym.value); } } } @@ -6745,28 +6781,7 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { // site. Only fire when at least one arg is concrete and there are no type vars. if !all_pure_unif && !has_type_vars { // Skip compiler-magic classes that are resolved without explicit instances - let class_str = crate::interner::resolve(class_name.name).unwrap_or_default(); - let is_magic = matches!( - class_str.as_str(), - "Partial" - | "Warn" - | "Coercible" - | "IsSymbol" - | "Fail" - | "Union" - | "Cons" - | "Lacks" - | "RowToList" - | "Nub" - | "CompareSymbol" - | "Append" - | "Compare" - | "Add" - | "Mul" - | "ToString" - | "Reflectable" - | "Reifiable" - ); + let is_magic = ctx.prim_class_names.contains(&class_name.name); if !is_magic { errors.push(TypeError::NoInstanceFound { span: *span, @@ -7127,29 +7142,8 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { if !class_has_instances && !has_type_vars && !has_mixed_unif && (!all_pure_unif || (!is_given && !all_generalized)) { - let class_str = crate::interner::resolve(class_name.name).unwrap_or_default(); // Skip compiler-magic classes that are resolved without explicit instances - let is_magic = matches!( - class_str.as_str(), - "Partial" - | "Warn" - | "Coercible" - | "IsSymbol" - | "Fail" - | "Union" - | "Cons" - | "Lacks" - | "RowToList" - | "Nub" - | "CompareSymbol" - | "Append" - | "Compare" - | "Add" - | "Mul" - | "ToString" - | "Reflectable" - | "Reifiable" - ); + let is_magic = ctx.prim_class_names.contains(&class_name.name); if !is_magic { errors.push(TypeError::NoInstanceFound { span: *span, @@ -7367,11 +7361,11 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { // Check that each listed constructor actually belongs to this type let valid_ctors = ctx.data_constructors.get(&qi(*name)); for ctor in ctors { - let is_valid = valid_ctors.map_or(false, |cs| cs.contains(&qi(*ctor))); + let is_valid = valid_ctors.map_or(false, |cs| cs.contains(&qi(ctor.value))); if !is_valid { errors.push(TypeError::UnkownExport { span: export_list.span, - name: *ctor, + name: ctor.value, }); } } @@ -7381,7 +7375,7 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { if !ctors.is_empty() { if let Some(all_ctors) = valid_ctors { let exported_set: std::collections::HashSet = - ctors.iter().map(|c| qi(*c)).collect(); + ctors.iter().map(|c| qi(c.value)).collect(); for ctor in all_ctors { if !exported_set.contains(ctor) { errors.push(TypeError::TransitiveExportError { @@ -7514,7 +7508,7 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { has_this_ctor && match members { crate::cst::DataMembers::All => true, - crate::cst::DataMembers::Explicit(cs) => cs.contains(&target), + crate::cst::DataMembers::Explicit(cs) => cs.iter().any(|c| c.value == target), } } else { false @@ -8267,10 +8261,15 @@ pub fn check_module(module: &Module, registry: &ModuleRegistry) -> CheckResult { } } + let span_types: HashMap = ctx.span_types.iter() + .map(|(span, ty)| (*span, ctx.state.zonk(ty.clone()))) + .collect(); + CheckResult { types: result_types, errors, exports: module_exports, + span_types, } } @@ -8843,7 +8842,7 @@ fn process_imports( // Track explicitly imported type names (unqualified) if qualifier.is_none() { if let Import::Type(name, _) | Import::Class(name) = item { - explicitly_imported_types.insert(*name); + explicitly_imported_types.insert(name.value); } } import_item( @@ -9248,12 +9247,13 @@ fn import_item( canonical_origins: &Option>, ) { match item { - Import::Value(name) => { - let name_qi = qi(*name); + Import::Value(name_spanned) => { + let name = name_spanned.value; + let name_qi = qi(name); if exports.values.get(&name_qi).is_none() && exports.class_methods.get(&name_qi).is_none() { errors.push(TypeError::UnknownImport { span: import_span, - name: *name, + name, }); return; } @@ -9272,24 +9272,24 @@ fn import_item( } else { scheme.clone() }; - env.insert_scheme(maybe_qualify_symbol(*name, qualifier), scheme); + env.insert_scheme(maybe_qualify_symbol(name, qualifier), scheme); } // Instances are imported centrally in process_imports with module-level dedup. // Import fixity if this is an operator if let Some(fixity) = exports.value_fixities.get(&name_qi) { - ctx.value_fixities.insert(*name, *fixity); + ctx.value_fixities.insert(name, *fixity); } if exports.function_op_aliases.contains(&name_qi) { ctx.function_op_aliases.insert(name_qi); } - if let Some(target) = exports.operator_class_targets.get(name) { - ctx.operator_class_targets.insert(qi(*name), qi(*target)); + if let Some(target) = exports.operator_class_targets.get(&name) { + ctx.operator_class_targets.insert(qi(name), qi(*target)); } if exports.constrained_class_methods.contains(&name_qi) { - ctx.constrained_class_methods.insert(*name); + ctx.constrained_class_methods.insert(name); } if let Some(constraints) = exports.method_own_constraints.get(&name_qi) { - ctx.method_own_constraints.entry(*name).or_insert_with(|| constraints.clone()); + ctx.method_own_constraints.entry(name).or_insert_with(|| constraints.clone()); } // Import ctor_details if this is a constructor alias (e.g. `:|` for `NonEmpty`) if let Some(details) = exports.ctor_details.get(&name_qi) { @@ -9312,9 +9312,9 @@ fn import_item( } } // Import partial discharger info (functions with Partial in param position) - if exports.partial_dischargers.contains(name) { + if exports.partial_dischargers.contains(&name) { ctx.partial_dischargers - .insert(maybe_qualify_qualified_ident(qi(*name), qualifier)); + .insert(maybe_qualify_qualified_ident(qi(name), qualifier)); } // Import ctor_details if the operator targets a constructor (e.g. `:` → Cons) // Use the TARGET name as key since Binder::Constructor uses the target name @@ -9331,8 +9331,9 @@ fn import_item( } } } - Import::Type(name, members) => { - let name_qi = qi(*name); + Import::Type(name_spanned, members) => { + let name = name_spanned.value; + let name_qi = qi(name); if let Some(ctors) = exports.data_constructors.get(&name_qi) { ctx.data_constructors.insert(name_qi, ctors.clone()); if let Some(q) = qualifier { @@ -9341,10 +9342,10 @@ fn import_item( if let Some(arity) = exports.type_con_arities.get(&name_qi) { ctx.type_con_arities.insert(name_qi, *arity); } - if let Some(roles) = exports.type_roles.get(name) { - ctx.type_roles.insert(*name, roles.clone()); + if let Some(roles) = exports.type_roles.get(&name) { + ctx.type_roles.insert(name, roles.clone()); } - if exports.newtype_names.contains(name) { + if exports.newtype_names.contains(&name) { ctx.newtype_names.insert(name_qi); } @@ -9353,14 +9354,14 @@ fn import_item( Some(DataMembers::Explicit(listed)) => { // Validate that each listed constructor actually exists for ctor_name in listed { - if !ctors.iter().any(|c| c.name == *ctor_name) { + if !ctors.iter().any(|c| c.name == ctor_name.value) { errors.push(TypeError::UnknownImportDataConstructor { span: import_span, - name: *ctor_name, + name: ctor_name.value, }); } } - listed.iter().map(|n| qi(*n)).collect() + listed.iter().map(|n| qi(n.value)).collect() } None => Vec::new(), // Just the type, no constructors }; @@ -9405,8 +9406,8 @@ fn import_item( } else { alias.1.clone() }; - ctx.state.type_aliases.insert(*name, (sym_params.clone(), body)); - ctx.qualified_import_unqual_aliases.remove(name); + ctx.state.type_aliases.insert(name, (sym_params.clone(), body)); + ctx.qualified_import_unqual_aliases.remove(&name); } if let Some(q) = qualifier { // Canonicalize body for qualified import @@ -9415,15 +9416,15 @@ fn import_item( for (&n, &origin) in &exports.type_origins { if origin == mod_sym { type_names.insert(n); } } - let body = canonicalize_alias_body_types(&alias.1, mod_sym, &type_names, Some(*name)); - let qualified_name = maybe_qualify_symbol(*name, Some(q)); + let body = canonicalize_alias_body_types(&alias.1, mod_sym, &type_names, Some(name)); + let qualified_name = maybe_qualify_symbol(name, Some(q)); ctx.state.type_aliases.insert(qualified_name, (sym_params.clone(), body.clone())); ctx.qualified_type_alias_names.insert(maybe_qualify_qualified_ident(name_qi, Some(q))); // Register under canonical key if let Some(co) = canonical_origins { - if let Some(&origin) = co.get(name) { + if let Some(&origin) = co.get(&name) { let origin_str = crate::interner::resolve(origin).unwrap_or_default(); - let name_str = crate::interner::resolve(*name).unwrap_or_default(); + let name_str = crate::interner::resolve(name).unwrap_or_default(); let canonical_key = crate::interner::intern(&format!("{}.{}", origin_str, name_str)); ctx.state.type_aliases.entry(canonical_key) .or_insert((sym_params.clone(), body)); @@ -9434,9 +9435,9 @@ fn import_item( // Skip for zero-param aliases to avoid self-referential expansion loops. if !sym_params.is_empty() { if let Some(co) = canonical_origins { - if let Some(&origin) = co.get(name) { + if let Some(&origin) = co.get(&name) { let origin_str = crate::interner::resolve(origin).unwrap_or_default(); - let name_str = crate::interner::resolve(*name).unwrap_or_default(); + let name_str = crate::interner::resolve(name).unwrap_or_default(); let canonical_key = crate::interner::intern(&format!("{}.{}", origin_str, name_str)); ctx.state.type_aliases.entry(canonical_key) .or_insert((sym_params.clone(), alias.1.clone())); @@ -9459,23 +9460,23 @@ fn import_item( } else { alias.1.clone() }; - ctx.state.type_aliases.insert(*name, (sym_params.clone(), body)); - ctx.qualified_import_unqual_aliases.remove(name); + ctx.state.type_aliases.insert(name, (sym_params.clone(), body)); + ctx.qualified_import_unqual_aliases.remove(&name); } if qualifier.is_some() { // Canonicalize body for qualified import let mod_sym = module_name_to_symbol(_module_name); let alias_names: HashSet = exports.type_aliases.keys().map(|k| k.name).collect(); - let body = canonicalize_alias_body_types(&alias.1, mod_sym, &alias_names, Some(*name)); - let qualified_name = maybe_qualify_symbol(*name, qualifier); + let body = canonicalize_alias_body_types(&alias.1, mod_sym, &alias_names, Some(name)); + let qualified_name = maybe_qualify_symbol(name, qualifier); ctx.state.type_aliases.insert(qualified_name, (sym_params.clone(), body.clone())); ctx.qualified_type_alias_names.insert(maybe_qualify_qualified_ident(name_qi, qualifier)); // Register under canonical key (skip zero-param to avoid self-ref loops) if !sym_params.is_empty() { if let Some(co) = canonical_origins { - if let Some(&origin) = co.get(name) { + if let Some(&origin) = co.get(&name) { let origin_str = crate::interner::resolve(origin).unwrap_or_default(); - let name_str = crate::interner::resolve(*name).unwrap_or_default(); + let name_str = crate::interner::resolve(name).unwrap_or_default(); let canonical_key = crate::interner::intern(&format!("{}.{}", origin_str, name_str)); ctx.state.type_aliases.entry(canonical_key) .or_insert((sym_params.clone(), body)); @@ -9486,9 +9487,9 @@ fn import_item( // Register under canonical key (unqualified import, skip zero-param) if !sym_params.is_empty() { if let Some(co) = canonical_origins { - if let Some(&origin) = co.get(name) { + if let Some(&origin) = co.get(&name) { let origin_str = crate::interner::resolve(origin).unwrap_or_default(); - let name_str = crate::interner::resolve(*name).unwrap_or_default(); + let name_str = crate::interner::resolve(name).unwrap_or_default(); let canonical_key = crate::interner::intern(&format!("{}.{}", origin_str, name_str)); ctx.state.type_aliases.entry(canonical_key) .or_insert((sym_params.clone(), alias.1.clone())); @@ -9499,26 +9500,27 @@ fn import_item( } else { errors.push(TypeError::UnknownImport { span: import_span, - name: *name, + name, }); } } - Import::Class(name) => { - let name_qi = qi(*name); + Import::Class(name_spanned) => { + let name = name_spanned.value; + let name_qi = qi(name); // Check if the class exists in the exports: it may have methods, // instances, or be a constraint-only class (no methods, e.g. `class (A a, B a) <= C a`). - let has_class = exports.class_methods.values().any(|(cn, _)| cn.name == *name) + let has_class = exports.class_methods.values().any(|(cn, _)| cn.name == name) || exports.instances.get(&name_qi).is_some() || exports.class_param_counts.contains_key(&name_qi); if !has_class { errors.push(TypeError::UnknownImport { span: import_span, - name: *name, + name, }); return; } for (method_name, (class_name, tvs)) in &exports.class_methods { - if class_name.name == *name { + if class_name.name == name { ctx.class_methods .insert(*method_name, (*class_name, tvs.iter().map(|s| s.name).collect())); if exports.constrained_class_methods.contains(method_name) { @@ -9537,8 +9539,9 @@ fn import_item( } // Instances are imported centrally in process_imports with module-level dedup. } - Import::TypeOp(name) => { - let name_qi = qi(*name); + Import::TypeOp(name_spanned) => { + let name = name_spanned.value; + let name_qi = qi(name); if let Some(target) = exports.type_operators.get(&name_qi) { ctx.type_operators.insert(name_qi, *target); // Import the target's type alias definition if it exists @@ -9560,7 +9563,7 @@ fn import_item( } else { errors.push(TypeError::UnknownImport { span: import_span, - name: *name, + name, }); } } @@ -9762,12 +9765,7 @@ fn import_all_except( /// Get the primary symbol name from an Import item. fn import_name(item: &Import) -> Symbol { - match item { - Import::Value(name) - | Import::Type(name, _) - | Import::TypeOp(name) - | Import::Class(name) => *name, - } + item.name() } /// Determines which names from a module's exports should be re-exported, @@ -9800,19 +9798,19 @@ fn build_import_filter( for imp in imports { match imp { crate::cst::Import::Value(name) => { - values.insert(*name); + values.insert(name.value); // Importing an operator also imports its target value into the env // so the typechecker can look up its type (AST desugars `1 + 2` to `add 1 2`). // The AST converter gates user-visible scoping separately. - if let Some(target) = mod_exports.value_operator_targets.get(&qi(*name)) { + if let Some(target) = mod_exports.value_operator_targets.get(&qi(name.value)) { values.insert(target.name); } } crate::cst::Import::Type(name, members) => { - types.insert(*name); + types.insert(name.value); // Importing Type(..) also imports its constructors as values if let Some(crate::cst::DataMembers::All) = members { - if let Some(ctors) = mod_exports.data_constructors.get(&qi(*name)) { + if let Some(ctors) = mod_exports.data_constructors.get(&qi(name.value)) { for ctor in ctors { values.insert(ctor.name); } @@ -9820,21 +9818,21 @@ fn build_import_filter( } else if let Some(crate::cst::DataMembers::Explicit(ctor_names)) = members { for ctor in ctor_names { - values.insert(*ctor); + values.insert(ctor.value); } } } crate::cst::Import::Class(name) => { - classes.insert(*name); + classes.insert(name.value); // Importing a class also imports all its methods for (method_name, (class_name, _)) in &mod_exports.class_methods { - if class_name.name == *name { + if class_name.name == name.value { values.insert(method_name.name); } } } crate::cst::Import::TypeOp(name) => { - type_ops.insert(*name); + type_ops.insert(name.value); } } } @@ -9854,12 +9852,12 @@ fn build_import_filter( for imp in imports { match imp { crate::cst::Import::Value(name) => { - hidden_values.insert(*name); + hidden_values.insert(name.value); } crate::cst::Import::Type(name, members) => { - hidden_types.insert(*name); + hidden_types.insert(name.value); if let Some(crate::cst::DataMembers::All) = members { - if let Some(ctors) = mod_exports.data_constructors.get(&qi(*name)) { + if let Some(ctors) = mod_exports.data_constructors.get(&qi(name.value)) { for ctor in ctors { hidden_values.insert(ctor.name); } @@ -9867,20 +9865,20 @@ fn build_import_filter( } else if let Some(crate::cst::DataMembers::Explicit(ctor_names)) = members { for ctor in ctor_names { - hidden_values.insert(*ctor); + hidden_values.insert(ctor.value); } } } crate::cst::Import::Class(name) => { - hidden_classes.insert(*name); + hidden_classes.insert(name.value); for (method_name, (class_name, _)) in &mod_exports.class_methods { - if class_name.name == *name { + if class_name.name == name.value { hidden_values.insert(method_name.name); } } } crate::cst::Import::TypeOp(name) => { - hidden_type_ops.insert(*name); + hidden_type_ops.insert(name.value); } } } @@ -10008,7 +10006,7 @@ fn filter_exports( if let Some(ctors) = all.data_constructors.get(&name_qi) { let export_ctors: Vec = match members { Some(DataMembers::All) => ctors.clone(), - Some(DataMembers::Explicit(listed)) => listed.iter().map(|n| qi(*n)).collect(), + Some(DataMembers::Explicit(listed)) => listed.iter().map(|n| qi(n.value)).collect(), None => { // Don't overwrite existing constructor list with empty // (handles `module X (A(..), A)` where second A has no members) @@ -11870,7 +11868,7 @@ fn check_instance_depth( } } - // Built-in solver instances for compiler-magic type classes. TODO make this module aware + // Built-in solver instances for compiler-magic type classes. let class_str = crate::interner::resolve(class_name.name) .unwrap_or_default() .to_string(); @@ -12080,7 +12078,7 @@ fn has_matching_instance_depth( return false; } - // Built-in solver instances for compiler-magic type classes + // Built-in solver instances for compiler-magic type classes. let class_str = crate::interner::resolve(class_name.name) .unwrap_or_default() .to_string(); @@ -14242,14 +14240,17 @@ pub(crate) fn extract_type_signature_constraints( } => { let mut result = Vec::new(); for c in constraints { + // Skip auto-satisfied compiler-magic classes that never fail and + // don't need solver verification at call sites. These are always + // auto-satisfied regardless of import source. + // Do NOT skip classes with solvers that can fail (Lacks, Coercible, + // Compare, Add, Mul, ToString, IsSymbol, Fail, etc.). let class_str = crate::interner::resolve(c.class.name).unwrap_or_default(); - // Skip compiler-magic classes that don't have explicit instances. - // These are resolved by special solvers or auto-satisfied. - let is_magic = matches!( + let is_auto_satisfied = matches!( class_str.as_str(), "Partial" | "Warn" | "Union" | "Cons" | "RowToList" | "CompareSymbol" ); - if is_magic { + if is_auto_satisfied { continue; } let mut args = Vec::new(); @@ -14265,7 +14266,7 @@ pub(crate) fn extract_type_signature_constraints( } if ok { result.push((c.class, args)); - } else if class_str == "Fail" { + } else if crate::interner::symbol_eq(c.class.name, "Fail") { // Fail constraints should always be recorded even if args can't // be converted (e.g. type-level Text/Quote from Prim.TypeError). // The args aren't needed for error detection — any use of Fail diff --git a/src/typechecker/infer.rs b/src/typechecker/infer.rs index 979c84c5..a501561a 100644 --- a/src/typechecker/infer.rs +++ b/src/typechecker/infer.rs @@ -186,6 +186,17 @@ pub struct InferCtx { /// unqualified. If a subsequent unqualified import provides the same alias name, /// it's removed from this set. pub qualified_import_unqual_aliases: HashSet, + /// Class names that originate from Prim / Prim submodules (compiler-magic classes). + /// Used to distinguish `Partial`, `Coercible`, `Union`, etc. from user-defined classes + /// with the same name. Populated during Prim import processing, cleared for any name + /// the user re-declares locally. + pub prim_class_names: HashSet, + /// Whether to collect span→type mappings (only needed in IDE/LSP mode). + pub collect_span_types: bool, + /// Span→Type map for local variable bindings (lambda params, case binders, + /// let/where bindings, do/ado bind statements). Collected during inference, + /// zonked at output time for hover support. + pub span_types: HashMap, } impl InferCtx { @@ -226,6 +237,9 @@ impl InferCtx { class_method_schemes: HashMap::new(), non_exhaustive_errors: Vec::new(), qualified_import_unqual_aliases: HashSet::new(), + prim_class_names: HashSet::new(), + collect_span_types: false, + span_types: HashMap::new(), } } @@ -1176,6 +1190,9 @@ impl InferCtx { if let Some(self_ty) = pre_inserted.get(&name.value) { self.state.unify(*span, self_ty, &binding_ty)?; } + if self.collect_span_types { + self.span_types.insert(name.span, binding_ty.clone()); + } let scheme = env.generalize_local_batch(&mut self.state, binding_ty, &all_binding_names); env.insert_scheme(name.value, scheme); eagerly_processed.insert(name.value); @@ -1253,6 +1270,9 @@ impl InferCtx { self.infer(env, expr)? }; self.scoped_type_vars = prev_scoped; + if self.collect_span_types { + self.span_types.insert(name.span, binding_ty.clone()); + } // Unify with pre-inserted type for recursion if let Some(self_ty) = pre_inserted.get(&name.value) { self.state.unify(*span, self_ty, &binding_ty)?; @@ -1838,6 +1858,9 @@ impl InferCtx { }), } }; + if self.collect_span_types { + self.span_types.insert(field.label.span, field_ty.clone()); + } field_types.push((field.label.value, field_ty)); } let record_ty = Type::Record(field_types, None); @@ -1869,6 +1892,9 @@ impl InferCtx { // Instantiate forall types: each access to a polymorphic field // (e.g. `forall a. a -> m a`) gets a fresh instantiation. let result = self.instantiate_forall_type(ty.clone())?; + if self.collect_span_types { + self.span_types.insert(field.span, result.clone()); + } return Ok(result); } } @@ -1882,6 +1908,9 @@ impl InferCtx { Some(Box::new(new_tail)), ); self.state.unify(span, &tail, &extended)?; + if self.collect_span_types { + self.span_types.insert(field.span, field_ty.clone()); + } return Ok(field_ty); } Err(TypeError::NotImplemented { @@ -1898,6 +1927,9 @@ impl InferCtx { Some(Box::new(row_tail)), ); self.state.unify(span, &record_ty, &expected_record)?; + if self.collect_span_types { + self.span_types.insert(field.span, field_ty.clone()); + } Ok(field_ty) } } @@ -2370,6 +2402,9 @@ impl InferCtx { match binder { Binder::Var { name, .. } => { env.insert_mono(name.value, expected.clone()); + if self.collect_span_types { + self.span_types.insert(name.span, expected.clone()); + } Ok(()) } Binder::Wildcard { .. } => Ok(()), @@ -2468,6 +2503,9 @@ impl InferCtx { } Binder::As { name, binder, .. } => { env.insert_mono(name.value, expected.clone()); + if self.collect_span_types { + self.span_types.insert(name.span, expected.clone()); + } self.infer_binder(env, binder, expected) } Binder::Typed { span, binder, ty } => { @@ -2510,6 +2548,9 @@ impl InferCtx { None => { // Pun: { x } means bind x to the value of field x env.insert_mono(field.label.value, field_ty.clone()); + if self.collect_span_types { + self.span_types.insert(field.label.span, field_ty.clone()); + } } } field_types.push((field.label.value, field_ty)); diff --git a/src/typechecker/mod.rs b/src/typechecker/mod.rs index 89f9e9ee..05f0ebd2 100644 --- a/src/typechecker/mod.rs +++ b/src/typechecker/mod.rs @@ -7,7 +7,6 @@ pub mod convert; pub mod check; pub mod kind; pub mod registry; -pub mod resolve; use std::collections::HashMap; @@ -16,7 +15,6 @@ use crate::typechecker::types::Type; pub use check::CheckResult; pub use registry::{ModuleExports, ModuleRegistry}; -pub use resolve::{ResolvedResult, ResolvedName, Namespace, DefinitionSite, ResolutionExports}; // ===== Deadline mechanism for aborting long-running typechecks ===== @@ -93,15 +91,29 @@ pub fn check_module(module: &crate::cst::Module) -> CheckResult { /// Typecheck a full CST module with a registry, returning partial results and accumulated errors. /// Performs CST→AST conversion internally; returns conversion errors if any. pub fn check_module_with_registry(module: &crate::cst::Module, registry: &ModuleRegistry) -> CheckResult { + check_module_with_options(module, registry, false) +} + +/// Typecheck a full CST module for IDE use, also collecting span→type mappings for hover. +pub fn check_module_for_ide(module: &crate::cst::Module, registry: &ModuleRegistry) -> CheckResult { + check_module_with_options(module, registry, true) +} + +fn check_module_with_options(module: &crate::cst::Module, registry: &ModuleRegistry, collect_span_types: bool) -> CheckResult { let (ast_module, convert_errors) = crate::ast::convert(module, registry); if !convert_errors.is_empty() { return CheckResult { types: HashMap::new(), errors: convert_errors, exports: ModuleExports::default(), + span_types: HashMap::new(), }; } - check::check_module(&ast_module, registry) + if collect_span_types { + check::check_module_for_ide(&ast_module, registry) + } else { + check::check_module(&ast_module, registry) + } } #[cfg(test)] diff --git a/src/typechecker/registry.rs b/src/typechecker/registry.rs index 28e1a955..9d1e65f7 100644 --- a/src/typechecker/registry.rs +++ b/src/typechecker/registry.rs @@ -116,4 +116,20 @@ impl ModuleRegistry { pub fn contains(&self, name: &[Symbol]) -> bool { self.modules.contains_key(name) || self.base.as_ref().map_or(false, |b| b.contains(name)) } + + /// Iterate over all module names and their exports (overlay + base). + pub fn iter_all(&self) -> Vec<(&[Symbol], &ModuleExports)> { + let mut result: HashMap<&[Symbol], &ModuleExports> = HashMap::new(); + // Base modules first (will be overridden by overlay) + if let Some(base) = &self.base { + for (name, exports) in base.iter_all() { + result.insert(name, exports); + } + } + // Overlay modules override base + for (name, exports) in &self.modules { + result.insert(name.as_slice(), exports); + } + result.into_iter().collect() + } } diff --git a/src/typechecker/types.rs b/src/typechecker/types.rs index 6eae8a16..6a7077cf 100644 --- a/src/typechecker/types.rs +++ b/src/typechecker/types.rs @@ -146,16 +146,42 @@ impl Type { } } -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Type { + /// Format with parentheses when in a nested context that requires them. + fn fmt_nested(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Type::App(..) | Type::Fun(..) | Type::Forall(..) => { + write!(f, "(")?; + self.fmt_inner(f)?; + write!(f, ")") + } + _ => self.fmt_inner(f), + } + } + + /// Format the type without outer parentheses. + fn fmt_inner(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Type::Unif(id) => write!(f, "?{}", id.0), Type::Var(sym) => write!(f, "{}", interner::resolve(*sym).unwrap_or_default()), Type::Con(sym) => write!(f, "{}", sym), - Type::App(func, arg) => write!(f, "({} {})", func, arg), - Type::Fun(from, to) => write!(f, "({} -> {})", from, to), + Type::App(func, arg) => { + // Function position doesn't need parens for App chains, but does for Fun/Forall + match func.as_ref() { + Type::App(..) | Type::Con(..) | Type::Var(..) | Type::Unif(..) => func.fmt_inner(f)?, + _ => func.fmt_nested(f)?, + }; + write!(f, " ")?; + arg.fmt_nested(f) + } + Type::Fun(from, to) => { + from.fmt_nested(f)?; + write!(f, " -> ")?; + // Right side of -> doesn't need parens for -> (right-associative) + to.fmt_inner(f) + } Type::Forall(vars, ty) => { - write!(f, "(forall")?; + write!(f, "forall")?; for (v, visible) in vars { if *visible { write!(f, " @{}", interner::resolve(*v).unwrap_or_default())?; @@ -163,7 +189,8 @@ impl fmt::Display for Type { write!(f, " {}", interner::resolve(*v).unwrap_or_default())?; } } - write!(f, ". {})", ty) + write!(f, ". ")?; + ty.fmt_inner(f) } Type::TypeString(sym) => write!(f, "\"{}\"", interner::resolve(*sym).unwrap_or_default()), Type::TypeInt(n) => write!(f, "{}", n), @@ -187,6 +214,12 @@ impl fmt::Display for Type { } } +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt_inner(f) + } +} + /// A type scheme (polytype): quantified type variables + monotype. /// When instantiated, each quantified var is replaced with a fresh unification variable. /// Quantified variables are `Type::Var(Symbol)` in the body, making schemes diff --git a/tests/fixtures/lsp/goto_definition/Simple.purs b/tests/fixtures/lsp/goto_definition/Simple.purs new file mode 100644 index 00000000..ee483230 --- /dev/null +++ b/tests/fixtures/lsp/goto_definition/Simple.purs @@ -0,0 +1,74 @@ +module Simple where + +import Prelude (add, ($)) +import Simple.Lib (class Cl, LibT(LibA, LibB), member, times2) + +int :: Int +int = fn 1 + +fn :: Int -> Int +fn int = times2 $ int + increment + where + increment = 1 + +myAdd = add + +infix 3 myAdd as + + +data T = A | B + +newtype I = I Int + +fn2 :: T -> I +fn2 = case _ of + A -> I 0 + B -> I 1 + +-- Format: line:col (name) => file start_line:start_col-end_line:end_col (0-indexed) +-- +-- Line 2: import Prelude (add, ($)) +-- 2:16 (add) => ../../packages/prelude/src/Data/Semiring.purs 44:2-44:5 +-- 2:22 ($) => ../../packages/prelude/src/Data/Function.purs 47:0-47:5 +-- +-- Line 3: import Simple.Lib (class Cl, LibT(LibA, LibB), member, times2) +-- 3:25 (Cl) => Simple/Lib.purs 4:6-4:8 +-- 3:29 (LibT) => Simple/Lib.purs 7:5-7:9 +-- 3:34 (LibA) => Simple/Lib.purs 8:4-8:8 +-- 3:40 (LibB) => Simple/Lib.purs 9:4-9:8 +-- 3:47 (member) => Simple/Lib.purs 5:2-5:8 +-- 3:55 (times2) => Simple/Lib.purs 11:0-11:6 +-- +-- Line 5: int :: Int +-- 5:7 (Int) => Prim (no source) +-- +-- Line 6: int = fn 1 +-- 6:6 (fn) => Simple.purs 9:0-13:0 +-- +-- Line 8: fn :: Int -> Int +-- 8:6 (Int) => Prim (no source) +-- 8:13 (Int) => Prim (no source) +-- +-- Line 9: fn int = times2 $ int + increment +-- 9:9 (times2) => Simple/Lib.purs 11:0-11:6 +-- 9:16 ($) => ../../packages/prelude/src/Data/Function.purs 47:0-47:5 +-- 9:18 (int) => Simple.purs 9:3-9:6 +-- 9:22 (+) => Simple.purs 15:0-15:18 +-- 9:24 (increment) => Simple.purs 11:3-11:12 +-- +-- Line 13: myAdd = add +-- 13:8 (add) => ../../packages/prelude/src/Data/Semiring.purs 44:2-44:5 +-- +-- Line 19: newtype I = I Int +-- 19:14 (Int) => Prim (no source) +-- +-- Line 21: fn2 :: T -> I +-- 21:7 (T) => Simple.purs 17:0-17:14 +-- 21:12 (I) => Simple.purs 19:0-19:17 +-- +-- Line 23: A -> I 0 +-- 23:2 (A) => Simple.purs 17:9-17:10 +-- 23:7 (I) => Simple.purs 19:0-19:17 +-- +-- Line 24: B -> I 1 +-- 24:2 (B) => Simple.purs 17:13-17:14 +-- 24:7 (I) => Simple.purs 19:0-19:17 diff --git a/tests/fixtures/lsp/goto_definition/Simple/Lib.purs b/tests/fixtures/lsp/goto_definition/Simple/Lib.purs new file mode 100644 index 00000000..390a8906 --- /dev/null +++ b/tests/fixtures/lsp/goto_definition/Simple/Lib.purs @@ -0,0 +1,12 @@ +module Simple.Lib where + +import Prelude + +class Cl a where + member :: a -> a + +data LibT + = LibA + | LibB + +times2 n = n * 2 diff --git a/tests/fixtures/lsp/hover/Simple.purs b/tests/fixtures/lsp/hover/Simple.purs new file mode 100644 index 00000000..0c1c5642 --- /dev/null +++ b/tests/fixtures/lsp/hover/Simple.purs @@ -0,0 +1,153 @@ +module Simple where + +import Simple.Lib (class Cl, member, times2, addOne, Effect, class MyFunctor, myMap) + +-- | The answer to everything +x = 42 + +fn :: Int -> Int +fn n = times2 n + +data Color = Red | Green | Blue + +data Box a = MkBox a + +boxed = MkBox x + +colorFn c = case c of + Red -> 1 + Green -> 2 + Blue -> 3 + +class MyShow a where + myShow :: a -> String + +instance MyShow Int where + myShow _ = "int" + +shown = myShow 42 + +-- | Wraps a value in an identity +newtype Identity a = Identity a + +-- | An alias for Int +type Age = Int + +-- | A foreign-imported function +foreign import myFfi :: Int -> String + +foreign import data MyOpaque :: Type + +useAddOne = addOne 1 + +useMember :: forall a. Cl a => a -> a +useMember a = member a + +useEffect :: Effect Int -> Int +useEffect _ = 0 + +useMap :: forall f a b. MyFunctor f => f a -> f b +useMap = myMap + +withLet :: Int +withLet = let y = 10 in y + +withWhere :: Int -> Int +withWhere q = r + where r = q + +myRecord = { name: "hello", age: 42 } + +getName = myRecord.name + +-- Format: line:col (name) => hover: +-- Use "null" for expected null result +-- Use "doc: " to also check doc-comment content +-- +-- Line 5: x = 42 +-- 5:0 (x) => hover: Int | doc: The answer to everything +-- +-- Line 7: fn :: Int -> Int +-- 7:0 (fn) => hover: Int -> Int +-- +-- Line 8: fn n = times2 n +-- 8:7 (times2) => hover: times2 +-- +-- Line 10: data Color = Red | Green | Blue +-- 10:5 (Color) => hover: Type +-- +-- Line 14: boxed = MkBox x +-- 14:0 (boxed) => hover: Box Int +-- 14:14 (x) => hover: Int +-- +-- Line 16: colorFn c = case c of +-- 16:0 (colorFn) => hover: Color -> Int +-- +-- Line 21: class MyShow a where +-- 21:6 (MyShow) => hover: Type -> Constraint +-- +-- Line 22: myShow :: a -> String +-- 22:2 (myShow) => hover: myShow +-- +-- Line 27: shown = myShow 42 +-- 27:0 (shown) => hover: String +-- 27:8 (myShow) => hover: myShow +-- +-- Line 30: newtype Identity a = Identity a +-- 30:8 (Identity) => hover: Type | doc: Wraps a value in an identity +-- +-- Line 33: type Age = Int +-- 33:5 (Age) => hover: Type | doc: An alias for Int +-- +-- Line 36: foreign import myFfi :: Int -> String +-- 36:15 (myFfi) => hover: Int -> String | doc: A foreign-imported function +-- +-- Line 38: foreign import data MyOpaque :: Type +-- 38:20 (MyOpaque) => hover: Type +-- +-- Line 40: useAddOne = addOne 1 +-- 40:0 (useAddOne) => hover: Int +-- 40:12 (addOne) => hover: addOne +-- +-- Line 42: useMember :: forall a. Cl a => a -> a +-- 42:0 (useMember) => hover: useMember +-- 42:23 (Cl) => hover: Type -> Constraint | doc: This is a class +-- +-- Line 45: useEffect :: Effect Int -> Int +-- 45:13 (Effect) => hover: Type -> Type | doc: Opaque effect type +-- +-- Line 48: useMap :: forall f a b. MyFunctor f => f a -> f b +-- 48:24 (MyFunctor) => hover: (Type -> Type) -> Constraint | doc: Custom functor +-- +-- Local variable: function parameter n (definition) +-- 8:3 (n) => hover: Int +-- Local variable: function parameter n (reference) +-- 8:14 (n) => hover: Int +-- +-- Local variable: case binder c (definition) +-- 16:8 (c) => hover: Color +-- +-- Local variable: let binding y (definition and reference) +-- 52:14 (y) => hover: Int +-- 52:24 (y) => hover: Int +-- +-- Local variable: function parameter q and where binding r +-- 55:10 (q) => hover: Int +-- 55:14 (r) => hover: Int +-- 56:8 (r) => hover: Int +-- +-- Line 2: import Simple.Lib (class Cl, member, ...) +-- 2:29 (member) => hover: member +-- 2:53 (Effect) => hover: Type -> Type | doc: Opaque effect type +-- 2:45 (addOne) => hover: addOne | doc: Adds one to a number +-- 2:67 (MyFunctor) => hover: (Type -> Type) -> Constraint | doc: Custom functor +-- +-- Record literal labels +-- 58:13 (name) => hover: String +-- 58:28 (age) => hover: Int +-- +-- Record access +-- 60:19 (name) => hover: String +-- +-- Line 1: empty line +-- 1:0 (ws) => hover: null diff --git a/tests/fixtures/lsp/hover/Simple/Lib.purs b/tests/fixtures/lsp/hover/Simple/Lib.purs new file mode 100644 index 00000000..ac92b037 --- /dev/null +++ b/tests/fixtures/lsp/hover/Simple/Lib.purs @@ -0,0 +1,24 @@ +module Simple.Lib where + +import Prelude + +-- | This is a class +class Cl a where + member :: a -> a + +data LibT + = LibA + | LibB + +times2 n = n * 2 + +-- | Adds one to a number +foreign import addOne :: Int -> Int + +-- | Opaque effect type +foreign import data Effect :: Type -> Type + +-- | Custom functor +class MyFunctor f where + myMap :: forall a b. (a -> b) -> f a -> f b + \ No newline at end of file diff --git a/tests/fixtures/original-compiler/failing/Redefined b/tests/fixtures/original-compiler/failing/Redefined new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/original-compiler/passing/TypeAliasShadowsImport/Lib.purs b/tests/fixtures/original-compiler/passing/TypeAliasShadowsImport/Lib.purs new file mode 100644 index 00000000..074b8123 --- /dev/null +++ b/tests/fixtures/original-compiler/passing/TypeAliasShadowsImport/Lib.purs @@ -0,0 +1,3 @@ +module TypeAliasShadowsImport.Lib where + +data Output = Output1 | Output2 diff --git a/tests/integration.rs b/tests/integration.rs index 8f17e1cb..410c0d60 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -32,8 +32,8 @@ fn lookup_type(source: &str, name: &str) -> Type { #[test] fn lex_simple_module() { - let tokens = lex("module Main where\nx = 42").unwrap(); - assert!(tokens.len() > 0); + let result = lex("module Main where\nx = 42").unwrap(); + assert!(result.tokens.len() > 0); } #[test] @@ -298,4 +298,30 @@ fn debug_fixture_errors() { check("NewtypeInstance5", "module Main where\nnewtype X a = X a\nclass Functor f where\n map :: forall a b. (a -> b) -> f a -> f b\nderive newtype instance functorX :: Functor X"); check("2806", "module X where\ndata E a b = L a | R b\ng :: forall a b . E a b -> a\ng e | L x <- e = x"); -} \ No newline at end of file +} + +#[test] +fn e2e_span_types_local_vars() { + use purescript_fast_compiler::build::{build_from_sources_with_options, BuildOptions}; + + let lib_source = std::fs::read_to_string("tests/fixtures/lsp/hover/Simple/Lib.purs").unwrap(); + let main_source = std::fs::read_to_string("tests/fixtures/lsp/hover/Simple.purs").unwrap(); + let main_mod = parse(&main_source).expect("parse Main"); + + let prelude_glob = "tests/fixtures/packages/prelude/src/**/*.purs"; + let mut sources: Vec<(String, String)> = Vec::new(); + sources.push(("Lib.purs".to_string(), lib_source)); + sources.push(("Main.purs".to_string(), main_source)); + for entry in glob::glob(prelude_glob).unwrap().flatten() { + if let Ok(src) = std::fs::read_to_string(&entry) { + sources.push((entry.to_string_lossy().into_owned(), src)); + } + } + let source_refs: Vec<(&str, &str)> = sources.iter().map(|(p, s)| (p.as_str(), s.as_str())).collect(); + let options = BuildOptions { output_dir: None, ..Default::default() }; + let (_, registry) = build_from_sources_with_options(&source_refs, &None, None, &options); + + let result = purescript_fast_compiler::typechecker::check_module_for_ide(&main_mod, ®istry); + // Should have span_types entries for local variables (n, c, q, y, r, a) + assert!(result.span_types.len() >= 5, "expected at least 5 span_types entries, got {}", result.span_types.len()); +} diff --git a/tests/lsp_e2e.rs b/tests/lsp_e2e.rs new file mode 100644 index 00000000..d35fecef --- /dev/null +++ b/tests/lsp_e2e.rs @@ -0,0 +1,945 @@ +use std::path::{Path, PathBuf}; + +use regex::Regex; +use serde_json::{json, Value}; +use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; +use tokio::sync::{mpsc, Mutex}; +use tower_lsp::lsp_types::Url; +use tower_lsp::{LspService, Server}; + +use purescript_fast_compiler::lsp::Backend; + +/// Send a JSON-RPC message with Content-Length framing. +async fn send_framed(writer: &mut (impl AsyncWriteExt + Unpin), msg: &Value) { + let body = serde_json::to_string(msg).unwrap(); + let framed = format!("Content-Length: {}\r\n\r\n{}", body.len(), body); + writer.write_all(framed.as_bytes()).await.unwrap(); +} + +/// Read one JSON-RPC message from the stream (parses Content-Length header). +async fn read_message(reader: &mut BufReader) -> Value { + let mut content_length: usize = 0; + loop { + let mut line = String::new(); + reader.read_line(&mut line).await.unwrap(); + let line = line.trim(); + if line.is_empty() { + break; + } + if let Some(len) = line.strip_prefix("Content-Length: ") { + content_length = len.trim().parse().unwrap(); + } + } + assert!(content_length > 0, "No Content-Length header found"); + + let mut body = vec![0u8; content_length]; + reader.read_exact(&mut body).await.unwrap(); + serde_json::from_slice(&body).unwrap() +} + +struct TestServer { + writer: std::sync::Arc>, + responses: mpsc::UnboundedReceiver, +} + +impl TestServer { + async fn start() -> Self { + Self::start_with_sources(None).await + } + + async fn start_with_sources(sources_cmd: Option) -> Self { + let (req_client, req_server) = tokio::io::duplex(1024 * 64); + let (resp_server, resp_client) = tokio::io::duplex(1024 * 64); + + let (service, socket) = LspService::new(|client| Backend::new(client, sources_cmd)); + tokio::spawn(Server::new(req_server, resp_server, socket).serve(service)); + + let writer = std::sync::Arc::new(Mutex::new(req_client)); + let (tx, rx) = mpsc::unbounded_channel(); + + // Background reader: auto-responds to server→client requests, forwards responses + let writer_clone = writer.clone(); + tokio::spawn(async move { + let mut reader = BufReader::new(resp_client); + loop { + let msg = read_message(&mut reader).await; + let has_method = msg.get("method").is_some(); + let has_id = msg.get("id").is_some(); + + if has_method && has_id { + // Server→client request (e.g. window/workDoneProgress/create) + let resp = json!({ + "jsonrpc": "2.0", + "id": msg["id"], + "result": null, + }); + let mut w = writer_clone.lock().await; + send_framed(&mut *w, &resp).await; + } else if has_id && !has_method { + // Response to our request + let _ = tx.send(msg); + } + // Notifications (method but no id): silently drop + } + }); + + let mut server = TestServer { + writer, + responses: rx, + }; + + server.initialize().await; + server + } + + async fn initialize(&mut self) { + self.send_request(1, "initialize", json!({ + "capabilities": {}, + "rootUri": null, + "processId": null, + })) + .await; + + let resp = self.read_response(1).await; + assert!(resp.get("result").is_some(), "initialize should succeed"); + + self.send_notification("initialized", json!({})).await; + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + async fn send_request(&mut self, id: u64, method: &str, params: Value) { + let msg = json!({ + "jsonrpc": "2.0", + "id": id, + "method": method, + "params": params, + }); + let mut w = self.writer.lock().await; + send_framed(&mut *w, &msg).await; + } + + async fn send_notification(&mut self, method: &str, params: Value) { + let msg = json!({ + "jsonrpc": "2.0", + "method": method, + "params": params, + }); + let mut w = self.writer.lock().await; + send_framed(&mut *w, &msg).await; + } + + async fn read_response(&mut self, expected_id: u64) -> Value { + loop { + let msg = self.responses.recv().await.expect("response channel closed"); + if msg.get("id").and_then(|id| id.as_u64()) == Some(expected_id) { + return msg; + } + } + } + + async fn open_file(&mut self, uri: &str, text: &str) { + self.send_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": uri, + "languageId": "purescript", + "version": 1, + "text": text, + } + }), + ) + .await; + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + async fn goto_definition(&mut self, id: u64, uri: &str, line: u32, character: u32) -> Value { + self.send_request( + id, + "textDocument/definition", + json!({ + "textDocument": { "uri": uri }, + "position": { "line": line, "character": character }, + }), + ) + .await; + self.read_response(id).await + } + + async fn hover(&mut self, id: u64, uri: &str, line: u32, character: u32) -> Value { + self.send_request( + id, + "textDocument/hover", + json!({ + "textDocument": { "uri": uri }, + "position": { "line": line, "character": character }, + }), + ) + .await; + self.read_response(id).await + } + + async fn completion(&mut self, id: u64, uri: &str, line: u32, character: u32) -> Value { + self.send_request( + id, + "textDocument/completion", + json!({ + "textDocument": { "uri": uri }, + "position": { "line": line, "character": character }, + }), + ) + .await; + self.read_response(id).await + } +} + +#[tokio::test] +async fn test_lsp_initialize_capabilities() { + let (req_client, req_server) = tokio::io::duplex(1024 * 64); + let (resp_server, resp_client) = tokio::io::duplex(1024 * 64); + + let (service, socket) = LspService::new(|client| Backend::new(client, None)); + tokio::spawn(Server::new(req_server, resp_server, socket).serve(service)); + + let mut writer = req_client; + let mut reader = BufReader::new(resp_client); + + send_framed( + &mut writer, + &json!({ + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "capabilities": {}, + "rootUri": null, + "processId": null, + }, + }), + ) + .await; + + let resp = read_message(&mut reader).await; + let result = resp.get("result").expect("should have result"); + let caps = result.get("capabilities").expect("should have capabilities"); + + let def_provider = caps.get("definitionProvider").expect("should have definitionProvider"); + assert_eq!(def_provider, &json!(true)); + + let sync = caps.get("textDocumentSync").expect("should have textDocumentSync"); + assert_eq!(sync, &json!(1)); + + let info = result.get("serverInfo").expect("should have serverInfo"); + assert_eq!(info.get("name").unwrap(), "pfc"); +} + +#[tokio::test] +async fn test_lsp_goto_def_local_value() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfoo = 1\n\nbar = foo"; + server.open_file(uri, src).await; + + let resp = server.goto_definition(10, uri, 4, 6).await; + let result = resp.get("result").expect("should have result"); + + assert!(result.is_object(), "result should be a Location object, got: {result}"); + let range = result.get("range").expect("should have range"); + let start = range.get("start").expect("should have start"); + assert_eq!(start.get("line").unwrap(), 2); + assert_eq!(start.get("character").unwrap(), 0); + assert_eq!(result.get("uri").unwrap(), uri); +} + +#[tokio::test] +async fn test_lsp_goto_def_local_constructor() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\ndata Color = Red | Green | Blue\n\nfoo = Red"; + server.open_file(uri, src).await; + + let resp = server.goto_definition(10, uri, 4, 6).await; + let result = resp.get("result").expect("should have result"); + + assert!(result.is_object(), "result should be a Location, got: {result}"); + let range = result.get("range").expect("should have range"); + let start = range.get("start").expect("should have start"); + assert_eq!(start.get("line").unwrap(), 2); + assert_eq!(result.get("uri").unwrap(), uri); +} + +#[tokio::test] +async fn test_lsp_goto_def_local_type_in_signature() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\ndata Foo = MkFoo\n\nbar :: Foo\nbar = MkFoo"; + server.open_file(uri, src).await; + + let resp = server.goto_definition(10, uri, 4, 7).await; + let result = resp.get("result").expect("should have result"); + + assert!(result.is_object(), "result should be a Location, got: {result}"); + let range = result.get("range").expect("should have range"); + let start = range.get("start").expect("should have start"); + assert_eq!(start.get("line").unwrap(), 2); +} + +#[tokio::test] +async fn test_lsp_goto_def_returns_null_for_unknown() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfoo = unknownThing"; + server.open_file(uri, src).await; + + let resp = server.goto_definition(10, uri, 2, 6).await; + let result = resp.get("result").expect("should have result"); + assert!(result.is_null(), "result should be null for unknown, got: {result}"); +} + +#[tokio::test] +async fn test_lsp_goto_def_returns_null_on_whitespace() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfoo = 1"; + server.open_file(uri, src).await; + + let resp = server.goto_definition(10, uri, 1, 0).await; + let result = resp.get("result").expect("should have result"); + assert!(result.is_null(), "result should be null on whitespace, got: {result}"); +} + +// --- Fixture-driven go-to-definition test --- + +struct GotoDefTestCase { + line: u32, + col: u32, + name: String, + expected: GotoDefExpected, +} + +enum GotoDefExpected { + NoSource, + Location { + uri: String, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + }, +} + +/// Parse test comments from a fixture file. +/// Format: `-- line:col (name) => file start_line:start_col-end_line:end_col` +/// Or: `-- line:col (name) => Prim (no source)` +fn parse_goto_def_comments(source: &str, fixture_dir: &Path) -> Vec { + let re = Regex::new(r"^-- (\d+):(\d+) \(([^)]+)\) => (.+)$").unwrap(); + let mut cases = Vec::new(); + + for line in source.lines() { + let line = line.trim(); + let Some(caps) = re.captures(line) else { + continue; + }; + + let test_line: u32 = caps[1].parse().unwrap(); + let test_col: u32 = caps[2].parse().unwrap(); + let name = caps[3].to_string(); + let target = &caps[4]; + + let expected = if target == "Prim (no source)" { + GotoDefExpected::NoSource + } else { + let (file, positions) = + target.rsplit_once(' ').expect("expected 'file line:col-line:col'"); + let pos_re = Regex::new(r"^(\d+):(\d+)-(\d+):(\d+)$").unwrap(); + let pos_caps = pos_re + .captures(positions) + .unwrap_or_else(|| panic!("bad position format: {positions}")); + + let start_line: u32 = pos_caps[1].parse().unwrap(); + let start_col: u32 = pos_caps[2].parse().unwrap(); + let end_line: u32 = pos_caps[3].parse().unwrap(); + let end_col: u32 = pos_caps[4].parse().unwrap(); + + let file_path = fixture_dir + .join(file) + .canonicalize() + .unwrap_or_else(|e| panic!("cannot resolve fixture path {file}: {e}")); + let uri = Url::from_file_path(&file_path) + .expect("valid file path") + .to_string(); + + GotoDefExpected::Location { + uri, + start_line, + start_col, + end_line, + end_col, + } + }; + + cases.push(GotoDefTestCase { + line: test_line, + col: test_col, + name, + expected, + }); + } + + cases +} + +#[tokio::test] +async fn test_lsp_goto_definition_fixture() { + let fixture_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/lsp/goto_definition"), + ) + .unwrap(); + + let packages_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/packages"), + ) + .unwrap(); + + // Read fixture source and parse test comments + let simple_path = fixture_dir.join("Simple.purs"); + let simple_source = std::fs::read_to_string(&simple_path).unwrap(); + let test_cases = parse_goto_def_comments(&simple_source, &fixture_dir); + assert!( + !test_cases.is_empty(), + "should find test cases in fixture comments" + ); + + let simple_uri = Url::from_file_path(&simple_path).unwrap().to_string(); + + // Start server with sources_cmd that loads fixture files + prelude + let sources_cmd = format!( + "echo '{}'; echo '{}'", + fixture_dir.join("**/*.purs").display(), + packages_dir.join("prelude/src/**/*.purs").display(), + ); + let mut server = TestServer::start_with_sources(Some(sources_cmd)).await; + + // Open Simple.purs so it's in self.files + server.open_file(&simple_uri, &simple_source).await; + + // Wait for source loading to complete by polling a known-good local definition. + // Line 6 col 6 = "fn" reference which should resolve to a local def. + let mut ready = false; + for _ in 0..100 { + let resp = server.goto_definition(99, &simple_uri, 6, 6).await; + let result = resp.get("result").unwrap(); + if !result.is_null() { + ready = true; + break; + } + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + } + assert!(ready, "server did not become ready within timeout"); + + // Run each test case + let mut id = 200u64; + let mut passed = 0; + let mut failed = 0; + + for case in &test_cases { + let resp = server + .goto_definition(id, &simple_uri, case.line, case.col) + .await; + let result = resp.get("result").unwrap(); + id += 1; + + match &case.expected { + GotoDefExpected::NoSource => { + if !result.is_null() { + eprintln!( + "FAIL {}:{} ({}) — expected null (Prim), got: {}", + case.line, case.col, case.name, result + ); + failed += 1; + } else { + passed += 1; + } + } + GotoDefExpected::Location { + uri: expected_uri, + start_line, + start_col, + end_line, + end_col, + } => { + if result.is_null() { + eprintln!( + "FAIL {}:{} ({}) — expected location at {expected_uri} {}:{}-{}:{}, got null", + case.line, case.col, case.name, start_line, start_col, end_line, end_col + ); + failed += 1; + continue; + } + + let result_uri = result + .get("uri") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let range = result.get("range").expect("should have range"); + let start = range.get("start").unwrap(); + let end = range.get("end").unwrap(); + + let got_start_line = start.get("line").unwrap().as_u64().unwrap() as u32; + let got_start_col = + start.get("character").unwrap().as_u64().unwrap() as u32; + let got_end_line = end.get("line").unwrap().as_u64().unwrap() as u32; + let got_end_col = end.get("character").unwrap().as_u64().unwrap() as u32; + + let mut case_ok = true; + + if result_uri != expected_uri { + eprintln!( + "FAIL {}:{} ({}) — wrong URI\n expected: {expected_uri}\n got: {result_uri}", + case.line, case.col, case.name + ); + case_ok = false; + } + + if got_start_line != *start_line + || got_start_col != *start_col + || got_end_line != *end_line + || got_end_col != *end_col + { + eprintln!( + "FAIL {}:{} ({}) — wrong range\n expected: {}:{}-{}:{}\n got: {}:{}-{}:{}", + case.line, case.col, case.name, + start_line, start_col, end_line, end_col, + got_start_line, got_start_col, got_end_line, got_end_col, + ); + case_ok = false; + } + + if case_ok { + passed += 1; + } else { + failed += 1; + } + } + } + } + + eprintln!( + "\nGoto definition fixture results: {passed} passed, {failed} failed out of {} total", + test_cases.len() + ); + + assert_eq!( + failed, 0, + "{failed} goto-definition test case(s) failed (see above)" + ); +} + +// --- Hover tests --- + +#[tokio::test] +async fn test_lsp_hover_simple_value() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfoo = 42"; + server.open_file(uri, src).await; + + let resp = server.hover(10, uri, 2, 0).await; + let result = resp.get("result").expect("should have result"); + + assert!(!result.is_null(), "hover result should not be null, got: {result}"); + let contents = result.get("contents").expect("should have contents"); + let value = contents.get("value").expect("should have value").as_str().unwrap(); + assert!(value.contains("foo"), "hover should contain name 'foo': {value}"); + assert!(value.contains("Int"), "hover should contain type 'Int': {value}"); +} + +#[tokio::test] +async fn test_lsp_hover_returns_null_on_whitespace() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfoo = 42"; + server.open_file(uri, src).await; + + let resp = server.hover(10, uri, 1, 0).await; + let result = resp.get("result").expect("should have result"); + assert!(result.is_null(), "hover on whitespace should be null, got: {result}"); +} + +#[tokio::test] +async fn test_lsp_hover_with_doc_comment() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\n-- | This is documented\nfoo = 42"; + server.open_file(uri, src).await; + + let resp = server.hover(10, uri, 3, 0).await; + let result = resp.get("result").expect("should have result"); + + assert!(!result.is_null(), "hover result should not be null, got: {result}"); + let contents = result.get("contents").expect("should have contents"); + let value = contents.get("value").expect("should have value").as_str().unwrap(); + assert!(value.contains("foo"), "hover should contain name: {value}"); + assert!(value.contains("Int"), "hover should contain type: {value}"); + assert!(value.contains("This is documented"), "hover should contain doc-comment: {value}"); +} + +#[tokio::test] +async fn test_lsp_hover_function_type() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfoo :: Int -> Int\nfoo x = x"; + server.open_file(uri, src).await; + + let resp = server.hover(10, uri, 3, 0).await; + let result = resp.get("result").expect("should have result"); + + assert!(!result.is_null(), "hover result should not be null, got: {result}"); + let contents = result.get("contents").expect("should have contents"); + let value = contents.get("value").expect("should have value").as_str().unwrap(); + assert!(value.contains("Int -> Int"), "hover should contain function type: {value}"); +} + +// --- Fixture-driven hover test --- + +struct HoverTestCase { + line: u32, + col: u32, + name: String, + expected: HoverExpected, +} + +enum HoverExpected { + Null, + Contains { + type_substr: String, + doc_substr: Option, + }, +} + +/// Parse test comments from a hover fixture file. +/// Format: `-- line:col (name) => hover: ` +/// Or: `-- line:col (name) => hover: | doc: ` +/// Or: `-- line:col (name) => hover: null` +fn parse_hover_comments(source: &str) -> Vec { + let re = Regex::new(r"^-- (\d+):(\d+) \(([^)]+)\) => hover: (.+)$").unwrap(); + let mut cases = Vec::new(); + + for line in source.lines() { + let line = line.trim(); + let Some(caps) = re.captures(line) else { + continue; + }; + + let test_line: u32 = caps[1].parse().unwrap(); + let test_col: u32 = caps[2].parse().unwrap(); + let name = caps[3].to_string(); + let target = caps[4].trim(); + + let expected = if target == "null" { + HoverExpected::Null + } else if let Some((type_part, doc_part)) = target.split_once(" | doc: ") { + HoverExpected::Contains { + type_substr: type_part.trim().to_string(), + doc_substr: Some(doc_part.trim().to_string()), + } + } else { + HoverExpected::Contains { + type_substr: target.to_string(), + doc_substr: None, + } + }; + + cases.push(HoverTestCase { + line: test_line, + col: test_col, + name, + expected, + }); + } + + cases +} + +#[tokio::test] +async fn test_lsp_hover_fixture() { + let fixture_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/lsp/hover"), + ) + .unwrap(); + + let packages_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/packages"), + ) + .unwrap(); + + let simple_path = fixture_dir.join("Simple.purs"); + let simple_source = std::fs::read_to_string(&simple_path).unwrap(); + let test_cases = parse_hover_comments(&simple_source); + assert!( + !test_cases.is_empty(), + "should find test cases in fixture comments" + ); + + let simple_uri = Url::from_file_path(&simple_path).unwrap().to_string(); + + let sources_cmd = format!( + "echo '{}'; echo '{}'", + fixture_dir.join("**/*.purs").display(), + packages_dir.join("prelude/src/**/*.purs").display(), + ); + let mut server = TestServer::start_with_sources(Some(sources_cmd)).await; + + server.open_file(&simple_uri, &simple_source).await; + + // Wait for source loading by polling a known-good hover + // Line 5 col 0 = "x" which should have type Int + let mut ready = false; + for _ in 0..100 { + let resp = server.hover(99, &simple_uri, 5, 0).await; + let result = resp.get("result").unwrap(); + if !result.is_null() { + ready = true; + break; + } + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + } + assert!(ready, "server did not become ready within timeout"); + + let mut id = 200u64; + let mut passed = 0; + let mut failed = 0; + + for case in &test_cases { + let resp = server + .hover(id, &simple_uri, case.line, case.col) + .await; + let result = resp.get("result").unwrap(); + id += 1; + + match &case.expected { + HoverExpected::Null => { + if !result.is_null() { + eprintln!( + "FAIL {}:{} ({}) — expected null, got: {}", + case.line, case.col, case.name, result + ); + failed += 1; + } else { + passed += 1; + } + } + HoverExpected::Contains { type_substr, doc_substr } => { + if result.is_null() { + eprintln!( + "FAIL {}:{} ({}) — expected hover containing '{}', got null", + case.line, case.col, case.name, type_substr + ); + failed += 1; + continue; + } + + let contents = result.get("contents").expect("should have contents"); + let value = contents + .get("value") + .and_then(|v| v.as_str()) + .unwrap_or(""); + + let mut case_ok = true; + + if !value.contains(type_substr.as_str()) { + eprintln!( + "FAIL {}:{} ({}) — hover does not contain '{}'\n got: {}", + case.line, case.col, case.name, type_substr, value + ); + case_ok = false; + } + + if let Some(doc) = doc_substr { + if !value.contains(doc.as_str()) { + eprintln!( + "FAIL {}:{} ({}) — hover does not contain doc '{}'\n got: {}", + case.line, case.col, case.name, doc, value + ); + case_ok = false; + } + } + + if case_ok { + passed += 1; + } else { + failed += 1; + } + } + } + } + + eprintln!( + "\nHover fixture results: {passed} passed, {failed} failed out of {} total", + test_cases.len() + ); + + assert_eq!( + failed, 0, + "{failed} hover test case(s) failed (see above)" + ); +} + +// --- Completion tests --- + +#[tokio::test] +async fn test_lsp_completion_local_values() { + let mut server = TestServer::start().await; + + let uri = "file:///test/Test.purs"; + let src = "module Test where\n\nfooBar = 1\n\nfooBaz = 2\n\nresult = foo"; + server.open_file(uri, src).await; + + // Cursor at end of "foo" on line 6 (0-indexed), column 12 + let resp = server.completion(10, uri, 6, 12).await; + let result = resp.get("result").expect("should have result"); + + assert!(!result.is_null(), "completion result should not be null, got: {result}"); + let items = result.get("items").expect("should have items"); + let items = items.as_array().expect("items should be array"); + + let labels: Vec<&str> = items.iter().filter_map(|i| i.get("label").and_then(|l| l.as_str())).collect(); + assert!(labels.contains(&"fooBar"), "should contain fooBar, got: {labels:?}"); + assert!(labels.contains(&"fooBaz"), "should contain fooBaz, got: {labels:?}"); +} + +#[tokio::test] +async fn test_lsp_completion_cross_module_with_auto_import() { + let fixture_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/lsp/hover"), + ) + .unwrap(); + + let packages_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/packages"), + ) + .unwrap(); + + let sources_cmd = format!( + "echo '{}'; echo '{}'", + fixture_dir.join("**/*.purs").display(), + packages_dir.join("prelude/src/**/*.purs").display(), + ); + let mut server = TestServer::start_with_sources(Some(sources_cmd)).await; + + let uri = "file:///test/Comp.purs"; + let src = "module Comp where\n\nresult = times"; + server.open_file(uri, src).await; + + // Wait for source loading + let mut ready = false; + for _ in 0..100 { + let resp = server.completion(99, uri, 2, 14).await; + let result = resp.get("result").unwrap(); + if !result.is_null() { + if let Some(items) = result.get("items").and_then(|i| i.as_array()) { + if !items.is_empty() { + ready = true; + break; + } + } + } + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + } + assert!(ready, "server did not return completions within timeout"); + + let resp = server.completion(100, uri, 2, 14).await; + let result = resp.get("result").unwrap(); + let items = result.get("items").unwrap().as_array().unwrap(); + + // Should find "times2" from Simple.Lib + let times2_item = items.iter().find(|i| { + i.get("label").and_then(|l| l.as_str()) == Some("times2") + }); + assert!(times2_item.is_some(), "should find times2 in completions, got labels: {:?}", + items.iter().filter_map(|i| i.get("label").and_then(|l| l.as_str())).collect::>()); + + let times2_item = times2_item.unwrap(); + + // Should show module and type in detail + let detail = times2_item.get("detail").and_then(|d| d.as_str()).unwrap_or(""); + assert!(detail.contains("Simple.Lib"), "detail should contain module name, got: {detail}"); + + // Should have auto-import edit + let edits = times2_item.get("additionalTextEdits"); + assert!(edits.is_some(), "should have additionalTextEdits for auto-import"); + let edits = edits.unwrap().as_array().unwrap(); + assert!(!edits.is_empty(), "additionalTextEdits should not be empty"); + + let edit_text = edits[0].get("newText").and_then(|t| t.as_str()).unwrap_or(""); + assert!(edit_text.contains("import Simple.Lib"), "auto-import should import Simple.Lib, got: {edit_text}"); + assert!(edit_text.contains("times2"), "auto-import should include times2, got: {edit_text}"); +} + +#[tokio::test] +async fn test_lsp_completion_already_imported_no_auto_import() { + let fixture_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/lsp/hover"), + ) + .unwrap(); + + let packages_dir = std::fs::canonicalize( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/packages"), + ) + .unwrap(); + + let sources_cmd = format!( + "echo '{}'; echo '{}'", + fixture_dir.join("**/*.purs").display(), + packages_dir.join("prelude/src/**/*.purs").display(), + ); + let mut server = TestServer::start_with_sources(Some(sources_cmd)).await; + + let uri = "file:///test/Comp2.purs"; + let src = "module Comp2 where\n\nimport Simple.Lib (times2)\n\nresult = times"; + server.open_file(uri, src).await; + + // Wait for source loading + let mut ready = false; + for _ in 0..100 { + let resp = server.completion(99, uri, 4, 14).await; + let result = resp.get("result").unwrap(); + if !result.is_null() { + if let Some(items) = result.get("items").and_then(|i| i.as_array()) { + if !items.is_empty() { + ready = true; + break; + } + } + } + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + } + assert!(ready, "server did not return completions within timeout"); + + let resp = server.completion(100, uri, 4, 14).await; + let result = resp.get("result").unwrap(); + let items = result.get("items").unwrap().as_array().unwrap(); + + let times2_item = items.iter().find(|i| { + i.get("label").and_then(|l| l.as_str()) == Some("times2") + }); + assert!(times2_item.is_some(), "should find times2 in completions"); + + let times2_item = times2_item.unwrap(); + + // Already imported — should NOT have additionalTextEdits + let edits = times2_item.get("additionalTextEdits"); + let has_edits = edits.map_or(false, |e| { + e.as_array().map_or(false, |a| !a.is_empty()) + }); + assert!(!has_edits, "already-imported value should not have auto-import edits"); +} diff --git a/tests/resolve.rs b/tests/resolve.rs index 50dfd9cb..4addab93 100644 --- a/tests/resolve.rs +++ b/tests/resolve.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use purescript_fast_compiler::cst::Module; use purescript_fast_compiler::parser; -use purescript_fast_compiler::typechecker::resolve::{resolve_names, ResolutionExports}; +use purescript_fast_compiler::lsp::utils::resolve::{resolve_names, ResolutionExports}; /// Support packages from tests/fixtures/packages used by the original compiler tests. /// Same list as in tests/build.rs. diff --git a/tests/snapshots.rs b/tests/snapshots.rs index fb2be9aa..6b312db6 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -86,7 +86,7 @@ fn snap_expr_let_polymorphism() { #[test] fn snap_expr_array() { - insta::assert_snapshot!(format_expr_type("[1, 2, 3]"), @"(Array Int)"); + insta::assert_snapshot!(format_expr_type("[1, 2, 3]"), @"Array Int"); } #[test] diff --git a/tests/snapshots/snapshots__snap_expr_lambda_identity.snap b/tests/snapshots/snapshots__snap_expr_lambda_identity.snap index 91384bed..b62d7df3 100644 --- a/tests/snapshots/snapshots__snap_expr_lambda_identity.snap +++ b/tests/snapshots/snapshots__snap_expr_lambda_identity.snap @@ -2,4 +2,4 @@ source: tests/snapshots.rs expression: ty --- -(?0 -> ?0) +?0 -> ?0 diff --git a/tests/snapshots/snapshots__snap_module_data_constructors.snap b/tests/snapshots/snapshots__snap_module_data_constructors.snap index 119a488c..c3490192 100644 --- a/tests/snapshots/snapshots__snap_module_data_constructors.snap +++ b/tests/snapshots/snapshots__snap_module_data_constructors.snap @@ -2,5 +2,5 @@ source: tests/snapshots.rs expression: "format_type_map(\"module T where\ndata Maybe a = Just a | Nothing\nx = Just 42\ny = Nothing\")" --- -x :: (Maybe Int) -y :: (Maybe ?6) +x :: Maybe Int +y :: Maybe ?6 diff --git a/tests/snapshots/snapshots__snap_module_function_and_application.snap b/tests/snapshots/snapshots__snap_module_function_and_application.snap index a8562745..f1284e6b 100644 --- a/tests/snapshots/snapshots__snap_module_function_and_application.snap +++ b/tests/snapshots/snapshots__snap_module_function_and_application.snap @@ -2,5 +2,5 @@ source: tests/snapshots.rs expression: "format_type_map(\"module T where\nf x = x\ng = f 42\")" --- -f :: (?3 -> ?3) +f :: ?3 -> ?3 g :: Int diff --git a/tests/snapshots/snapshots__snap_parse_module_data.snap b/tests/snapshots/snapshots__snap_parse_module_data.snap index f7d80856..d12db7c4 100644 --- a/tests/snapshots/snapshots__snap_parse_module_data.snap +++ b/tests/snapshots/snapshots__snap_parse_module_data.snap @@ -59,6 +59,7 @@ expression: "resolve_symbols_in_debug(&format!(\"{:#?}\", module.decls))" }, }, ], + doc_comments: [], }, DataConstructor { span: Span { @@ -87,6 +88,7 @@ expression: "resolve_symbols_in_debug(&format!(\"{:#?}\", module.decls))" }, }, ], + doc_comments: [], }, ], kind_sig: None, @@ -96,5 +98,6 @@ expression: "resolve_symbols_in_debug(&format!(\"{:#?}\", module.decls))" None, None, ], + doc_comments: [], }, ]