4
4
5
5
module Language.PureScript.Bridge.Printer where
6
6
7
+ import Control.Lens
7
8
import Control.Monad
8
9
import Data.Map.Strict (Map )
9
10
import qualified Data.Map.Strict as Map
@@ -57,22 +58,37 @@ moduleToText :: Module 'PureScript -> Text
57
58
moduleToText m = T. unlines $
58
59
" -- File auto generated by purescript-bridge! --"
59
60
: " module " <> psModuleName m <> " where\n "
60
- : map importLineToText (Map. elems (psImportLines m))
61
- ++ [ " \n import Data.Generic (class Generic)\n\n " ]
61
+ : map importLineToText allImports
62
+ ++ [ " "
63
+ , " import Prelude"
64
+ , " import Data.Generic (class Generic)"
65
+ , " "
66
+ ]
62
67
++ map sumTypeToText (psTypes m)
68
+ where
69
+ otherImports = importsFromList _lensImports
70
+ allImports = Map. elems $ mergeImportLines otherImports (psImportLines m)
63
71
72
+ _lensImports :: [ImportLine ]
73
+ _lensImports = [
74
+ ImportLine " Data.Maybe" $ Set. fromList [" Maybe(..)" ]
75
+ -- , ImportLine "Prelude" mempty
76
+ , ImportLine " Data.Lens" $ Set. fromList [" PrismP" , " LensP" , " prism'" , " lens" ]
77
+ ]
64
78
65
79
importLineToText :: ImportLine -> Text
66
80
importLineToText l = " import " <> importModule l <> " (" <> typeList <> " )"
67
81
where
68
82
typeList = T. intercalate " , " (Set. toList (importTypes l))
69
83
70
84
sumTypeToText :: SumType 'PureScript -> Text
71
- sumTypeToText st@ (SumType t cs) = T. unlines $
85
+ sumTypeToText st@ (SumType t cs) = ( T. unlines $
72
86
" data " <> typeInfoToText True t <> " ="
73
87
: " " <> T. intercalate " \n | " (map (constructorToText 4 ) cs)
74
- : [ " \n derive instance generic" <> _typeName t <> " :: " <> genericConstrains <> genericInstance t ]
88
+ : [ " \n derive instance generic" <> _typeName t <> " :: " <> genericConstrains <> genericInstance t ])
89
+ <> " \n " <> sep <> " \n " <> sumTypeToPrismsAndLenses st <> sep
75
90
where
91
+ sep = T. replicate 80 " -"
76
92
genericInstance = (" Generic " <> ) . typeInfoToText False
77
93
genericConstrains
78
94
| stpLength == 0 = mempty
@@ -86,6 +102,26 @@ sumTypeToText st@(SumType t cs) = T.unlines $
86
102
sumTypeParameters = filter isTypeParam . Set. toList $ getUsedTypes st
87
103
isTypeParam typ = _typeName typ `elem` map _typeName (_typeParameters t)
88
104
105
+ sumTypeToPrismsAndLenses :: SumType 'PureScript -> Text
106
+ sumTypeToPrismsAndLenses st = sumTypeToPrisms st <> sumTypeToLenses st
107
+
108
+ sumTypeToPrisms :: SumType 'PureScript -> Text
109
+ sumTypeToPrisms st = T. unlines $ map (constructorToPrism moreThan1 st) cs
110
+ where
111
+ cs = st ^. sumTypeConstructors
112
+ moreThan1 = length cs > 1
113
+
114
+
115
+ sumTypeToLenses :: SumType 'PureScript -> Text
116
+ sumTypeToLenses st = T. unlines $ recordEntryToLens st <$> dcName <*> dcRecords
117
+ where
118
+ cs = st ^. sumTypeConstructors
119
+ dcName = lensableConstructor ^.. traversed. sigConstructor
120
+ dcRecords = lensableConstructor ^.. traversed. sigValues. _Right. traverse . filtered hasUnderscore
121
+ hasUnderscore e = e ^. recLabel. to (T. isPrefixOf " _" )
122
+ lensableConstructor = filter singleRecordCons cs ^? _head
123
+ singleRecordCons (DataConstructor _ (Right _)) = True
124
+ singleRecordCons _ = False
89
125
90
126
constructorToText :: Int -> DataConstructor 'PureScript -> Text
91
127
constructorToText _ (DataConstructor n (Left ts)) = n <> " " <> T. intercalate " " (map (typeInfoToText False ) ts)
@@ -95,10 +131,84 @@ constructorToText indentation (DataConstructor n (Right rs)) =
95
131
<> spaces indentation <> " }"
96
132
where
97
133
intercalation = " \n " <> spaces indentation <> " ," <> " "
98
- spaces c = T. replicate c " "
134
+
135
+ spaces :: Int -> Text
136
+ spaces c = T. replicate c " "
137
+
138
+
139
+ typeNameAndForall :: SumType 'PureScript -> (Text , Text )
140
+ typeNameAndForall st = (typName, forAll)
141
+ where
142
+ typName = typeInfoToText False (st ^. sumTypeInfo)
143
+ forAllParams = st ^.. sumTypeInfo. typeParameters. traversed. to (typeInfoToText False )
144
+ forAll = case forAllParams of
145
+ [] -> " :: "
146
+ cs -> " :: forall " <> T. intercalate " " cs <> " . "
147
+ -- textParameters = map (typeInfoToText False) params
148
+
149
+ fromEntries :: (RecordEntry a -> Text ) -> [RecordEntry a ] -> Text
150
+ fromEntries mkElem rs = " { " <> inners <> " }"
151
+ where
152
+ inners = T. intercalate " , " $ map mkElem rs
153
+
154
+ mkFnArgs :: [RecordEntry 'PureScript] -> Text
155
+ mkFnArgs [r] = r ^. recLabel
156
+ mkFnArgs rs = fromEntries (\ recE -> recE ^. recLabel <> " : " <> recE ^. recLabel) rs
157
+
158
+ mkTypeSig :: [RecordEntry 'PureScript] -> Text
159
+ mkTypeSig [] = " Unit"
160
+ mkTypeSig [r] = typeInfoToText False $ r ^. recValue
161
+ mkTypeSig rs = fromEntries recordEntryToText rs
162
+
163
+ constructorToPrism :: Bool -> SumType 'PureScript -> DataConstructor 'PureScript -> Text
164
+ constructorToPrism otherConstructors st (DataConstructor n args) =
165
+ case args of
166
+ Left cs -> pName <> forAll <> " PrismP " <> typName <> " " <> mkTypeSig types <> " \n "
167
+ <> pName <> " = prism' " <> getter <> " f\n "
168
+ <> spaces 2 <> " where\n "
169
+ <> spaces 4 <> " f " <> mkF cs
170
+ <> otherConstructorFallThrough
171
+ where
172
+ mkF [] = n <> " = Just unit\n "
173
+ mkF _ = " (" <> n <> " " <> T. unwords (map _recLabel types) <> " ) = Just $ " <> mkFnArgs types <> " \n "
174
+ getter | cs == [] = " (\\ _ -> " <> n <> " )"
175
+ | length cs == 1 = n
176
+ | otherwise = " (\\ { " <> T. intercalate " , " cArgs <> " } -> " <> n <> " " <> T. intercalate " " cArgs <> " )"
177
+ where
178
+ cArgs = map (T. singleton . fst ) $ zip [' a' .. ] cs
179
+ types = [RecordEntry (T. singleton label) t | (label, t) <- zip [' a' .. ] cs]
180
+ Right rs -> pName <> forAll <> " PrismP " <> typName <> " { " <> recordSig <> " }\n "
181
+ <> pName <> " = prism' " <> n <> " f\n "
182
+ <> spaces 2 <> " where\n "
183
+ <> spaces 4 <> " f (" <> n <> " r) = Just r\n "
184
+ <> otherConstructorFallThrough
185
+ where
186
+ recordSig = T. intercalate " , " (map recordEntryToText rs)
187
+ where
188
+ (typName, forAll) = typeNameAndForall st
189
+ pName = " _" <> n
190
+ otherConstructorFallThrough | otherConstructors = spaces 4 <> " f _ = Nothing\n "
191
+ | otherwise = " \n "
192
+
193
+ recordEntryToLens :: SumType 'PureScript -> Text -> RecordEntry 'PureScript -> Text
194
+ recordEntryToLens st constructorName e =
195
+ case hasUnderscore of
196
+ False -> " "
197
+ True ->
198
+ lensName <> forAll <> " LensP " <> typName <> " " <> recType <> " \n "
199
+ <> lensName <> " = lens get set\n where\n "
200
+ <> spaces 4 <> " get (" <> constructorName <> " r) = r." <> recName <> " \n "
201
+ <> spaces 4 <> " set (" <> constructorName <> " r) = " <> setter
202
+ where
203
+ (typName, forAll) = typeNameAndForall st
204
+ setter = constructorName <> " <<< r { " <> recName <> " = _ }\n "
205
+ recName = e ^. recLabel
206
+ lensName = T. drop 1 recName
207
+ recType = typeInfoToText False (e ^. recValue)
208
+ hasUnderscore = e ^. recLabel. to (T. isPrefixOf " _" )
99
209
100
210
recordEntryToText :: RecordEntry 'PureScript -> Text
101
- recordEntryToText e = _recLabel e <> " :: " <> typeInfoToText True (_recValue e )
211
+ recordEntryToText e = _recLabel e <> " :: " <> typeInfoToText True (e ^. recValue )
102
212
103
213
104
214
typeInfoToText :: Bool -> PSType -> Text
0 commit comments