purescript-bridge 0.1.0.0 → 0.3.0.0
raw patch · 16 files changed
+525/−393 lines, 16 filesdep +directory
Dependencies added: directory
Files
- dist/build/testsStub/testsStub-tmp/testsStub.hs +0/−5
- purescript-bridge.cabal +11/−8
- src/Language/PureScript/Bridge.hs +98/−0
- src/Language/PureScript/Bridge/Primitives.hs +64/−0
- src/Language/PureScript/Bridge/Printer.hs +111/−0
- src/Language/PureScript/Bridge/SumType.hs +82/−0
- src/Language/PureScript/Bridge/Tuple.hs +39/−0
- src/Language/PureScript/Bridge/TypeInfo.hs +45/−0
- src/Language/PureScript/Bridge/TypeParameters.hs +74/−0
- src/PureScript/Bridge.hs +0/−56
- src/PureScript/Bridge/Primitives.hs +0/−55
- src/PureScript/Bridge/Printer.hs +0/−100
- src/PureScript/Bridge/SumType.hs +0/−85
- src/PureScript/Bridge/Tuple.hs +0/−39
- src/PureScript/Bridge/TypeInfo.hs +0/−44
- test/Tests.hs +1/−1
− dist/build/testsStub/testsStub-tmp/testsStub.hs
@@ -1,5 +0,0 @@-module Main ( main ) where-import Distribution.Simple.Test.LibV09 ( stubMain )-import Tests ( tests )-main :: IO ()-main = stubMain tests
purescript-bridge.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.0.0+version: 0.3.0.0 -- A short (one-line) description of the package. synopsis: Generate PureScript data types from Haskell data types@@ -51,12 +51,13 @@ library -- Modules exported by the library.- exposed-modules: PureScript.Bridge.SumType- , PureScript.Bridge.TypeInfo- , PureScript.Bridge.Tuple- , PureScript.Bridge.Primitives- , PureScript.Bridge- , PureScript.Bridge.Printer+ exposed-modules: Language.PureScript.Bridge.SumType+ , Language.PureScript.Bridge.TypeInfo+ , Language.PureScript.Bridge.Tuple+ , Language.PureScript.Bridge.Primitives+ , Language.PureScript.Bridge+ , Language.PureScript.Bridge.Printer+ , Language.PureScript.Bridge.TypeParameters -- Modules included in this library but not exported. -- other-modules:@@ -67,6 +68,7 @@ -- Other library packages from which modules are imported. build-depends: base >=4.8 && <4.9 , containers+ , directory , filepath , text , generic-deriving@@ -82,7 +84,8 @@ Test-Suite tests type: detailed-0.9 test-module: Tests- build-depends: base, Cabal >= 1.9.2+ build-depends: base+ , Cabal >= 1.9.2 , HUnit , purescript-bridge hs-source-dirs: test
+ src/Language/PureScript/Bridge.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE OverloadedStrings #-}+module Language.PureScript.Bridge (+ bridgeSumType+ , defaultBridge+ , module Bridge+ , writePSTypes+ ) where+++import qualified Data.Text as T++import Language.PureScript.Bridge.SumType as Bridge+import Language.PureScript.Bridge.TypeInfo as Bridge+import Language.PureScript.Bridge.Tuple as Bridge+import Language.PureScript.Bridge.Primitives as Bridge+import Language.PureScript.Bridge.Printer as Bridge++++import Control.Applicative+import qualified Data.Map as M+import Data.Maybe++-- | Your entry point to this library and quite likely all you will need.+-- | Make sure all your types derive Generic and Typeable.+-- | Typeable is not needed from ghc-7.10 on.+-- | Then call 'writePSTypes' like this:+-- | @+-- | let myTypes = [+-- | 'toSumType' ('Proxy' :: 'Proxy' MyType1)+-- | , 'toSumType' ('Proxy' :: 'Proxy' MyType2)+-- | ]+-- | 'writePSTypes' 'defaultBridge' "path/to/you/purescript/project" myTypes+-- | @+-- | You can add new type mappings, like this:+-- | @+-- | myBridge = 'defaultBridge' <|> mySpecialTypeBridge+-- | @+-- | Find examples for implementing your own type bridges in: 'Language.PureScript.Bridge.Primitives'+-- | A real world use case of this library can be found <https://github.com/gonimo/gonimo-back/blob/master/src/MkFrontendTypes.hs here>.+-- | Last but not least:+-- | WARNING: This function overwrites files - make backups or use version control!+writePSTypes :: TypeBridge -> FilePath -> [SumType] -> IO ()+writePSTypes br root sts = do+ let bridged = map (bridgeSumType br) sts+ let modules = M.elems $ sumTypesToModules M.empty bridged+ mapM_ (printModule root) modules++bridgeSumType :: TypeBridge -> SumType -> SumType+bridgeSumType br (SumType t cs) = SumType t $ map (bridgeConstructor br) cs++{--+ -- Optimistically and recursively translate types: If the passed TypeBridge returns Nothing,+ -- then the original TypeInfo is returned with the typePackage field cleared.+ -- You don't need to call this function directly, just use bridgeSumType with your TypeBridge+--}+doBridge :: TypeBridge -> TypeInfo -> TypeInfo+doBridge br info = let+ translated = info { typePackage = "" }+ res = fixTypeParameters $ fromMaybe translated (br info)+ in+ res {+ typeParameters = map (doBridge br) . typeParameters $ res+ }++-- | Default bridge for mapping primitive/common types:+-- | You can append your own bridges like this:+-- | defaultBridge <|> myBridge1 <|> myBridge2+defaultBridge :: TypeBridge+defaultBridge t = stringBridge t+ <|> listBridge t+ <|> maybeBridge t+ <|> eitherBridge t+ <|> boolBridge t+ <|> intBridge t+ <|> tupleBridge t++bridgeConstructor :: TypeBridge -> DataConstructor -> DataConstructor+bridgeConstructor br (DataConstructor name (Left infos)) =+ DataConstructor name . Left $ map (doBridge br) infos+bridgeConstructor br (DataConstructor name (Right record)) =+ DataConstructor name . Right $ map (bridgeRecordEntry br) record++bridgeRecordEntry :: TypeBridge -> RecordEntry -> RecordEntry+bridgeRecordEntry br (RecordEntry label value) = RecordEntry label $ doBridge br value++-- | Translate types that come from any module named "Something.TypeParameters" to lower case:+-- | Also drop the 1 at the end if present+fixTypeParameters :: TypeInfo -> TypeInfo+fixTypeParameters t+ | T.isSuffixOf "TypeParameters" (typeModule t) = t {+ typePackage = "" -- Don't suggest any packages+ , typeModule = "" -- Don't import any modules+ , typeName = stripNum . T.toLower $ typeName t+ }+ | otherwise = t+ where+ stripNum v = fromMaybe v (T.stripSuffix "1" v)
+ src/Language/PureScript/Bridge/Primitives.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}+module Language.PureScript.Bridge.Primitives where+++import Language.PureScript.Bridge.TypeInfo++boolBridge :: TypeBridge+boolBridge t+ | eqTypeName "Bool" t = Just $ t {+ typePackage = "purescript-prim"+ , typeModule = "Prim"+ , typeName = "Boolean"+ , typeParameters = []+ }+ | otherwise = Nothing++intBridge :: TypeBridge+intBridge t+ | eqTypeName "Int" t = Just $ t {+ typePackage = "purescript-prim"+ , typeModule = "Prim"+ }+ | otherwise = Nothing+++stringBridge :: TypeBridge+stringBridge t+ | isStringLike = Just $ t {+ typePackage = "purescript-prim"+ , typeModule = "Prim"+ , typeName = "String"+ , typeParameters = []+ }+ | otherwise = Nothing+ where+ isStringLike = isText || isString+ isText = typeName t == "Text"+ isString = typeName t == "[]" && all ((==) "Char" . typeName) (typeParameters t)++listBridge :: TypeBridge+listBridge t+ | eqTypeName "[]" t = Just $ t {+ typePackage = "purescript-prim"+ , typeModule = "Prim"+ , typeName = "Array"+ }+ | otherwise = Nothing++maybeBridge :: TypeBridge+maybeBridge t+ | eqTypeName "Maybe" t = Just $ t {+ typePackage = "purescript-maybe"+ , typeModule = "Data.Maybe"+ }+ | otherwise = Nothing+++eitherBridge :: TypeBridge+eitherBridge t+ | eqTypeName "Either" t = Just $ t {+ typePackage = "purescript-either"+ , typeModule = "Data.Either"+ }+ | otherwise = Nothing
+ src/Language/PureScript/Bridge/Printer.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE OverloadedStrings #-}+module Language.PureScript.Bridge.Printer where++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as M+import Data.Monoid+import Data.Set (Set)+import qualified Data.Set as S+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import System.Directory+import System.FilePath+import Control.Monad+++import Language.PureScript.Bridge.SumType+import Language.PureScript.Bridge.TypeInfo+++data PSModule = PSModule {+ psModuleName :: !Text+, psImportLines :: !(Map Text ImportLine)+, psTypes :: ![SumType]+} deriving Show++data ImportLine = ImportLine {+ importModule :: !Text+, importTypes :: !(Set Text)+} deriving Show++type Modules = Map Text PSModule+type ImportLines = Map Text ImportLine++printModule :: FilePath -> PSModule -> IO ()+printModule root m = do+ unlessM (doesDirectoryExist mDir) $ createDirectoryIfMissing True mDir+ T.writeFile mPath . moduleToText $ m+ where+ mFile = (joinPath . map T.unpack . T.splitOn "." $ psModuleName m) <> ".purs"+ mPath = root </> mFile+ mDir = takeDirectory mPath++moduleToText :: PSModule -> Text+moduleToText m = T.unlines $+ "module " <> psModuleName m <> " where\n"+ : map importLineToText (M.elems (psImportLines m))+ ++ [ "\nimport Data.Generic (class Generic)\n\n" ]+ ++ map sumTypeToText (psTypes m)+++importLineToText :: ImportLine -> Text+importLineToText l = "import " <> importModule l <> " (" <> typeList <> ")"+ where+ typeList = T.intercalate ", " (S.toList (importTypes l))++sumTypeToText :: SumType -> Text+sumTypeToText (SumType t cs) = T.unlines $+ "data " <> typeName t <> " ="+ : [ " " <> T.intercalate "\n | " (map (constructorToText 4) cs) ]+ ++ [ "\nderive instance generic" <> typeName t <> " :: Generic " <> typeName t <> "\n" ]+++constructorToText :: Int -> DataConstructor -> Text+constructorToText _ (DataConstructor n (Left ts)) = n <> " " <> T.intercalate " " (map typeInfoToText ts)+constructorToText indentation (DataConstructor n (Right rs)) = T.unlines $+ n <> " {"+ : [ spaces (indentation + 2) <> T.intercalate intercalation (map recordEntryToText rs) ]+ ++ [ spaces indentation <> "}" ]+ where+ intercalation = "\n" <> spaces indentation <> "," <> " "+ spaces c = T.replicate c " "++recordEntryToText :: RecordEntry -> Text+recordEntryToText e = recLabel e <> " :: " <> typeInfoToText (recValue e)+++typeInfoToText :: TypeInfo -> Text+typeInfoToText t = if length textParameters > 1 then "(" <> inner <> ")" else inner+ where+ inner = typeName t <> T.intercalate " " textParameters+ textParameters = map typeInfoToText (typeParameters t)++sumTypesToModules :: Modules -> [SumType] -> Modules+sumTypesToModules = foldr sumTypeToModule++sumTypeToModule :: SumType -> Modules -> Modules+sumTypeToModule st@(SumType t _) = M.alter (Just. updateModule) (typeModule t)+ where+ updateModule Nothing = PSModule {+ psModuleName = typeModule t+ , psImportLines = dropSelf $ typesToImportLines M.empty (getUsedTypes st)+ , psTypes = [st]+ }+ updateModule (Just m) = m {+ psImportLines = dropSelf $ typesToImportLines (psImportLines m) (getUsedTypes st)+ , psTypes = st : psTypes m+ }+ dropSelf = M.delete (typeModule t)++typesToImportLines :: ImportLines -> [TypeInfo] -> ImportLines+typesToImportLines = foldr typeToImportLines++typeToImportLines :: TypeInfo -> ImportLines -> ImportLines+typeToImportLines t = M.alter (Just . updateLine) (typeModule t)+ where+ updateLine Nothing = ImportLine (typeModule t) (S.singleton (typeName t))+ updateLine (Just (ImportLine m types)) = ImportLine m $ S.insert (typeName t) types++unlessM :: Monad m => m Bool -> m () -> m ()+unlessM mbool action = mbool >>= flip unless action
+ src/Language/PureScript/Bridge/SumType.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeSynonymInstances #-}+++{-# LANGUAGE DeriveGeneric #-}+++module Language.PureScript.Bridge.SumType where++import Generics.Deriving+import Data.Text (Text)+import qualified Data.Text as T+import Data.Proxy+import Data.Typeable++import Language.PureScript.Bridge.TypeInfo++data SumType = SumType TypeInfo [DataConstructor] deriving Show++data DataConstructor = DataConstructor {+ sigConstructor :: !Text+, sigValues :: !(Either [TypeInfo] [RecordEntry])+} deriving Show++data RecordEntry = RecordEntry {+ recLabel :: !Text+, recValue :: !TypeInfo+} deriving Show++toSumType :: forall t. (Generic t, Typeable t, GDataConstructor (Rep t)) => Proxy t -> SumType+toSumType p = SumType (mkTypeInfo p) constructors+ where+ constructors = gToConstructors (from (undefined :: t))++class GDataConstructor f where+ gToConstructors :: f a -> [DataConstructor]++class GRecordEntry f where+ gToRecordEntries :: f a -> [RecordEntry]++instance (Datatype a, GDataConstructor c) => GDataConstructor (D1 a c) where+ gToConstructors (M1 c) = gToConstructors c++instance (GDataConstructor a, GDataConstructor b) => GDataConstructor (a :+: b) where+ gToConstructors (_ :: (a :+: b) f) = gToConstructors (undefined :: a f) ++ gToConstructors (undefined :: b f)++instance (Constructor a, GRecordEntry b) => GDataConstructor (C1 a b) where+ gToConstructors c@(M1 r) = [+ DataConstructor {+ sigConstructor = constructor+ , sigValues = values+ }+ ]+ where+ constructor = T.pack $ conName c+ values = if conIsRecord c+ then Right $ gToRecordEntries r+ else Left $ map recValue $ gToRecordEntries r++instance (GRecordEntry a, GRecordEntry b) => GRecordEntry (a :*: b) where+ gToRecordEntries (_ :: (a :*: b) f) = gToRecordEntries (undefined :: a f) ++ gToRecordEntries (undefined :: b f)+++instance GRecordEntry U1 where+ gToRecordEntries _ = []++instance (Selector a, Typeable t) => GRecordEntry (S1 a (K1 R t)) where+ gToRecordEntries e = [+ RecordEntry { recLabel = T.pack (selName e)+ , recValue = mkTypeInfo (Proxy :: Proxy t)+ }+ ]++getUsedTypes :: SumType -> [TypeInfo]+getUsedTypes (SumType _ cs) = foldr constructorToType [] cs++constructorToType :: DataConstructor -> [TypeInfo] -> [TypeInfo]+constructorToType (DataConstructor _ (Left myTs)) ts = concatMap flattenTypeInfo myTs ++ ts+constructorToType (DataConstructor _ (Right rs)) ts = concatMap (flattenTypeInfo . recValue) rs ++ ts
+ src/Language/PureScript/Bridge/Tuple.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE OverloadedStrings #-}+module Language.PureScript.Bridge.Tuple where++import Data.Monoid+import Data.Text (Text)+import qualified Data.Text as T+++import Language.PureScript.Bridge.TypeInfo++data TupleParserState =+ Start | OpenFound | ColonFound | Tuple | NoTuple deriving (Eq, Show)+++tupleBridge :: TypeBridge+tupleBridge t+ | isTuple (typeName t) = Just $ t {+ typePackage = "purescript-tuples"+ , typeModule = if size == 2 then "Data.Tuple" else "Data.Tuple.Nested"+ , typeName = "Tuple" <> if size == 2 then "" else T.pack (show size)+ }+ | otherwise = Nothing+ where+ size = length $ typeParameters t+++step :: TupleParserState -> Char -> TupleParserState+step Start '(' = OpenFound+step Start _ = NoTuple+step OpenFound ',' = ColonFound+step OpenFound _ = NoTuple+step ColonFound ',' = ColonFound+step ColonFound ')' = Tuple+step ColonFound _ = NoTuple+step Tuple _ = NoTuple+step NoTuple _ = NoTuple++isTuple :: Text -> Bool+isTuple = (== Tuple) . T.foldl' step Start
+ src/Language/PureScript/Bridge/TypeInfo.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE OverloadedStrings #-}++module Language.PureScript.Bridge.TypeInfo where+++import Data.Text (Text)+import qualified Data.Text as T+import Data.Proxy+import Data.Typeable+++-- Translates a Haskell type info to a PureScript type info:+type TypeBridge = TypeInfo -> Maybe TypeInfo+++-- Basic info about a data type:+data TypeInfo = TypeInfo {+ -- | Hackage package+ typePackage :: !Text+ -- | Full Module path+, typeModule :: !Text+, typeName :: !Text+, typeParameters :: ![TypeInfo]+} deriving (Eq, Show)++mkTypeInfo :: Typeable t => Proxy t -> TypeInfo+mkTypeInfo = mkTypeInfo' . typeRep++mkTypeInfo' :: TypeRep -> TypeInfo+mkTypeInfo' rep = let+ con = typeRepTyCon rep+ in TypeInfo {+ typePackage = T.pack $ tyConPackage con+ , typeModule = T.pack $ tyConModule con+ , typeName = T.pack $ tyConName con+ , typeParameters = map mkTypeInfo' (typeRepArgs rep)+ }++-- | Put the TypeInfo in a list together with all its typeParameters (recursively)+flattenTypeInfo :: TypeInfo -> [TypeInfo]+flattenTypeInfo t = t : concatMap flattenTypeInfo (typeParameters t)++-- Little helper for type bridge implementers+eqTypeName :: Text -> TypeInfo -> Bool+eqTypeName name = (== name) . typeName
+ src/Language/PureScript/Bridge/TypeParameters.hs view
@@ -0,0 +1,74 @@+-- | As we translate types and not type constructors, we have to pass dummy types+-- | to any type constructor. doBridge will translate all parameter types which+-- | come from a module TypeParameters (e.g. this one) to lower case.+-- | E.g. for translating something like Maybe:+-- | @+-- | data Maybe' a = Nothing' | Just' a+-- | @+-- | you would use:+-- | @+-- | import 'Language.PureScript.Bridge'+-- | import 'Language.PureScript.Bridge.TypeParameters'+-- | toSumType (Proxy :: Proxy (Maybe A)) -- Note the capital A, which comes from the TypeParameters module.+-- | @++module Language.PureScript.Bridge.TypeParameters where+++data A+data B+data C+data D+data E+data F+data G+data H+data I+data J+data K+data L+data M+data N+data O+data P+data Q+data R+data S+data T+data U+data V+data W+data X+data Y+data Z++-- | You can use those if your type parameters are actually type constructors as well:+-- | @+-- | toSumType (Proxy :: Proxy ('ReaderT' R M1 A))+-- | @+data A1 a+data B1 a+data C1 a+data D1 a+data E1 a+data F1 a+data G1 a+data H1 a+data I1 a+data J1 a+data K1 a+data L1 a+data M1 a+data N1 a+data O1 a+data P1 a+data Q1 a+data R1 a+data S1 a+data T1 a+data U1 a+data V1 a+data W1 a+data X1 a+data Y1 a+data Z1 a
− src/PureScript/Bridge.hs
@@ -1,56 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module PureScript.Bridge (- bridgeSumType- , defaultBridge- ) where---import Data.Text (Text)-import qualified Data.Text as T-import Data.Monoid--import PureScript.Bridge.SumType-import PureScript.Bridge.TypeInfo-import PureScript.Bridge.Tuple-import PureScript.Bridge.Primitives---import Control.Applicative-import Data.Maybe--bridgeSumType :: TypeBridge -> SumType -> SumType-bridgeSumType br (SumType t cs) = SumType t . map (bridgeConstructor br) $ cs--{--- -- Optimistically and recursively translate types: If the passed TypeBridge returns Nothing,- -- then the original TypeInfo is returned with the typePackage field cleared.- -- You don't need to call this function directly, just use bridgeSumType with your TypeBridge---}-doBridge :: TypeBridge -> TypeInfo -> TypeInfo-doBridge br info = let- translated = info { typePackage = "" }- res = fromMaybe translated (br info)- in- res {- typeParameters = map (doBridge br) . typeParameters $ res- }---- | Default bridge for mapping primitive/common types:--- | You can append your own bridges like this:--- | defaultBridge <|> myBridge1 <|> myBridge2-defaultBridge :: TypeBridge-defaultBridge t = stringBridge t- <|> listBridge t- <|> maybeBridge t- <|> eitherBridge t- <|> intBridge t- <|> tupleBridge t--bridgeConstructor :: TypeBridge -> DataConstructor -> DataConstructor-bridgeConstructor br (DataConstructor name (Left infos)) =- DataConstructor name . Left $ map (doBridge br) infos-bridgeConstructor br (DataConstructor name (Right record)) =- DataConstructor name . Right $ map (bridgeRecordEntry br) record--bridgeRecordEntry :: TypeBridge -> RecordEntry -> RecordEntry-bridgeRecordEntry br (RecordEntry label value) = RecordEntry label $ doBridge br value
− src/PureScript/Bridge/Primitives.hs
@@ -1,55 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module PureScript.Bridge.Primitives where---import PureScript.Bridge.TypeInfo---intBridge :: TypeBridge-intBridge t- | eqTypeName "Int" t = Just $ t {- typePackage = "purescript-prim"- , typeModule = "Prim"- }- | otherwise = Nothing---stringBridge :: TypeBridge-stringBridge t- | isStringLike = Just $ t {- typePackage = "purescript-prim"- , typeModule = "Prim"- , typeName = "String"- , typeParameters = []- }- | otherwise = Nothing- where- isStringLike = isText || isString- isText = typeName t == "Text" && typePackage t == "text"- isString = typeName t == "[]" && all ((==) "Char" . typeName) (typeParameters t)--listBridge :: TypeBridge-listBridge t- | eqTypeName "[]" t = Just $ t {- typePackage = "purescript-prim"- , typeModule = "Prim"- , typeName = "Array"- }- | otherwise = Nothing--maybeBridge :: TypeBridge-maybeBridge t- | eqTypeName "Maybe" t = Just $ t {- typePackage = "purescript-maybe"- , typeModule = "Data.Maybe"- }- | otherwise = Nothing---eitherBridge :: TypeBridge-eitherBridge t- | eqTypeName "Either" t = Just $ t {- typePackage = "purescript-either"- , typeModule = "Data.Either"- }- | otherwise = Nothing
− src/PureScript/Bridge/Printer.hs
@@ -1,100 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module PureScript.Bridge.Printer where--import Data.Map.Strict (Map)-import qualified Data.Map.Strict as M-import Data.Monoid-import Data.Set (Set)-import qualified Data.Set as S-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.IO as T-import System.FilePath---import PureScript.Bridge.SumType-import PureScript.Bridge.TypeInfo---data PSModule = PSModule {- psModuleName :: Text-, psImportLines :: Map Text ImportLine-, psTypes :: [SumType]-} deriving Show--data ImportLine = ImportLine {- importModule :: Text-, importTypes :: Set Text-} deriving Show--type Modules = Map Text PSModule-type ImportLines = Map Text ImportLine--printModule :: FilePath -> PSModule -> IO ()-printModule root m = T.writeFile mPath . moduleToText $ m- where- mFile = joinPath . map T.unpack . T.splitOn "." $ psModuleName m- mPath = root </> mFile--moduleToText :: PSModule -> Text-moduleToText m = T.unlines $- "module " <> psModuleName m <> "where\n"- : map importLineToText (M.elems (psImportLines m))- ++ [ "\n\n" ]- ++ map sumTypeToText (psTypes m)---importLineToText :: ImportLine -> Text-importLineToText l = "import " <> importModule l <> "(" <> typeList <> ")"- where- typeList = T.intercalate ", " (S.toList (importTypes l))--sumTypeToText :: SumType -> Text-sumTypeToText (SumType t cs) = T.unlines $- "data " <> typeName t <> "="- : [ " " <> T.intercalate " | " (map (constructorToText 4) cs) ]--constructorToText :: Int -> DataConstructor -> Text-constructorToText _ (DataConstructor n (Left ts)) = n <> T.intercalate " " (map typeInfoToText ts)-constructorToText indentation (DataConstructor n (Right rs)) = T.unlines $- n <> " {"- : [spaces (indentation + 2) <> T.intercalate intercalation (map recordEntryToText rs)]- ++ [ spaces indentation <> "}" ]- where- intercalation = "\n" <> spaces indentation <> "," <> spaces 2- spaces c = T.replicate c " "--recordEntryToText :: RecordEntry -> Text-recordEntryToText e = recLabel e <> " :: " <> typeInfoToText (recValue e)---typeInfoToText :: TypeInfo -> Text-typeInfoToText t = if length textParameters > 1 then "(" <> inner <> ")" else inner- where- inner = typeName t <> T.intercalate " " textParameters- textParameters = map typeInfoToText (typeParameters t)--sumTypesToModules :: Modules -> [SumType] -> Modules-sumTypesToModules = foldr sumTypeToModule--sumTypeToModule :: SumType -> Modules -> Modules-sumTypeToModule st@(SumType t _) = M.alter (Just. updateModule) (typeModule t)- where- updateModule Nothing = PSModule {- psModuleName = typeModule t- , psImportLines = typesToImportLines M.empty (getUsedTypes st)- , psTypes = [st]- }- updateModule (Just m) = m {- psImportLines = typesToImportLines (psImportLines m) (getUsedTypes st)- , psTypes = st : psTypes m- }--typesToImportLines :: ImportLines -> [TypeInfo] -> ImportLines-typesToImportLines = foldr typeToImportLines--typeToImportLines :: TypeInfo -> ImportLines -> ImportLines-typeToImportLines t = M.alter (Just . updateLine) (typeModule t)- where- updateLine Nothing = ImportLine (typeModule t) (S.singleton (typeName t))- updateLine (Just (ImportLine m types)) = ImportLine m $ S.insert (typeName t) types
− src/PureScript/Bridge/SumType.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeSynonymInstances #-}---{-# LANGUAGE DeriveGeneric #-}---module PureScript.Bridge.SumType where--import Generics.Deriving-import Data.Text (Text)-import qualified Data.Text as T-import Data.Proxy-import Data.Typeable--import PureScript.Bridge.TypeInfo--data SumType = SumType TypeInfo [DataConstructor] deriving Show--data DataConstructor = DataConstructor {- sigConstructor :: Text-, sigValues :: Either [TypeInfo] [RecordEntry]-} deriving Show--data RecordEntry = RecordEntry {- recLabel :: Text-, recValue :: TypeInfo-} deriving Show--toSumType :: forall t. (Generic t, Typeable t, GDataConstructor (Rep t)) => Proxy t -> SumType-toSumType p = SumType (mkTypeInfo p) constructors- where- constructors = gToConstructors (from (undefined :: t))--class GDataConstructor f where- gToConstructors :: f a -> [DataConstructor]--class GRecordEntry f where- gToRecordEntries :: f a -> [RecordEntry]--instance (Datatype a, GDataConstructor c) => GDataConstructor (D1 a c) where- gToConstructors (M1 c) = gToConstructors c--instance (GDataConstructor a, GDataConstructor b) => GDataConstructor (a :+: b) where- gToConstructors (_ :: (a :+: b) f) = gToConstructors (undefined :: a f) ++ gToConstructors (undefined :: b f)--instance (Constructor a, GRecordEntry b) => GDataConstructor (C1 a b) where- gToConstructors c@(M1 r) = [- DataConstructor {- sigConstructor = constructor- , sigValues = values- }- ]- where- constructor = T.pack $ conName c- values = if conIsRecord c- then Right $ gToRecordEntries r- else Left $ map recValue $ gToRecordEntries r--instance (GRecordEntry a, GRecordEntry b) => GRecordEntry (a :*: b) where- gToRecordEntries (_ :: (a :*: b) f) = gToRecordEntries (undefined :: a f) ++ gToRecordEntries (undefined :: b f)---instance GRecordEntry U1 where- gToRecordEntries _ = []--instance (Selector a, Typeable t) => GRecordEntry (S1 a (K1 R t)) where- gToRecordEntries e = [- RecordEntry { recLabel = T.pack (selName e)- , recValue = mkTypeInfo (Proxy :: Proxy t)- }- ]--getUsedTypes :: SumType -> [TypeInfo]-getUsedTypes (SumType _ cs) = foldr constructorToType [] cs--constructorToType :: DataConstructor -> [TypeInfo] -> [TypeInfo]-constructorToType (DataConstructor _ (Left myTs)) ts = concatMap flattenTypeInfo myTs ++ ts-constructorToType (DataConstructor _ (Right rs)) ts = concatMap (flattenTypeInfo . recValue) rs ++ ts--flattenTypeInfo :: TypeInfo -> [TypeInfo]-flattenTypeInfo t = t : concatMap flattenTypeInfo (typeParameters t)
− src/PureScript/Bridge/Tuple.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module PureScript.Bridge.Tuple where--import Data.Monoid-import Data.Text (Text)-import qualified Data.Text as T---import PureScript.Bridge.TypeInfo--data TupleParserState =- Start | OpenFound | ColonFound | Tuple | NoTuple deriving (Eq, Show)---tupleBridge :: TypeBridge-tupleBridge t- | isTuple (typeName t) = Just $ t {- typePackage = "purescript-tuples"- , typeModule = if size == 2 then "Data.Tuple" else "Data.Tuple.Nested"- , typeName = "Tuple" <> if size == 2 then "" else T.pack (show size)- }- | otherwise = Nothing- where- size = length $ typeParameters t---step :: TupleParserState -> Char -> TupleParserState-step Start '(' = OpenFound-step Start _ = NoTuple-step OpenFound ',' = ColonFound-step OpenFound _ = NoTuple-step ColonFound ',' = ColonFound-step ColonFound ')' = Tuple-step ColonFound _ = NoTuple-step Tuple _ = NoTuple-step NoTuple _ = NoTuple--isTuple :: Text -> Bool-isTuple = (== Tuple) . T.foldl' step Start
− src/PureScript/Bridge/TypeInfo.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module PureScript.Bridge.TypeInfo where---import Data.Text (Text)-import qualified Data.Text as T-import Data.Maybe-import Data.Proxy-import Data.Typeable----- Translates a Haskell type info to a PureScript type info:-type TypeBridge = TypeInfo -> Maybe TypeInfo----- Basic info about a data type:-data TypeInfo = TypeInfo {- -- | Hackage package- typePackage :: Text- -- | Full Module path-, typeModule :: Text-, typeName :: Text-, typeParameters :: [TypeInfo]-} deriving (Eq, Show)--mkTypeInfo :: Typeable t => Proxy t -> TypeInfo-mkTypeInfo = mkTypeInfo' . typeRep--mkTypeInfo' :: TypeRep -> TypeInfo-mkTypeInfo' rep = let- con = typeRepTyCon rep- in TypeInfo {- typePackage = T.pack $ tyConPackage con- , typeModule = T.pack $ tyConModule con- , typeName = T.pack $ tyConName con- , typeParameters = map mkTypeInfo' (typeRepArgs rep)- }------ Little helper for type bridge implementers-eqTypeName :: Text -> TypeInfo -> Bool-eqTypeName name = (== name) . typeName
test/Tests.hs view
@@ -7,7 +7,7 @@ import Data.Proxy-import PureScript.Bridge.TypeInfo+import Language.PureScript.Bridge.TypeInfo testTypeInfo :: IO Progress