@@ -0,0 +1,892 @@
1+---------------------------------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.CodeGen.Cpp
4+-- Copyright : (c) 2013-15 Phil Freeman, Andy Arvanitis, and other contributors
5+-- License : MIT
6+--
7+-- Maintainer : Andy Arvanitis <andy.arvanitis@gmail.com>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+-- This module generates code in the simplified C++1x intermediate representation from
13+-- Purescript code.
14+--
15+---------------------------------------------------------------------------------------------------
16+{-# LANGUAGE GADTs #-}
17+{-# LANGUAGE ViewPatterns #-}
18+{-# LANGUAGE FlexibleContexts #-}
19+{-# LANGUAGE ScopedTypeVariables #-}
20+{-# LANGUAGE PatternGuards #-}
21+
22+module Language.PureScript.CodeGen.Cpp
23+ ( module AST
24+ , module Common
25+ , moduleToCpp
26+ , P.prettyPrintCpp
27+ ) where
28+
29+import Data.Char (isLetter)
30+import Data.List
31+import Data.Function (on)
32+import Data.Maybe (catMaybes, fromJust)
33+import qualified Data.Map as M
34+
35+import Control.Monad (forM, replicateM)
36+import Control.Monad.Reader (MonadReader, ask)
37+import Control.Monad.Supply.Class
38+import Language.PureScript.AST.SourcePos
39+import Language.PureScript.CodeGen.Cpp.AST as AST
40+import Language.PureScript.CodeGen.Cpp.Common as Common
41+import Language.PureScript.CodeGen.Cpp.File
42+import Language.PureScript.CodeGen.Cpp.Optimizer
43+import Language.PureScript.CodeGen.Cpp.Optimizer.TCO
44+import Language.PureScript.CodeGen.Cpp.Types
45+import Language.PureScript.Comments
46+import Language.PureScript.CoreFn
47+import Language.PureScript.Names
48+import Language.PureScript.Options
49+import Language.PureScript.Sugar.TypeClasses (superClassDictionaryNames)
50+import Language.PureScript.Traversals (sndM)
51+
52+import qualified Language.PureScript.Constants as C
53+import qualified Language.PureScript.Environment as E
54+import qualified Language.PureScript.Pretty.Cpp as P
55+import qualified Language.PureScript.Types as T
56+
57+-- |
58+-- Generate code in the simplified C++1x intermediate representation for all declarations in a
59+-- module.
60+--
61+---------------------------------------------------------------------------------------------------
62+moduleToCpp :: forall m. (Applicative m, Monad m, MonadReader Options m, MonadSupply m)
63+ => E.Environment -> Module Ann -> m [Cpp]
64+---------------------------------------------------------------------------------------------------
65+moduleToCpp env (Module _ mn imps _ foreigns decls) = do
66+ cppImports <-
67+ traverse (pure . runModuleName) .
68+ delete (ModuleName [ProperName C.prim]) . (\\ [mn]) $
69+ (snd <$> imps)
70+ let cppImports' = "PureScript" : cppImports
71+ cppDecls <- concat <$> mapM (bindToCpp [CppTopLevel]) decls
72+ cppDecls' <- traverse optimize cppDecls
73+ foreignWrappers <- curriedForeigns
74+ let optimized = cppDecls' ++ foreignWrappers
75+ let moduleHeader =
76+ fileBegin mn "HH" ++
77+ P.linebreak ++
78+ ((\i -> CppInclude i i) <$> cppImports') ++
79+ (if not (null foreigns)
80+ then [CppInclude [] (runModuleName mn ++ "_ffi")]
81+ else []) ++
82+ P.linebreak ++
83+ [ CppNamespace (runModuleName mn) $
84+ (CppUseNamespace <$> cppImports') ++
85+ P.linebreak ++
86+ dataCtors ++
87+ toHeader optimized ++
88+ toHeaderFns optimized
89+ ] ++
90+ P.linebreak ++
91+ fileEnd mn "HH"
92+ let bodyCpps = toBodyDecl optimized ++ toBody optimized
93+ moduleBody =
94+ CppInclude (runModuleName mn) (runModuleName mn) :
95+ P.linebreak ++
96+ (if null bodyCpps
97+ then []
98+ else [ CppNamespace (runModuleName mn) $
99+ (CppUseNamespace <$> cppImports') ++
100+ P.linebreak ++ bodyCpps ]) ++
101+ P.linebreak ++
102+ (if isMain mn
103+ then [nativeMain] ++ P.linebreak
104+ else [])
105+ return $ moduleHeader ++ CppEndOfHeader : moduleBody
106+ where
107+ -- |
108+ -- Generate code in the simplified C++1x intermediate representation for a declaration
109+ --
110+ -------------------------------------------------------------------------------------------------
111+ bindToCpp :: [CppValueQual] -> Bind Ann -> m [Cpp]
112+ -------------------------------------------------------------------------------------------------
113+ bindToCpp qs (NonRec ann ident val) = return <$> declToCpp qs (ann, ident) val
114+ bindToCpp qs (Rec vals) = forM vals (uncurry $ declToCpp (CppRecursive:qs))
115+
116+ -- |
117+ -- Desugar a declaration into a variable introduction or named function
118+ -- declaration.
119+ -------------------------------------------------------------------------------------------------
120+ declToCpp :: [CppValueQual] -> (Ann, Ident) -> Expr Ann -> m Cpp
121+ -------------------------------------------------------------------------------------------------
122+ declToCpp _ (_, ident) e@(Abs (_, _, _, Just IsTypeClassConstructor) _ _) =
123+ let className = identToCpp ident
124+ (supers, members) = span (C.__superclass_ `isPrefixOf`) (identToCpp <$> (fst $ unAbs e []))
125+ in return $ CppStruct className [CppEnum Nothing Nothing (sort supers ++ members)]
126+
127+ declToCpp _ (_, Op _) (Var (_, _, Nothing, _) _) =
128+ return CppNoOp -- skip operator aliases
129+
130+ declToCpp vqs (_, ident) (Abs (_, com, ty, _) arg body) = do
131+ fn <- if argcnt > 1 && CppTopLevel `elem` vqs
132+ then do
133+ argNames <- replicateM (argcnt - length args' - 1) freshName
134+ let args'' = zip argNames (repeat aty)
135+ block <- asReturnBlock <$> valueToCpp
136+ (if null argNames
137+ then snd abs'
138+ else curriedApp
139+ (tail argNames)
140+ (App
141+ nullAnn
142+ (snd abs')
143+ (Var nullAnn . Qualified Nothing . Ident $ head argNames)))
144+ return $ CppNamespace ""
145+ [ CppFunction
146+ name
147+ (arg' : args' ++ args'')
148+ rty
149+ (vqs ++ if isAccessor (snd abs') then [CppInline] else [])
150+ (convertNestedLambdas $ convertDictIndexers block)
151+ , CppFunction
152+ (curriedName name)
153+ [arg']
154+ rty
155+ []
156+ (asReturnBlock $
157+ curriedLambda
158+ (fst <$> args' ++ args'')
159+ (CppApp (CppVar name)
160+ (CppVar . fst <$> (arg' : args' ++ args''))))
161+ ]
162+ else do
163+ block <- valueToCpp body
164+ return $ CppFunction
165+ name
166+ [arg']
167+ rty
168+ (vqs ++ if isIndexer block then [CppInline] else [])
169+ (convertNestedLambdas . asReturnBlock $ convertDictIndexers block)
170+ return (CppComment com fn)
171+ where
172+ ty' | Just t <- ty = Just t
173+ | Just (t, _, _) <- M.lookup (mn, ident) (E.names env) = Just t
174+ | otherwise = Nothing
175+ argcnt = maybe 0 countArgs ty'
176+ name = identToCpp ident
177+ aty = Just $ CppAny [CppConst, CppRef]
178+ rty = Just $ CppAny []
179+ arg' = (identToCpp arg, aty)
180+ args' = (\i -> (identToCpp i, aty)) <$> fst abs'
181+ abs' = unAbs body []
182+ isAccessor :: Expr Ann -> Bool
183+ isAccessor Accessor{} = True
184+ isAccessor _ = False
185+ isIndexer :: Cpp -> Bool
186+ isIndexer (CppIndexer (CppStringLiteral _) (CppVar _)) = True
187+ isIndexer _ = False
188+ classes = qualifiedToCpp (Ident . runProperName) . fst <$>
189+ maybe [] extractConstraints ty'
190+ dictClasses = zip (CppVar . fst <$> arg' : args') classes
191+ convertDictIndexers = everywhereOnCpp (dictIndexerToEnum dictClasses)
192+
193+ declToCpp _ (_, ident) (Constructor _ _ (ProperName _) []) =
194+ return $
195+ CppFunction
196+ (identToCpp ident)
197+ []
198+ (Just $ CppAny [])
199+ [CppInline]
200+ (asReturnBlock $
201+ CppDataLiteral [CppAccessor (CppVar $ identToCpp ident) (CppVar "data")])
202+
203+ declToCpp _ (_, ident) (Constructor _ _ (ProperName _) fields) =
204+ return . CppNamespace "" $
205+ [ CppFunction
206+ name
207+ (farg <$> fields')
208+ (Just $ CppAny [])
209+ [CppInline]
210+ (asReturnBlock $
211+ CppDataLiteral $
212+ CppAccessor (CppVar name) (CppVar "data") : (CppVar <$> fields'))
213+ ] ++
214+ if length fields > 1
215+ then
216+ [ CppFunction
217+ (curriedName name)
218+ (farg <$> f)
219+ (Just $ CppAny [])
220+ []
221+ (CppBlock (fieldLambdas fs))
222+ ]
223+ else
224+ []
225+ where
226+ name = identToCpp ident
227+ fields' = identToCpp <$> fields
228+ (f, fs) | (f' : fs') <- fields' = ([f'], fs')
229+ | otherwise = ([], [])
230+ farg = \x -> (x, Just $ CppAny [CppConst, CppRef])
231+ fieldLambdas flds
232+ | (f' : fs') <- flds = [ CppReturn . maybeRemCaps $
233+ CppLambda
234+ [CppCaptureAll]
235+ (farg <$> [f'])
236+ (Just $ CppAny [])
237+ (CppBlock $ fieldLambdas fs') ]
238+ | otherwise = [CppReturn $ CppApp (CppVar name) (CppVar <$> fields')]
239+
240+ declToCpp qs (_, ident) e
241+ | Just ty <- exprType e,
242+ CppTopLevel `elem` qs,
243+ argcnt <- countArgs ty,
244+ argcnt > 0 = do
245+ argNames <- replicateM argcnt freshName
246+ block <- valueToCpp $
247+ curriedApp
248+ (tail argNames)
249+ (App nullAnn e (Var nullAnn . Qualified Nothing . Ident $ head argNames))
250+ let fn' = CppFunction
251+ (curriedName name)
252+ [(head argNames, Just $ CppAny [CppConst, CppRef])]
253+ rty
254+ []
255+ (asReturnBlock $
256+ curriedLambda
257+ (tail argNames)
258+ (CppApp (CppVar name)
259+ (CppVar <$> argNames)))
260+ return $ CppNamespace "" $
261+ [ CppFunction
262+ name
263+ (zip argNames $ repeat aty)
264+ rty
265+ []
266+ (asReturnBlock block)
267+ ] ++ if argcnt > 1
268+ then [fn']
269+ else []
270+ where
271+ name = identToCpp ident
272+ aty = Just $ CppAny [CppConst, CppRef]
273+ rty = Just $ CppAny []
274+
275+ declToCpp _ (_, ident) val = do
276+ val' <- valueToCpp val
277+ return $
278+ CppVariableIntroduction
279+ (identToCpp ident, Just $ CppAny [CppConst])
280+ []
281+ (Just val')
282+
283+ -------------------------------------------------------------------------------------------------
284+ exprType :: Expr Ann -> Maybe T.Type
285+ -------------------------------------------------------------------------------------------------
286+ exprType (Var (_, _, Just ty, _) _) = Just ty
287+ exprType (App (_, _, Just ty, _) _ _) = Just ty
288+ exprType (Case (_, _, Just ty, _) _ _) = Just ty
289+ exprType (Let (_, _, Just ty, _) _ _) = Just ty
290+ exprType _ = Nothing
291+
292+ -------------------------------------------------------------------------------------------------
293+ fnToLambda :: Cpp -> Cpp
294+ -------------------------------------------------------------------------------------------------
295+ fnToLambda (CppFunction name args _ qs body) =
296+ CppVariableIntroduction
297+ (name, Just $ CppAny [CppConst])
298+ (filter (==CppStatic) qs)
299+ (Just $ CppLambda cs args (Just $ CppAny []) body)
300+ where
301+ cs | (not . null) (qs `intersect` [CppInline, CppStatic]) = []
302+ | otherwise = [CppCaptureAll]
303+ fnToLambda (CppComment com cpp) = CppComment com (fnToLambda cpp)
304+ fnToLambda _ = error "Not a function!"
305+
306+ -------------------------------------------------------------------------------------------------
307+ convertNestedLambdas :: Cpp -> Cpp
308+ -------------------------------------------------------------------------------------------------
309+ convertNestedLambdas = everywhereOnCpp go
310+ where
311+ go :: Cpp -> Cpp
312+ go f@(CppFunction {}) = maybeRemCaps $ fnToLambda f
313+ go (CppLambda _ args rtyp body) = maybeRemCaps $ CppLambda [CppCaptureAll] args rtyp body
314+ go cpp = cpp
315+
316+ -------------------------------------------------------------------------------------------------
317+ convertRecursiveFns :: [Cpp] -> Cpp -> [Cpp]
318+ -------------------------------------------------------------------------------------------------
319+ convertRecursiveFns cpps otherCpp
320+ | not $ null fns = dict : (accessor topdict <$> filteredFns allCpps) ++ cpps'
321+ where
322+ cpps' = everywhereOnCpp removeRec <$> cpps
323+ allCpps = CppBlock $ otherCpp : cpps'
324+
325+ fns :: [(String, Cpp)]
326+ fns = toelem <$> concatMap (everythingOnCpp (++) recursive) cpps
327+
328+ filteredFns :: Cpp -> [(String, Cpp)]
329+ filteredFns cpp = filter (\(f,_) -> everythingOnCpp (||) (== CppVar f) cpp) fns
330+
331+ recursive :: Cpp -> [Cpp]
332+ recursive cpp'@(CppFunction _ _ _ qs _) | CppRecursive `elem` qs = [cpp']
333+ recursive _ = []
334+
335+ removeRec :: Cpp -> Cpp
336+ removeRec(CppFunction _ _ _ qs _) | CppRecursive `elem` qs = CppNoOp
337+ removeRec cpp' = cpp'
338+
339+ dict :: Cpp
340+ dict = CppVariableIntroduction
341+ (topdict, Just $ CppAny [CppConst])
342+ []
343+ (Just $ CppDataLiteral ((\(f,cpp) -> CppComment [LineComment f] cpp) <$> fns))
344+
345+ accessor :: String -> (String, Cpp) -> Cpp
346+ accessor dictname (name, _) =
347+ CppVariableIntroduction
348+ (name, Just $ CppAny [CppConst])
349+ []
350+ (Just accessed)
351+ where
352+ dict' = CppVar dictname
353+ dict'' = CppCast dataType dict'
354+ accessed = CppApp
355+ (CppIndexer
356+ (CppNumericLiteral .
357+ Left .
358+ fromIntegral .
359+ fromJust $ findIndex ((== name) . fst) fns)
360+ dict'')
361+ [dict']
362+
363+ toelem :: Cpp -> (String, Cpp)
364+ toelem (CppFunction name args rtyp _ body'@(CppBlock body)) =
365+ (name, withdict . maybeRemCaps $ CppLambda
366+ [CppCaptureAll]
367+ args
368+ rtyp
369+ (CppBlock $ (accessor localdict <$> filteredFns body') ++ body))
370+ toelem _ = error "not a function"
371+
372+ withdict :: Cpp -> Cpp
373+ withdict cpp' = maybeRemCaps $ CppLambda
374+ [CppCaptureAll]
375+ [(localdict, Just $ CppAny [CppConst, CppRef])]
376+ (Just $ CppAny [])
377+ (asReturnBlock cpp')
378+ topdict :: String
379+ topdict = "_$dict$_"
380+ localdict :: String
381+ localdict = "$dict$"
382+
383+ convertRecursiveFns cpps _ = cpps
384+
385+ -- |
386+ -- Generate code in the simplified C++1x intermediate representation for a value or expression.
387+ --
388+ -------------------------------------------------------------------------------------------------
389+ valueToCpp :: Expr Ann -> m Cpp
390+ -------------------------------------------------------------------------------------------------
391+ valueToCpp (Var (_, _, _, Just (IsConstructor _ [])) ident) =
392+ return $ CppApp (varToCpp ident) []
393+
394+ valueToCpp (Var (_, _, _, Just (IsConstructor _ [_])) ident) =
395+ return $ varToCpp ident
396+
397+ valueToCpp (Var (_, _, _, Just (IsConstructor _ _)) ident) =
398+ return . curriedName' $ varToCpp ident
399+
400+ valueToCpp (Var (_, _, Just ty, _) ident@(Qualified (Just _) _))
401+ | countArgs ty > 1 =
402+ return . curriedName' $ varToCpp ident
403+
404+ valueToCpp (Var _ ident) =
405+ return $ varToCpp ident
406+
407+ valueToCpp (Literal _ (NumericLiteral n)) =
408+ return (CppNumericLiteral n)
409+
410+ valueToCpp (Literal _ (StringLiteral s)) =
411+ return (CppStringLiteral s)
412+
413+ valueToCpp (Literal _ (CharLiteral c)) =
414+ return (CppCharLiteral c)
415+
416+ valueToCpp (Literal _ (BooleanLiteral b)) =
417+ return (CppBooleanLiteral b)
418+
419+ valueToCpp (Literal _ (ArrayLiteral xs)) =
420+ CppArrayLiteral <$> mapM valueToCpp xs
421+
422+ valueToCpp (Literal _ (ObjectLiteral ps)) =
423+ CppObjectLiteral <$> sortBy (compare `on` fst) <$> mapM (sndM valueToCpp) ps
424+
425+ valueToCpp (Accessor _ prop val) =
426+ CppIndexer <$> pure (CppStringLiteral prop) <*> valueToCpp val
427+
428+ -- TODO: use a more efficient way of copying/updating the map?
429+ valueToCpp (ObjectUpdate (_, _, Just ty, _) obj ps) = do
430+ obj' <- valueToCpp obj
431+ updatedFields <- mapM (sndM valueToCpp) ps
432+ let origKeys = (allKeys ty) \\ (fst <$> updatedFields)
433+ origFields = (\key -> (key, CppIndexer (CppStringLiteral key) obj')) <$> origKeys
434+ return $ CppObjectLiteral . sortBy (compare `on` fst) $ origFields ++ updatedFields
435+ where
436+ allKeys :: T.Type -> [String]
437+ allKeys (T.TypeApp (T.TypeConstructor _) r@(T.RCons {})) = fst <$> (fst $ T.rowToList r)
438+ allKeys (T.ForAll _ t _) = allKeys t
439+ allKeys _ = error $ show "Not a recognized row type"
440+
441+ valueToCpp (ObjectUpdate _ _ _) = error $ "Bad Type in object update!"
442+
443+ valueToCpp (Abs (_, _, ty, _) arg body) = do
444+ cpp <- valueToCpp body
445+ let cpp' = convertNestedLambdas cpp
446+ return $
447+ CppLambda
448+ []
449+ [(identToCpp arg, Just $ CppAny [CppConst, CppRef])]
450+ (Just $ CppAny [])
451+ (asReturnBlock $ convertDictIndexers cpp')
452+ where
453+ arg' = identToCpp arg
454+ args' = identToCpp <$> (fst $ unAbs body [])
455+ classes = qualifiedToCpp (Ident . runProperName) . fst <$>
456+ maybe [] extractConstraints ty
457+ dictClasses = zip (CppVar <$> arg' : args') classes
458+ convertDictIndexers = everywhereOnCpp (dictIndexerToEnum dictClasses)
459+ valueToCpp e@App{} = do
460+ let (f, args) = unApp e []
461+ args' <- mapM valueToCpp args
462+ case f of
463+ Var (_, _, _, Just IsNewtype) _ -> return (head args')
464+ Var (_, _, _, Just (IsConstructor _ fields)) ident
465+ | length args == length fields ->
466+ return $ CppApp (uncurried' $ qualifiedToCpp id ident) args'
467+ Var (_, _, _, Just IsTypeClassConstructor) (Qualified mn' (Ident classname)) ->
468+ let Just (_, constraints, fns) = findClass (Qualified mn' (ProperName classname)) in
469+ return . CppObjectLiteral $
470+ zip
471+ ((sort $ superClassDictionaryNames constraints) ++ (fst <$> fns))
472+ args'
473+ Var ann (Qualified (Just mn') ident)
474+ | argcnt <- maybe 0 countArgs ty,
475+ argcnt > 1 -> do
476+ f' <- valueToCpp f
477+ let (uncurriedArgs, curriedArgs) =
478+ if length args' >= argcnt
479+ then splitAt argcnt args'
480+ else ([], args')
481+ fn' =
482+ if null uncurriedArgs
483+ then curriedName' f'
484+ else CppApp (uncurried' f') uncurriedArgs
485+ return $ foldl (\fn a -> CppApp fn [a]) fn' curriedArgs
486+ where
487+ ty | (_, _, Just t, _) <- ann = Just t
488+ | Just (t, _, _) <- M.lookup (mn', ident) (E.names env) = Just t
489+ | otherwise = Nothing
490+ _ ->
491+ flip (foldl (\fn a -> CppApp fn [a])) args' <$> valueToCpp f
492+
493+ valueToCpp (Case (maybeSpan, _, ty, _) values binders) = do
494+ vals <- mapM valueToCpp values
495+ bindersToCpp maybeSpan ty binders vals
496+
497+ valueToCpp (Let _ ds val) = do
498+ ds' <- concat <$> mapM (bindToCpp []) ds
499+ ret <- valueToCpp val
500+ opts <- ask
501+ let ds'' = tco opts <$> ds'
502+ let rs = if hasRecursion ds''
503+ then convertRecursiveFns ds'' ret
504+ else ds''
505+ let cpps = convertNestedLambdas <$> rs
506+ return $ CppApp
507+ (CppLambda [] [] Nothing (CppBlock (cpps ++ [CppReturn ret])))
508+ []
509+ where
510+ hasRecursion :: [Cpp] -> Bool
511+ hasRecursion cpps' = any (everythingOnCpp (||) hasRecursiveRef) cpps'
512+ where
513+ fnames :: [String]
514+ fnames = concatMap (everythingOnCpp (++) fname) cpps'
515+
516+ fname :: Cpp -> [String]
517+ fname (CppFunction name _ _ _ _) = [name]
518+ fname _ = []
519+
520+ hasRecursiveRef :: Cpp -> Bool
521+ hasRecursiveRef (CppFunction _ _ _ _ body) = everythingOnCpp (||) ref body
522+ hasRecursiveRef _ = False
523+
524+ ref :: Cpp -> Bool
525+ ref (CppVar name) | name `elem` fnames = True
526+ ref _ = False
527+
528+ valueToCpp Constructor{} = return CppNoOp
529+
530+ -- |
531+ -- Generate code in the simplified C++1x intermediate representation for a reference to a
532+ -- variable.
533+ --
534+ -------------------------------------------------------------------------------------------------
535+ varToCpp :: Qualified Ident -> Cpp
536+ -------------------------------------------------------------------------------------------------
537+ varToCpp (Qualified Nothing ident) = CppVar (identToCpp ident)
538+ varToCpp qual = qualifiedToCpp id qual
539+
540+ -- |
541+ -- Generate code in the simplified C++1x intermediate representation for pattern match binders
542+ -- and guards.
543+ --
544+ -------------------------------------------------------------------------------------------------
545+ bindersToCpp :: Maybe SourceSpan -> Maybe T.Type -> [CaseAlternative Ann] -> [Cpp] -> m Cpp
546+ -------------------------------------------------------------------------------------------------
547+ bindersToCpp maybeSpan _ binders vals = do
548+ valNames <- replicateM (length vals) freshName
549+ let assignments = zipWith mkVarDecl valNames (map Just vals)
550+ cpps <- forM binders $ \(CaseAlternative bs result) -> do
551+ ret <- guardsToCpp result
552+ go valNames ret bs
553+ return $
554+ CppApp
555+ (CppLambda
556+ []
557+ []
558+ (Just $ CppAny [])
559+ (CppBlock (assignments ++ concat cpps ++ [failedPatternError valNames])))
560+ []
561+ where
562+ mkVarDecl :: String -> Maybe Cpp -> Cpp
563+ mkVarDecl name = CppVariableIntroduction (name, Nothing) []
564+ go :: [String] -> [Cpp] -> [Binder Ann] -> m [Cpp]
565+ go _ done [] = return done
566+ go (v:vs) done' (b:bs) = do
567+ done'' <- go vs done' bs
568+ binderToCpp v done'' b
569+ go _ _ _ = error "Invalid arguments to bindersToCpp"
570+
571+ failedPatternError :: [String] -> Cpp
572+ failedPatternError _ =
573+ CppThrow $ CppApp (CppVar "runtime_error") [CppStringLiteral errorMessage]
574+
575+ errorMessage :: String
576+ errorMessage = "Failed pattern match" ++
577+ maybe "" (((" at " ++ runModuleName mn ++ " ") ++) . displayStartEndPos) maybeSpan ++
578+ ": "
579+
580+ guardsToCpp :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [Cpp]
581+ guardsToCpp (Left gs) = forM gs $ \(cond, val) -> do
582+ cond' <- valueToCpp cond
583+ done <- valueToCpp val
584+ return $ CppIfElse cond' (asReturnBlock done) Nothing
585+ guardsToCpp (Right v) = return . CppReturn <$> valueToCpp v
586+
587+ -- |
588+ -- Generate code in the simplified C++1x intermediate representation for a pattern match
589+ -- binder.
590+ --
591+ -------------------------------------------------------------------------------------------------
592+ binderToCpp :: String -> [Cpp] -> Binder Ann -> m [Cpp]
593+ -------------------------------------------------------------------------------------------------
594+ binderToCpp _ done (NullBinder{}) = return done
595+
596+ binderToCpp varName done (LiteralBinder _ l) =
597+ literalToBinderCpp varName done l
598+
599+ binderToCpp varName done (VarBinder _ ident) =
600+ return $
601+ CppVariableIntroduction
602+ (identToCpp ident, Just $ CppAny [CppConst])
603+ []
604+ (Just $ CppVar varName)
605+ : done
606+
607+ binderToCpp varName done (ConstructorBinder (_, _, _, Just IsNewtype) _ _ [b]) =
608+ binderToCpp varName done b
609+
610+ binderToCpp varName done (ConstructorBinder (_, _, _, Just (IsConstructor ctorType fields))
611+ _
612+ (Qualified mn' (ProperName ctor))
613+ bs) = do
614+ cpps <- go (zip fields bs) done
615+ return $
616+ case ctorType of
617+ ProductType -> cpps
618+ SumType ->
619+ [ CppIfElse
620+ (CppBinary
621+ Equal
622+ (CppIndexer
623+ (CppVar ctorKey)
624+ (CppCast dataType $ CppVar varName))
625+ ctorCpp)
626+ (CppBlock cpps)
627+ Nothing]
628+ where
629+ go :: [(Ident, Binder Ann)] -> [Cpp] -> m [Cpp]
630+ go [] done' = return done'
631+ go ((field, binder) : remain) done' = do
632+ argVar <- freshName
633+ done'' <- go remain done'
634+ cpps <- binderToCpp argVar done'' binder
635+ return $
636+ CppVariableIntroduction
637+ (argVar, Nothing)
638+ []
639+ (Just $ CppIndexer
640+ (fieldToIndex field)
641+ (CppCast dataType $ CppVar varName))
642+ : cpps
643+ fieldToIndex :: Ident -> Cpp
644+ fieldToIndex = CppNumericLiteral . Left . (+1) . read . dropWhile isLetter . runIdent
645+ ctor' :: Cpp
646+ ctor' = CppAccessor (CppVar . identToCpp $ Ident ctor) (CppVar "data")
647+ ctorCpp :: Cpp
648+ ctorCpp | Just mn'' <- mn', mn'' /= mn = CppAccessor ctor' (CppVar $ moduleNameToCpp mn'')
649+ | otherwise = ctor'
650+
651+ binderToCpp _ _ b@(ConstructorBinder{}) =
652+ error $ "Invalid ConstructorBinder in binderToCpp: " ++ show b
653+
654+ binderToCpp varName done (NamedBinder _ ident binder) = do
655+ cpp <- binderToCpp varName done binder
656+ return $
657+ CppVariableIntroduction
658+ (identToCpp ident, Just $ CppAny [CppConst])
659+ []
660+ (Just $ CppVar varName)
661+ : cpp
662+
663+ -------------------------------------------------------------------------------------------------
664+ literalToBinderCpp :: String -> [Cpp] -> Literal (Binder Ann) -> m [Cpp]
665+ -------------------------------------------------------------------------------------------------
666+ literalToBinderCpp varName done (NumericLiteral num) =
667+ return [ CppIfElse
668+ (CppBinary Equal (CppVar varName) (CppNumericLiteral num))
669+ (CppBlock done)
670+ Nothing ]
671+
672+ literalToBinderCpp varName done (CharLiteral c) =
673+ return [ CppIfElse
674+ (CppBinary Equal (CppVar varName) (CppCharLiteral c))
675+ (CppBlock done)
676+ Nothing ]
677+
678+ literalToBinderCpp varName done (StringLiteral str) =
679+ return [ CppIfElse
680+ (CppBinary Equal (CppVar varName) (CppStringLiteral str))
681+ (CppBlock done)
682+ Nothing ]
683+
684+ literalToBinderCpp varName done (BooleanLiteral True) =
685+ return [ CppIfElse
686+ (CppVar varName)
687+ (CppBlock done)
688+ Nothing ]
689+
690+ literalToBinderCpp varName done (BooleanLiteral False) =
691+ return [ CppIfElse
692+ (CppUnary
693+ CppNot
694+ (CppVar varName))
695+ (CppBlock done)
696+ Nothing ]
697+
698+ literalToBinderCpp varName done (ObjectLiteral bs) = go done bs
699+ where
700+ go :: [Cpp] -> [(String, Binder Ann)] -> m [Cpp]
701+ go done' [] = return done'
702+ go done' ((prop, binder):bs') = do
703+ propVar <- freshName
704+ done'' <- go done' bs'
705+ cpp <- binderToCpp propVar done'' binder
706+ return $
707+ CppVariableIntroduction
708+ (propVar, Nothing)
709+ []
710+ (Just $ CppIndexer
711+ (CppStringLiteral prop)
712+ (CppVar varName))
713+ : cpp
714+
715+ literalToBinderCpp varName done (ArrayLiteral bs) = do
716+ cpp <- go done 0 bs
717+ let cond =
718+ case length bs of
719+ 0 -> CppBinary
720+ Dot
721+ (CppCast arrayType $ CppVar varName)
722+ (CppApp (CppVar "empty") [])
723+ n -> let var = CppCast arrayType $ CppVar varName
724+ in CppBinary
725+ Equal
726+ (CppBinary Dot var (CppApp (CppVar "size") []))
727+ (CppNumericLiteral (Left (fromIntegral n)))
728+ return [ CppIfElse cond (CppBlock cpp) Nothing ]
729+ where
730+ go :: [Cpp] -> Integer -> [Binder Ann] -> m [Cpp]
731+ go done' _ [] = return done'
732+ go done' index (binder:bs') = do
733+ elVar <- freshName
734+ done'' <- go done' (index + 1) bs'
735+ cpp <- binderToCpp elVar done'' binder
736+ return $
737+ CppVariableIntroduction
738+ (elVar, Nothing)
739+ []
740+ (Just $ CppIndexer
741+ (CppNumericLiteral (Left index))
742+ (CppVar varName))
743+ : cpp
744+
745+ -------------------------------------------------------------------------------------------------
746+ unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
747+ -------------------------------------------------------------------------------------------------
748+ unApp (App _ val arg) args = unApp val (arg : args)
749+ unApp other args = (other, args)
750+
751+ -------------------------------------------------------------------------------------------------
752+ unAbs :: Expr Ann -> [Ident] -> ([Ident], Expr Ann)
753+ -------------------------------------------------------------------------------------------------
754+ unAbs (Abs _ arg val) args = unAbs val (args ++ [arg])
755+ unAbs other args = (args, other)
756+
757+ -------------------------------------------------------------------------------------------------
758+ qualifiedToCpp :: (a -> Ident) -> Qualified a -> Cpp
759+ -------------------------------------------------------------------------------------------------
760+ qualifiedToCpp f (Qualified (Just (ModuleName [ProperName mn'])) a)
761+ | mn' == C.prim = CppVar . runIdent $ f a
762+ qualifiedToCpp f (Qualified (Just mn') a)
763+ | mn /= mn' = CppAccessor (CppVar . identToCpp $ f a) (CppVar (moduleNameToCpp mn'))
764+ qualifiedToCpp f (Qualified _ a) = CppVar $ identToCpp (f a)
765+
766+ -- |
767+ -- Find a class in scope by name, retrieving its list of constraints, function names and types.
768+ --
769+ -------------------------------------------------------------------------------------------------
770+ findClass :: Qualified (ProperName 'ClassName) -> Maybe ([String], [T.Constraint], [(String, T.Type)])
771+ -------------------------------------------------------------------------------------------------
772+ findClass name
773+ | Just (params, fns, constraints) <- M.lookup name (E.typeClasses env),
774+ fns' <- (\(i,t) -> (runIdent i, t)) <$> fns
775+ = Just (fst <$> params, constraints, (sortBy (compare `on` normalizedName . fst) fns'))
776+ findClass _ = Nothing
777+
778+ -------------------------------------------------------------------------------------------------
779+ dataCtors :: [Cpp]
780+ -------------------------------------------------------------------------------------------------
781+ dataCtors = [CppNamespace "data"
782+ [CppEnum Nothing (Just intType) . ("_" :) .
783+ catMaybes . map modCtor . M.keys $ E.dataConstructors env]]
784+ where
785+ modCtor :: Qualified (ProperName a) -> Maybe String
786+ modCtor (Qualified (Just mn') (ProperName name)) | mn' == mn = Just name
787+ modCtor _ = Nothing
788+
789+ -------------------------------------------------------------------------------------------------
790+ curriedForeigns :: m [Cpp]
791+ -------------------------------------------------------------------------------------------------
792+ curriedForeigns = concat <$> mapM go allForeigns
793+ where
794+ go (ident, ty)
795+ | argcnt <- countArgs ty,
796+ argcnt > 1 = do
797+ let name = identToCpp ident
798+ argNames <- replicateM argcnt freshName
799+ return $
800+ [ CppFunction
801+ (curriedName name)
802+ [(head argNames, Just $ CppAny [CppConst, CppRef])]
803+ (Just $ CppAny [])
804+ []
805+ (asReturnBlock $
806+ curriedLambda
807+ (tail argNames)
808+ (CppApp (CppVar name)
809+ (CppVar <$> argNames)))
810+ ]
811+ go _ = return []
812+
813+ allForeigns :: [(Ident, T.Type)]
814+ allForeigns = map (\((_, ident), (ty, _, _)) -> (ident, ty)) .
815+ filter (\((mn', _), (_, kind, _)) -> mn' == mn && kind == E.External) .
816+ M.toList $ E.names env
817+
818+---------------------------------------------------------------------------------------------------
819+asReturnBlock :: Cpp -> Cpp
820+---------------------------------------------------------------------------------------------------
821+asReturnBlock cpp = CppBlock [CppReturn cpp]
822+
823+---------------------------------------------------------------------------------------------------
824+curriedLambda :: [String] -> Cpp -> Cpp
825+---------------------------------------------------------------------------------------------------
826+curriedLambda args ret = foldr (\arg ret' -> CppLambda
827+ [CppCaptureAll]
828+ [(arg, Just $ CppAny [CppConst, CppRef])]
829+ (Just $ CppAny [])
830+ (asReturnBlock ret')
831+ ) ret args
832+
833+---------------------------------------------------------------------------------------------------
834+curriedApp :: [String] -> Expr Ann -> Expr Ann
835+---------------------------------------------------------------------------------------------------
836+curriedApp args vals = foldl (\val arg -> App
837+ nullAnn
838+ val
839+ (Var nullAnn . Qualified Nothing $ Ident arg))
840+ vals args
841+
842+---------------------------------------------------------------------------------------------------
843+countArgs :: T.Type -> Int
844+---------------------------------------------------------------------------------------------------
845+countArgs = go 0
846+ where
847+ go argcnt (T.ForAll _ t _) = go argcnt t
848+ go argcnt (T.TypeApp (T.TypeApp fn _) t) | fn == E.tyFunction = go (argcnt + 1) t
849+ go argcnt (T.ConstrainedType ts t) = go (argcnt + length ts) t
850+ go argcnt _ = argcnt
851+
852+---------------------------------------------------------------------------------------------------
853+extractConstraints :: T.Type -> [T.Constraint]
854+---------------------------------------------------------------------------------------------------
855+extractConstraints = go []
856+ where
857+ go cs (T.ForAll _ t _) = go cs t
858+ go cs (T.TypeApp (T.TypeApp fn _) t) | fn == E.tyFunction = go cs t
859+ go cs (T.ConstrainedType ts t) = go (cs ++ ts) t
860+ go cs _ = cs
861+
862+---------------------------------------------------------------------------------------------------
863+dictIndexerToEnum :: [(Cpp,Cpp)] -> Cpp -> Cpp
864+---------------------------------------------------------------------------------------------------
865+dictIndexerToEnum repls (CppIndexer (CppStringLiteral prop) dict)
866+ | Just cls <- lookup dict repls =
867+ CppBinary Dot
868+ (CppIndexer
869+ (CppAccessor (CppVar . identToCpp $ Ident prop) cls)
870+ (CppCast mapType dict))
871+ (CppVar "second")
872+dictIndexerToEnum _ cpp = cpp
873+
874+---------------------------------------------------------------------------------------------------
875+curriedName :: String -> String
876+---------------------------------------------------------------------------------------------------
877+curriedName name@('$' : _) = name
878+curriedName name = '$' : name
879+
880+---------------------------------------------------------------------------------------------------
881+uncurried' :: Cpp -> Cpp
882+---------------------------------------------------------------------------------------------------
883+uncurried' (CppVar ('$':name)) = CppVar name
884+uncurried' (CppAccessor a b) = CppAccessor (uncurried' a) b
885+uncurried' cpp = cpp
886+
887+---------------------------------------------------------------------------------------------------
888+curriedName' :: Cpp -> Cpp
889+---------------------------------------------------------------------------------------------------
890+curriedName' (CppVar name) = CppVar (curriedName name)
891+curriedName' (CppAccessor a b) = CppAccessor (curriedName' a) b
892+curriedName' cpp = cpp