purescript 0.4.5.1 → 0.4.6
raw patch · 22 files changed
+735/−344 lines, 22 filesdep +timedep ~haskeline
Dependencies added: time
Dependency ranges changed: haskeline
Files
- prelude/prelude.purs +254/−83
- psc/Main.hs +84/−25
- psci/Main.hs +2/−2
- purescript.cabal +3/−3
- src/Language/PureScript.hs +114/−9
- src/Language/PureScript/CodeGen/Externs.hs +1/−1
- src/Language/PureScript/CodeGen/JS.hs +6/−3
- src/Language/PureScript/Declarations.hs +3/−2
- src/Language/PureScript/ModuleDependencies.hs +1/−1
- src/Language/PureScript/Options.hs +5/−1
- src/Language/PureScript/Parser/Common.hs +1/−1
- src/Language/PureScript/Parser/Declarations.hs +14/−3
- src/Language/PureScript/Sugar.hs +2/−2
- src/Language/PureScript/Sugar/BindingGroups.hs +5/−0
- src/Language/PureScript/Sugar/Names.hs +52/−53
- src/Language/PureScript/Sugar/Operators.hs +57/−42
- src/Language/PureScript/TypeChecker.hs +6/−8
- src/Language/PureScript/TypeChecker/Kinds.hs +1/−1
- src/Language/PureScript/TypeChecker/Monad.hs +9/−3
- src/Language/PureScript/TypeChecker/Types.hs +67/−64
- src/Language/PureScript/Values.hs +7/−0
- tests/Main.hs +41/−37
prelude/prelude.purs view
@@ -6,18 +6,22 @@ const :: forall a b. a -> b -> a const a _ = a + on :: forall a b c. (b -> b -> c) -> (a -> b) -> a -> a -> c+ on f g x y = g x `f` g y+ infixr 9 >>> infixr 9 <<< class Category a where id :: forall t. a t t (<<<) :: forall b c d. a c d -> a b c -> a b d- (>>>) :: forall b c d. a b c -> a c d -> a b d + (>>>) :: forall a b c d. (Category a) => a b c -> a c d -> a b d+ (>>>) f g = g <<< f+ instance categoryArr :: Category (->) where id x = x (<<<) f g x = f (g x)- (>>>) f g x = g (f x) infixr 0 $ infixl 0 #@@ -172,46 +176,53 @@ (==) _ _ = false (/=) xs ys = not (xs == ys) + data Ordering = LT | GT | EQ++ instance showOrdering :: Show Ordering where+ show LT = "LT"+ show GT = "GT"+ show EQ = "EQ"++ class Ord a where+ compare :: a -> a -> Ordering+ infixl 4 <++ (<) :: forall a. (Ord a) => a -> a -> Boolean+ (<) a1 a2 = case a1 `compare` a2 of+ LT -> true+ _ -> false+ infixl 4 >- infixl 4 <=- infixl 4 >= - class Ord a where- (<) :: a -> a -> Boolean- (>) :: a -> a -> Boolean- (<=) :: a -> a -> Boolean- (>=) :: a -> a -> Boolean+ (>) :: forall a. (Ord a) => a -> a -> Boolean+ (>) a1 a2 = case a1 `compare` a2 of+ GT -> true+ _ -> false - foreign import numLess "function numLess(n1) {\- \ return function(n2) {\- \ return n1 < n2;\- \ };\- \}" :: Number -> Number -> Boolean+ infixl 4 <= - foreign import numLessEq "function numLessEq(n1) {\- \ return function(n2) {\- \ return n1 <= n2;\- \ };\- \}" :: Number -> Number -> Boolean+ (<=) :: forall a. (Ord a) => a -> a -> Boolean+ (<=) a1 a2 = case a1 `compare` a2 of+ GT -> false+ _ -> true - foreign import numGreater "function numGreater(n1) {\- \ return function(n2) {\- \ return n1 > n2;\- \ };\- \}" :: Number -> Number -> Boolean+ infixl 4 >= - foreign import numGreaterEq "function numGreaterEq(n1) {\- \ return function(n2) {\- \ return n1 >= n2;\- \ };\- \}" :: Number -> Number -> Boolean+ (>=) :: forall a. (Ord a) => a -> a -> Boolean+ (>=) a1 a2 = case a1 `compare` a2 of+ LT -> false+ _ -> true + foreign import numCompare+ "function numCompare(n1) {\+ \ return function(n2) {\+ \ return n1 < n2 ? module.LT : n1 > n2 ? module.GT : module.EQ;\+ \ };\+ \}" :: Number -> Number -> Ordering+ instance ordNumber :: Ord Number where- (<) = numLess- (>) = numGreater- (<=) = numLessEq- (>=) = numGreaterEq+ compare = numCompare infixl 10 & infixl 10 |@@ -323,7 +334,6 @@ module Data.Monoid where import Prelude- import Data.Array (foldl) infixr 6 <> @@ -339,13 +349,30 @@ mempty = [] (<>) = Data.Array.concat - mconcat :: forall m. (Monoid m) => [m] -> m- mconcat = foldl (<>) mempty+module Control.Applicative where + import Prelude++ infixl 4 <*+ infixl 4 *>++ (<*) :: forall a b f. (Applicative f) => f a -> f b -> f a+ (<*) x y = const <$> x <*> y++ (*>) :: forall a b f. (Applicative f) => f a -> f b -> f b+ (*>) x y = const id <$> x <*> y++ lift2 :: forall a b c f. (Applicative f) => (a -> b -> c) -> f a -> f b -> f c+ lift2 f x y = f <$> x <*> y++ lift3 :: forall a b c d f. (Applicative f) => (a -> b -> c -> d) -> f a -> f b -> f c -> f d+ lift3 f x y z = f <$> x <*> y <*> z+ module Control.Monad where import Prelude import Data.Array+ import Data.Traversable replicateM :: forall m a. (Monad m) => Number -> m a -> m [a] replicateM 0 _ = return []@@ -354,13 +381,6 @@ as <- replicateM (n - 1) m return (a : as) - mapM :: forall m a b. (Monad m) => (a -> m b) -> [a] -> m [b]- mapM _ [] = return []- mapM f (a:as) = do- b <- f a- bs <- mapM f as- return (b : bs)- infixr 1 >=> infixr 1 <=< @@ -372,13 +392,6 @@ (<=<) :: forall m a b c. (Monad m) => (b -> m c) -> (a -> m b) -> a -> m c (<=<) = flip (>=>) - sequence :: forall m a. (Monad m) => [m a] -> m [a]- sequence [] = return []- sequence (m:ms) = do- a <- m- as <- sequence ms- return (a : as)- join :: forall m a. (Monad m) => m (m a) -> m a join mm = do m <- mm@@ -392,8 +405,8 @@ when true m = m when false _ = return {} - zipWithM :: forall m a b c. (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]- zipWithM f xs ys = sequence $ zipWith f xs ys+ zipWithA :: forall m a b c. (Applicative m) => (a -> b -> m c) -> [a] -> [b] -> m [c]+ zipWithA f xs ys = sequence (zipWith f xs ys) module Data.Maybe where @@ -408,6 +421,12 @@ fromMaybe :: forall a. a -> Maybe a -> a fromMaybe a = maybe a (id :: forall a. a -> a) + isJust :: forall a. Maybe a -> Boolean+ isJust = maybe false (const true)++ isNothing :: forall a. Maybe a -> Boolean+ isNothing = maybe true (const false)+ instance monadMaybe :: Monad Maybe where return = Just (>>=) m f = maybe Nothing f m@@ -441,6 +460,12 @@ either f _ (Left a) = f a either _ g (Right b) = g b + isLeft :: forall a b. Either a b -> Boolean+ isLeft = either (const true) (const false)++ isRight :: forall a b. Either a b -> Boolean+ isRight = either (const false) (const true)+ instance monadEither :: Monad (Either e) where return = Right (>>=) = either (\e _ -> Left e) (\a f -> f a)@@ -481,14 +506,6 @@ map _ [] = [] map f (x:xs) = f x : map f xs - foldr :: forall a b. (a -> b -> a) -> a -> [b] -> a- foldr f a (b : bs) = f (foldr f a bs) b- foldr _ a [] = a-- foldl :: forall a b. (b -> a -> b) -> b -> [a] -> b- foldl _ b [] = b- foldl f b (a:as) = foldl f (f b a) as- foreign import length "function length(xs) {\ \ return xs.length;\ \}" :: forall a. [a] -> Number@@ -596,20 +613,15 @@ singleton :: forall a. a -> [a] singleton a = [a] - concatMap :: forall a b. [a] -> (a -> [b]) -> [b]- concatMap [] f = []- concatMap (a:as) f = f a `concat` concatMap as f+ concatMap :: forall a b. (a -> [b]) -> [a] -> [b]+ concatMap _ [] = []+ concatMap f (a:as) = f a `concat` concatMap f as filter :: forall a. (a -> Boolean) -> [a] -> [a] filter _ [] = [] filter p (x:xs) | p x = x : filter p xs filter p (_:xs) = filter p xs - find :: forall a. (a -> Boolean) -> [a] -> Maybe a- find _ [] = Nothing- find p (x:xs) | p x = Just x- find p (_:xs) = find p xs- isEmpty :: forall a. [a] -> Boolean isEmpty [] = true isEmpty _ = false@@ -622,14 +634,6 @@ zipWith f (a:as) (b:bs) = f a b : zipWith f as bs zipWith _ _ _ = [] - any :: forall a. (a -> Boolean) -> [a] -> Boolean- any _ [] = false- any p (a:as) = p a || any p as-- all :: forall a. (a -> Boolean) -> [a] -> Boolean- all _ [] = true- all p (a:as) = p a && all p as- drop :: forall a. Number -> [a] -> [a] drop 0 xs = xs drop _ [] = []@@ -640,12 +644,19 @@ take _ [] = [] take n (x:xs) = x : take (n - 1) xs + nub :: forall a. (Eq a) => [a] -> [a]+ nub = nubBy (==)++ nubBy :: forall a. (a -> a -> Boolean) -> [a] -> [a]+ nubBy _ [] = []+ nubBy (==) (x:xs) = x : nubBy (==) (filter (\y -> not (x == y)) xs)+ instance showArray :: (Show a) => Show [a] where show xs = "[" ++ joinWith (map show xs) "," ++ "]" instance monadArray :: Monad [] where return = singleton- (>>=) = concatMap+ (>>=) = flip concatMap instance functorArray :: Functor [] where (<$>) = map@@ -689,6 +700,12 @@ data Tuple a b = Tuple a b + fst :: forall a b. Tuple a b -> a+ fst (Tuple a _) = a++ snd :: forall a b. Tuple a b -> b+ snd (Tuple _ b) = b+ instance showTuple :: (Show a, Show b) => Show (Tuple a b) where show (Tuple a b) = "Tuple(" ++ show a ++ ", " ++ show b ++ ")" @@ -710,6 +727,9 @@ (==) (Tuple a1 b1) (Tuple a2 b2) = a1 == a2 && b1 == b2 (/=) t1 t2 = not (t1 == t2) + instance functorTuple :: Functor (Tuple a) where+ (<$>) f (Tuple x y) = Tuple x (f y)+ module Data.String where foreign import lengthS "function lengthS(s) {\@@ -758,7 +778,7 @@ foreign import split "function split(sep) {\ \ return function(s) {\- \ return s.split(s);\+ \ return s.split(sep);\ \ };\ \}" :: String -> String -> [String] @@ -909,19 +929,19 @@ \}" :: Number -> Number foreign import max "function max(n1){\- \ return function(n2) {\ + \ return function(n2) {\ \ return Math.max(n1, n2);\ \ }\ \}" :: Number -> Number -> Number foreign import min "function min(n1){\- \ return function(n2) {\ + \ return function(n2) {\ \ return Math.min(n1, n2);\ \ }\ \}" :: Number -> Number -> Number foreign import pow "function pow(n){\- \ return function(p) {\ + \ return function(p) {\ \ return Math.pow(n, p);\ \ }\ \}" :: Number -> Number -> Number@@ -1039,7 +1059,7 @@ \ return Math.random();\ \}" :: forall e. Eff (random :: Random | e) Number -module Control.Monad.Error where+module Control.Monad.Eff.Error where import Control.Monad.Eff @@ -1206,7 +1226,7 @@ class Enum a where toEnum :: Number -> Maybe a fromEnum :: a -> Number- + module Text.Parsing.Read where class Read a where@@ -1223,3 +1243,154 @@ instance readNumber :: Read Number where read = readNumberImpl++module Data.Foldable where++ import Prelude+ import Control.Applicative+ import Data.Either+ import Data.Eq+ import Data.Maybe+ import Data.Monoid+ import Data.Tuple++ class Foldable f where+ foldr :: forall a b. (a -> b -> b) -> b -> f a -> b+ foldl :: forall a b. (b -> a -> b) -> b -> f a -> b+ foldMap :: forall a m. (Monoid m) => (a -> m) -> f a -> m++ instance foldableArray :: Foldable [] where+ foldr _ z [] = z+ foldr f z (x:xs) = x `f` (foldr f z xs)++ foldl _ z [] = z+ foldl f z (x:xs) = foldl f (z `f` x) xs++ foldMap _ [] = mempty+ foldMap f (x:xs) = f x <> foldMap f xs++ instance foldableEither :: Foldable (Either a) where+ foldr _ z (Left _) = z+ foldr f z (Right x) = x `f` z++ foldl _ z (Left _) = z+ foldl f z (Right x) = z `f` x++ foldMap f (Left _) = mempty+ foldMap f (Right x) = f x++ instance foldableMaybe :: Foldable Maybe where+ foldr _ z Nothing = z+ foldr f z (Just x) = x `f` z++ foldl _ z Nothing = z+ foldl f z (Just x) = z `f` x++ foldMap f Nothing = mempty+ foldMap f (Just x) = f x++ instance foldableRef :: Foldable Ref where+ foldr f z (Ref x) = x `f` z++ foldl f z (Ref x) = z `f` x++ foldMap f (Ref x) = f x++ instance foldableTuple :: Foldable (Tuple a) where+ foldr f z (Tuple _ x) = x `f` z++ foldl f z (Tuple _ x) = z `f` x++ foldMap f (Tuple _ x) = f x++ fold :: forall f m. (Foldable f, Monoid m) => f m -> m+ fold = foldMap id++ traverse_ :: forall a b f m. (Applicative m, Foldable f) => (a -> m b) -> f a -> m {}+ traverse_ f = foldr ((*>) <<< f) (pure {})++ for_ :: forall a b f m. (Applicative m, Foldable f) => f a -> (a -> m b) -> m {}+ for_ = flip traverse_++ sequence_ :: forall a f m. (Applicative m, Foldable f) => f (m a) -> m {}+ sequence_ = traverse_ id++ mconcat :: forall f m. (Foldable f, Monoid m) => f m -> m+ mconcat = foldl (<>) mempty++ and :: forall f. (Foldable f) => f Boolean -> Boolean+ and = foldl (&&) true++ or :: forall f. (Foldable f) => f Boolean -> Boolean+ or = foldl (||) false++ any :: forall a f. (Foldable f) => (a -> Boolean) -> f a -> Boolean+ any p = or <<< foldMap (\x -> [p x])++ all :: forall a f. (Foldable f) => (a -> Boolean) -> f a -> Boolean+ all p = and <<< foldMap (\x -> [p x])++ sum :: forall f. (Foldable f) => f Number -> Number+ sum = foldl (+) 0++ product :: forall f. (Foldable f) => f Number -> Number+ product = foldl (*) 1++ elem :: forall a f. (Eq a, Foldable f) => a -> f a -> Boolean+ elem = any <<< (==)++ notElem :: forall a f. (Eq a, Foldable f) => a -> f a -> Boolean+ notElem x = not <<< elem x++ find :: forall a f. (Foldable f) => (a -> Boolean) -> f a -> Maybe a+ find p f = case foldMap (\x -> if p x then [x] else []) f of+ (x:_) -> Just x+ [] -> Nothing++module Data.Traversable where++ import Prelude+ import Data.Array ((:))+ import Data.Either+ import Data.Eq+ import Data.Foldable+ import Data.Maybe+ import Data.Tuple++ class Traversable t where+ traverse :: forall a b m. (Applicative m) => (a -> m b) -> t a -> m (t b)+ sequence :: forall a m. (Applicative m) => t (m a) -> m (t a)++ instance traversableArray :: Traversable [] where+ traverse _ [] = pure []+ traverse f (x:xs) = (:) <$> (f x) <*> traverse f xs++ sequence [] = pure []+ sequence (x:xs) = (:) <$> x <*> sequence xs++ instance traversableEither :: Traversable (Either a) where+ traverse _ (Left x) = pure (Left x)+ traverse f (Right x) = Right <$> f x++ sequence (Left x) = pure (Left x)+ sequence (Right x) = Right <$> x++ instance traversableRef :: Traversable Ref where+ traverse f (Ref x) = Ref <$> f x++ sequence (Ref x) = Ref <$> x++ instance traversableMaybe :: Traversable Maybe where+ traverse _ Nothing = pure Nothing+ traverse f (Just x) = Just <$> f x++ sequence Nothing = pure Nothing+ sequence (Just x) = Just <$> x++ instance traversableTuple :: Traversable (Tuple a) where+ traverse f (Tuple x y) = Tuple x <$> f y++ sequence (Tuple x y) = Tuple x <$> y++ for :: forall a b m t. (Applicative m, Traversable t) => t a -> (a -> m b) -> m (t b)+ for x f = traverse f x
psc/Main.hs view
@@ -12,48 +12,99 @@ -- ----------------------------------------------------------------------------- +{-# LANGUAGE GeneralizedNewtypeDeriving #-}+ module Main where -import qualified Language.PureScript as P-import System.Console.CmdTheLine import Control.Applicative-import Control.Monad (forM)+import Control.Monad.Error++import Data.Version (showVersion)++import System.Console.CmdTheLine+import System.Directory+ (doesFileExist, getModificationTime, createDirectoryIfMissing)+import System.FilePath (takeDirectory) import System.Exit (exitSuccess, exitFailure)-import qualified System.IO.UTF8 as U+import System.IO.Error (tryIOError)+ import Text.Parsec (ParseError)++import qualified Language.PureScript as P import qualified Paths_purescript as Paths-import Data.Version (showVersion)+import qualified System.IO.UTF8 as U preludeFilename :: IO FilePath preludeFilename = Paths.getDataFileName "prelude/prelude.purs" -readInput :: Maybe [FilePath] -> IO (Either ParseError [P.Module])-readInput Nothing = P.runIndentParser "" P.parseModules <$> getContents-readInput (Just input) = fmap (fmap concat . sequence) $ forM input $ \inputFile -> do+readInput :: Maybe [FilePath] -> IO (Either ParseError [(FilePath, P.Module)])+readInput Nothing = do+ text <- getContents+ return $ map ((,) undefined) <$> P.runIndentParser "" P.parseModules text+readInput (Just input) = fmap collect $ forM input $ \inputFile -> do text <- U.readFile inputFile- return $ P.runIndentParser inputFile P.parseModules text+ return $ (inputFile, P.runIndentParser inputFile P.parseModules text)+ where+ collect :: [(FilePath, Either ParseError [P.Module])] -> Either ParseError [(FilePath, P.Module)]+ collect = fmap concat . sequence . map (\(fp, e) -> fmap (map ((,) fp)) e) -compile :: P.Options -> Maybe [FilePath] -> Maybe FilePath -> Maybe FilePath -> IO ()-compile opts input output externs = do+newtype Make a = Make { unMake :: ErrorT String IO a } deriving (Functor, Monad, MonadIO, MonadError String)++runMake :: Make a -> IO (Either String a)+runMake = runErrorT . unMake++makeIO :: IO a -> Make a+makeIO = Make . ErrorT . fmap (either (Left . show) Right) . tryIOError++instance P.MonadMake Make where+ getTimestamp path = makeIO $ do+ exists <- doesFileExist path+ case exists of+ True -> Just <$> getModificationTime path+ False -> return Nothing+ readTextFile path = makeIO $ do+ U.putStrLn $ "Reading " ++ path+ U.readFile path+ writeTextFile path text = makeIO $ do+ mkdirp path+ U.putStrLn $ "Writing " ++ path+ U.writeFile path text+ liftError = either throwError return++compile :: Bool -> P.Options -> Maybe [FilePath] -> Maybe FilePath -> Maybe FilePath -> IO ()+compile makeMode opts input output externs = do modules <- readInput input case modules of Left err -> do U.print err exitFailure Right ms ->- case P.compile opts ms of- Left err -> do- U.putStrLn err- exitFailure- Right (js, exts, _) -> do- case output of- Just path -> U.writeFile path js- Nothing -> U.putStrLn js- case externs of- Nothing -> return ()- Just filePath -> U.writeFile filePath exts- exitSuccess+ case makeMode of+ True -> do+ e <- runMake $ P.make opts ms+ case e of+ Left err -> do+ U.putStrLn err+ exitFailure+ Right _ -> do+ exitSuccess+ False ->+ case P.compile opts (map snd ms) of+ Left err -> do+ U.putStrLn err+ exitFailure+ Right (js, exts, _) -> do+ case output of+ Just path -> mkdirp path >> U.writeFile path js+ Nothing -> U.putStrLn js+ case externs of+ Just path -> mkdirp path >> U.writeFile path exts+ Nothing -> return ()+ exitSuccess +mkdirp :: FilePath -> IO ()+mkdirp = createDirectoryIfMissing True . takeDirectory+ useStdIn :: Term Bool useStdIn = value . flag $ (optInfo [ "s", "stdin" ]) { optDoc = "Read from standard input" }@@ -94,6 +145,10 @@ noOpts = value $ flag $ (optInfo [ "no-opts" ]) { optDoc = "Skip the optimization phase." } +make :: Term Bool+make = value $ flag $ (optInfo [ "make" ])+ { optDoc = "Run in make mode" }+ browserNamespace :: Term String browserNamespace = value $ opt "PS" $ (optInfo [ "browser-namespace" ]) { optDoc = "Specify the namespace that PureScript modules will be exported to when running in the browser." }@@ -102,8 +157,12 @@ dceModules = value $ optAll [] $ (optInfo [ "m", "module" ]) { optDoc = "Enables dead code elimination, all code which is not a transitive dependency of a specified module will be removed. This argument can be used multiple times." } +codeGenModules :: Term [String]+codeGenModules = value $ optAll [] $ (optInfo [ "codegen" ])+ { optDoc = "A list of modules for which Javascript and externs should be generated. This argument can be used multiple times." }+ options :: Term P.Options-options = P.Options <$> tco <*> performRuntimeTypeChecks <*> magicDo <*> runMain <*> noOpts <*> browserNamespace <*> dceModules+options = P.Options <$> tco <*> performRuntimeTypeChecks <*> magicDo <*> runMain <*> noOpts <*> browserNamespace <*> dceModules <*> codeGenModules stdInOrInputFiles :: FilePath -> Term (Maybe [FilePath]) stdInOrInputFiles prelude = combine <$> useStdIn <*> (not <$> noPrelude) <*> inputFiles@@ -113,7 +172,7 @@ combine True _ _ = Nothing term :: FilePath -> Term (IO ())-term prelude = compile <$> options <*> stdInOrInputFiles prelude <*> outputFile <*> externsFile+term prelude = compile <$> make <*> options <*> stdInOrInputFiles prelude <*> outputFile <*> externsFile termInfo :: TermInfo termInfo = defTI
psci/Main.hs view
@@ -195,7 +195,7 @@ -- | Compilation options. -- options :: P.Options-options = P.Options True False True (Just "Main") True "PS" []+options = P.Options True False True (Just "Main") True "PS" [] [] -- | -- Makes a volatile module to execute the current expression.@@ -204,7 +204,7 @@ createTemporaryModule exec PSCiState{psciImportedModuleNames = imports, psciLetBindings = lets} value = let moduleName = P.ModuleName [P.ProperName "Main"]- importDecl m = P.ImportDeclaration m Nothing+ importDecl m = P.ImportDeclaration m Nothing Nothing traceModule = P.ModuleName [P.ProperName "Debug", P.ProperName "Trace"] trace = P.Var (P.Qualified (Just traceModule) (P.Ident "print")) itValue = foldl (\x f -> f x) value lets
purescript.cabal view
@@ -1,5 +1,5 @@ name: purescript-version: 0.4.5.1+version: 0.4.6 cabal-version: >=1.8 build-type: Custom license: MIT@@ -21,7 +21,7 @@ build-depends: base >=4 && <5, cmdtheline -any, containers -any, directory -any, filepath -any, mtl -any, parsec -any, syb -any, transformers -any, utf8-string -any, pattern-arrows -any, monad-unify >= 0.2 && < 0.3,- xdg-basedir -any+ xdg-basedir -any, time -any if (!os(windows)) build-depends: unix -any exposed-modules: Data.Generics.Extras@@ -95,7 +95,7 @@ executable psci build-depends: base >=4 && <5, containers -any, directory -any, filepath -any,- mtl -any, parsec -any, haskeline <=0.7.1.1, purescript -any,+ mtl -any, parsec -any, haskeline -any, purescript -any, syb -any, transformers -any, utf8-string -any, process -any, xdg-basedir -any, cmdtheline -any main-is: Main.hs
src/Language/PureScript.hs view
@@ -13,7 +13,7 @@ -- ----------------------------------------------------------------------------- -module Language.PureScript (module P, compile) where+module Language.PureScript (module P, compile, compile', MonadMake(..), make) where import Language.PureScript.Values as P import Language.PureScript.Types as P@@ -34,10 +34,12 @@ import qualified Language.PureScript.Constants as C import Data.List (intercalate)-import Data.Maybe (mapMaybe)+import Data.Time.Clock+import Data.Maybe (fromMaybe, mapMaybe) import Control.Monad.State.Lazy import Control.Applicative ((<$>), (<*>), pure) import qualified Data.Map as M+import System.FilePath (pathSeparator) -- | -- Compile a collection of modules@@ -59,23 +61,126 @@ -- * Pretty-print the generated Javascript -- compile :: Options -> [Module] -> Either String (String, String, Environment)-compile opts ms = do+compile = compile' initEnvironment++compile' :: Environment -> Options -> [Module] -> Either String (String, String, Environment)+compile' env opts ms = do sorted <- sortModules ms desugared <- desugar sorted- (elaborated, env) <- runCheck $ forM desugared $ \(Module moduleName' decls exps) -> do+ (elaborated, env') <- runCheck' env $ forM desugared $ \(Module moduleName' decls exps) -> do modify (\s -> s { checkCurrentModule = Just moduleName' }) Module moduleName' <$> typeCheckAll mainModuleIdent moduleName' decls <*> pure exps regrouped <- createBindingGroupsModule . collapseBindingGroupsModule $ elaborated let entryPoints = moduleNameFromString `map` optionsModules opts let elim = if null entryPoints then regrouped else eliminateDeadCode entryPoints regrouped- let js = mapMaybe (flip (moduleToJs opts) env) elim- let exts = intercalate "\n" . map (`moduleToPs` env) $ elim- js' <- case mainModuleIdent of+ let codeGenModules = moduleNameFromString `map` optionsCodeGenModules opts+ let modulesToCodeGen = if null codeGenModules then elim else filter (\(Module mn _ _) -> mn `elem` codeGenModules) elim+ let js = mapMaybe (flip (moduleToJs opts) env') modulesToCodeGen+ let exts = intercalate "\n" . map (`moduleToPs` env') $ modulesToCodeGen+ js' <- generateMain env' opts js+ return (prettyPrintJS [wrapExportsContainer opts js'], exts, env')+ where+ mainModuleIdent = moduleNameFromString <$> optionsMain opts++generateMain :: Environment -> Options -> [JS] -> Either String [JS]+generateMain env opts js =+ case moduleNameFromString <$> optionsMain opts of Just mmi -> do when ((mmi, Ident C.main) `M.notMember` names env) $ Left $ show mmi ++ "." ++ C.main ++ " is undefined" return $ js ++ [JSApp (JSAccessor C.main (JSAccessor (moduleNameToJs mmi) (JSVar C._ps))) []] _ -> return js- return (prettyPrintJS [wrapExportsContainer opts js'], exts, env)++-- |+-- A type class which collects the IO actions we need to be able to run in "make" mode+--+class MonadMake m where+ -- |+ -- Get a file timestamp+ --+ getTimestamp :: FilePath -> m (Maybe UTCTime)++ -- |+ -- Read a file as a string+ --+ readTextFile :: FilePath -> m String++ -- |+ -- Write a text file+ --+ writeTextFile :: FilePath -> String -> m ()++ -- |+ -- Report an error+ --+ liftError :: Either String a -> m a+++-- |+-- Compiles in "make" mode, compiling each module separately to a js files and an externs file+--+-- If timestamps have not changed, the externs file can be used to provide the module's types without+-- having to typecheck the module again.+--+make :: (Functor m, Monad m, MonadMake m) => Options -> [(FilePath, Module)] -> m ()+make opts ms = do+ let filePathMap = M.fromList (map (\(fp, (Module mn _ _)) -> (mn, fp)) ms)++ sorted <- liftError $ sortModules (map snd ms)++ marked <- forM sorted $ \m@(Module moduleName' _ _) -> do+ let filePath = toFileName moduleName'++ jsFile = "js" ++ pathSeparator : filePath ++ ".js"+ externsFile = "externs" ++ pathSeparator : filePath ++ ".externs"+ inputFile = fromMaybe (error "Input file is undefined in make") $ M.lookup moduleName' filePathMap++ jsTimestamp <- getTimestamp jsFile+ externsTimestamp <- getTimestamp externsFile+ inputTimestamp <- getTimestamp inputFile++ case inputTimestamp < min jsTimestamp externsTimestamp of+ True -> do+ externs <- readTextFile externsFile+ externsModules <- liftError . either (Left . show) Right $ P.runIndentParser externsFile P.parseModules externs+ case externsModules of+ [m'@(Module moduleName'' _ _)] | moduleName' == moduleName'' -> return (True, m')+ _ -> liftError . Left $ "Externs file " ++ externsFile ++ " was invalid"+ False -> return (False, m)++ desugared <- liftError $ zip (map fst marked) <$> desugar (map snd marked)++ go initEnvironment desugared+ where- mainModuleIdent = moduleNameFromString <$> optionsMain opts+ go :: (Functor m, Monad m, MonadMake m) => Environment -> [(Bool, Module)] -> m ()+ go _ [] = return ()+ go env ((True, Module moduleName' typings _) : ms') = do+ (_, env') <- liftError . runCheck' env $ do+ modify (\s -> s { checkCurrentModule = Just moduleName' })+ typeCheckAll Nothing moduleName' typings++ go env' ms'+ go env ((False, Module moduleName' decls exps) : ms') = do+ let filePath = toFileName moduleName'+ jsFile = "js" ++ pathSeparator : filePath ++ ".js"+ externsFile = "externs" ++ pathSeparator : filePath ++ ".externs"++ (elaborated, env') <- liftError . runCheck' env $ do+ modify (\s -> s { checkCurrentModule = Just moduleName' })+ typeCheckAll Nothing moduleName' decls++ regrouped <- liftError . createBindingGroups moduleName' . collapseBindingGroups $ elaborated++ let mod' = Module moduleName' regrouped exps+ js = moduleToJs opts mod' env'+ exts = moduleToPs mod' env'+ js' = maybe "" (prettyPrintJS . return . wrapExportsContainer opts . return) js++ writeTextFile jsFile js'+ writeTextFile externsFile exts++ go env' ms'++ toFileName :: ModuleName -> FilePath+ toFileName (ModuleName ps) = intercalate [pathSeparator] . map runProperName $ ps
src/Language/PureScript/CodeGen/Externs.hs view
@@ -57,7 +57,7 @@ printDctor dctor = case dctor `lookup` tys of Nothing -> Nothing Just tyArgs -> Just $ show dctor ++ " " ++ unwords (map prettyPrintTypeAtom tyArgs)- tell ["data " ++ show pn ++ " " ++ unwords args ++ " = " ++ intercalate " | " (mapMaybe printDctor dctors')]+ tell ["data " ++ show pn ++ " " ++ unwords args ++ (if null dctors' then "" else " = " ++ intercalate " | " (mapMaybe printDctor dctors'))] Just (_, TypeSynonym) -> case Qualified (Just moduleName) pn `M.lookup` typeSynonyms env of Nothing -> error $ show pn ++ " has no type synonym info in exportToPs"
src/Language/PureScript/CodeGen/JS.hs view
@@ -52,11 +52,14 @@ case jsDecls of [] -> Nothing _ -> Just $ JSAssignment (JSAccessor (moduleNameToJs name) (JSVar C._ps)) $- JSApp (JSFunction Nothing ["module"] (JSBlock $ jsDecls ++ [JSReturn $ JSVar "module"]))- [JSBinary Or (JSAccessor (moduleNameToJs name) (JSVar C._ps)) (JSObjectLiteral [])]+ JSApp (JSFunction Nothing [] (JSBlock $+ JSVariableIntroduction "module" (Just $ JSObjectLiteral []) :+ jsDecls +++ jsExports +++ [JSReturn $ JSVar "module"])) [] where jsDecls = (concat $ mapMaybe (\decl -> fmap (map $ optimize opts) $ declToJs opts name decl env) decls)- ++ concatMap exportToJs exps+ jsExports = concatMap exportToJs exps moduleToJs _ _ _ = error "Exports should have been elaborated in name desugaring" -- |
src/Language/PureScript/Declarations.hs view
@@ -117,9 +117,10 @@ -- | FixityDeclaration Fixity String -- |- -- A module import (module name, optional set of identifiers to import)+ -- A module import (module name, optional set of identifiers to import, optional "qualified as"+ -- name) --- | ImportDeclaration ModuleName (Maybe [DeclarationRef])+ | ImportDeclaration ModuleName (Maybe [DeclarationRef]) (Maybe ModuleName) -- | -- A type class declaration (name, argument, member declarations) --
src/Language/PureScript/ModuleDependencies.hs view
@@ -47,7 +47,7 @@ qualifiedProperNames (Qualified (Just mn) _) = [mn] qualifiedProperNames _ = [] imports :: Declaration -> [ModuleName]- imports (ImportDeclaration mn _) = [mn]+ imports (ImportDeclaration mn _ _) = [mn] imports _ = [] getModuleName :: Module -> ModuleName
src/Language/PureScript/Options.hs view
@@ -49,10 +49,14 @@ -- The modules to keep while enabling dead code elimination -- , optionsModules :: [String]+ -- |+ -- The modules to code gen+ --+ , optionsCodeGenModules :: [String] } deriving Show -- | -- Default compiler options -- defaultOptions :: Options-defaultOptions = Options False False False Nothing False "PS" []+defaultOptions = Options False False False Nothing False "PS" [] []
src/Language/PureScript/Parser/Common.hs view
@@ -328,7 +328,7 @@ -- Parse an identifier in backticks or an operator -- parseIdentInfix :: P.Parsec String ParseState (Qualified Ident)-parseIdentInfix = P.between tick tick (parseQualified (Ident <$> identifier)) <|> Qualified Nothing <$> (Op <$> operator)+parseIdentInfix = P.between tick tick (parseQualified (Ident <$> identifier)) <|> (parseQualified (Op <$> operator)) -- | -- Mark the current indentation level
src/Language/PureScript/Parser/Declarations.hs view
@@ -99,9 +99,20 @@ parseImportDeclaration = do reserved "import" indented- moduleName' <- moduleName- idents <- P.optionMaybe $ parens $ commaSep1 parseDeclarationRef- return $ ImportDeclaration moduleName' idents+ qualImport <|> stdImport+ where+ stdImport = do+ moduleName' <- moduleName+ idents <- P.optionMaybe $ parens $ commaSep parseDeclarationRef+ return $ ImportDeclaration moduleName' idents Nothing+ qualImport = do+ reserved "qualified"+ indented+ moduleName' <- moduleName+ idents <- P.optionMaybe $ parens $ commaSep parseDeclarationRef+ reserved "as"+ asQ <- moduleName+ return $ ImportDeclaration moduleName' idents (Just asQ) parseDeclarationRef :: P.Parsec String ParseState DeclarationRef parseDeclarationRef = ValueRef <$> parseIdent
src/Language/PureScript/Sugar.hs view
@@ -49,11 +49,11 @@ -- * Qualify any unqualified names and types -- desugar :: [Module] -> Either String [Module]-desugar = rebracket- >=> desugarDo+desugar = desugarDo >=> desugarLetBindings >>> desugarCasesModule >=> desugarImports+ >=> rebracket >=> desugarTypeDeclarationsModule >=> desugarTypeClasses >=> createBindingGroupsModule
src/Language/PureScript/Sugar/BindingGroups.hs view
@@ -25,6 +25,7 @@ import Data.Graph import Data.Generics import Data.List (nub, intersect)+import Data.Maybe (mapMaybe) import Control.Applicative ((<$>), (<*>), pure) import Language.PureScript.Declarations@@ -89,6 +90,10 @@ usedProperNames moduleName = nub . everything (++) (mkQ [] usedNames) where usedNames :: Type -> [ProperName]+ usedNames (ConstrainedType constraints _) = flip mapMaybe constraints $ \qual ->+ case qual of+ (Qualified (Just moduleName') name, _) | moduleName == moduleName' -> Just name+ _ -> Nothing usedNames (TypeConstructor (Qualified (Just moduleName') name)) | moduleName == moduleName' = [name] usedNames _ = []
src/Language/PureScript/Sugar/Names.hs view
@@ -16,7 +16,7 @@ desugarImports ) where -import Data.Maybe (fromMaybe)+import Data.Maybe (fromMaybe, isJust) import Data.Generics (extM, mkM, everywhereM) import Data.Generics.Extras (mkS, extS, everywhereWithContextM') @@ -61,19 +61,19 @@ -- | -- Local names for types within a module mapped to to their qualified names --- { importedTypes :: M.Map ProperName (Qualified ProperName)+ { importedTypes :: M.Map (Qualified ProperName) (Qualified ProperName) -- | -- Local names for data constructors within a module mapped to to their qualified names --- , importedDataConstructors :: M.Map ProperName (Qualified ProperName)+ , importedDataConstructors :: M.Map (Qualified ProperName) (Qualified ProperName) -- | -- Local names for classes within a module mapped to to their qualified names --- , importedTypeClasses :: M.Map ProperName (Qualified ProperName)+ , importedTypeClasses :: M.Map (Qualified ProperName) (Qualified ProperName) -- | -- Local names for values within a module mapped to to their qualified names --- , importedValues :: M.Map Ident (Qualified Ident)+ , importedValues :: M.Map (Qualified Ident) (Qualified Ident) } deriving (Show) -- |@@ -89,8 +89,11 @@ -- | -- Adds an empty module to an ExportEnvironment. ---addEmptyModule :: ExportEnvironment -> ModuleName -> ExportEnvironment-addEmptyModule env name = M.insert name (Exports [] [] []) env+addEmptyModule :: ExportEnvironment -> ModuleName -> Either String ExportEnvironment+addEmptyModule env name =+ if name `M.member` env+ then throwError $ "Module '" ++ show name ++ "' has been defined more than once"+ else return $ M.insert name (Exports [] [] []) env -- | -- Adds a type belonging to a module to the export environment.@@ -187,8 +190,14 @@ ValueDeclaration name nameKind [] Nothing <$> everywhereWithContextM' [] (mkS bindFunctionArgs `extS` bindBinders) val where bindFunctionArgs bound (Abs (Left arg) val') = return (arg : bound, Abs (Left arg) val')- bindFunctionArgs bound (Var name'@(Qualified Nothing ident)) | ident `notElem` bound = (,) bound <$> (Var <$> updateValueName name')- bindFunctionArgs bound (Var name'@(Qualified (Just _) _)) = (,) bound <$> (Var <$> updateValueName name')+ bindFunctionArgs bound (Var name'@(Qualified Nothing ident)) | ident `notElem` bound =+ (,) bound <$> (Var <$> updateValueName name')+ bindFunctionArgs bound (Var name'@(Qualified (Just _) _)) =+ (,) bound <$> (Var <$> updateValueName name')+ bindFunctionArgs bound (BinaryNoParens name'@(Qualified Nothing ident) v1 v2) | ident `notElem` bound =+ (,) bound <$> (BinaryNoParens <$> updateValueName name' <*> pure v1 <*> pure v2)+ bindFunctionArgs bound (BinaryNoParens name'@(Qualified (Just _) _) v1 v2) =+ (,) bound <$> (BinaryNoParens <$> updateValueName name' <*> pure v1 <*> pure v2) bindFunctionArgs bound other = return (bound, other) bindBinders :: [Ident] -> CaseAlternative -> Either String ([Ident], CaseAlternative) bindBinders bound c@(CaseAlternative bs _ _) = return (binderNames bs ++ bound, c)@@ -204,38 +213,26 @@ updateType t = return t updateConstraints = mapM (\(name, ts) -> (,) <$> updateClassName name <*> pure ts) - updateTypeName (Qualified Nothing name) = update "type" importedTypes name- updateTypeName (Qualified (Just mn') name) = do- modExports <- getExports mn'- case name `lookup` exportedTypes modExports of- Nothing -> throwError $ "Unknown type '" ++ show (Qualified (Just mn') name) ++ "'"- _ -> return $ Qualified (Just mn') name-- updateDataConstructorName (Qualified Nothing name) = update "data constructor" importedDataConstructors name- updateDataConstructorName (Qualified (Just mn') name) = do- modExports <- getExports mn'- let allDcons = join $ snd `map` exportedTypes modExports- if name `elem` allDcons- then return $ Qualified (Just mn') name- else throwError $ "Unknown data constructor '" ++ show (Qualified (Just mn') name) ++ "'"-- updateClassName (Qualified Nothing name) = update "type class" importedTypeClasses name- updateClassName (Qualified (Just mn') name) = check "type class" exportedTypeClasses mn' name-- updateValueName (Qualified Nothing name) = update "value" importedValues name- updateValueName (Qualified (Just mn') name) = check "value" exportedValues mn' name-- -- Replace an unqualified name with a qualified- update :: (Ord a, Show a) => String -> (ImportEnvironment -> M.Map a (Qualified a)) -> a -> Either String (Qualified a)- update t get name = maybe (throwError $ "Unknown " ++ t ++ " '" ++ show name ++ "'") return $ M.lookup name (get imports)+ updateTypeName = update "type" importedTypes (\mes -> isJust . (`lookup` (exportedTypes mes)))+ updateClassName = update "type class" importedTypeClasses (flip elem . exportedTypeClasses)+ updateValueName = update "value" importedValues (flip elem . exportedValues)+ updateDataConstructorName = update "data constructor" importedDataConstructors (\mes -> flip elem (join $ snd `map` exportedTypes mes)) - -- Check that a qualified name is valid- check :: (Show a, Eq a) => String -> (Exports -> [a]) -> ModuleName -> a -> Either String (Qualified a)- check t get mn' name = do- modExports <- getExports mn'- if name `elem` get modExports- then return $ Qualified (Just mn') name- else throwError $ "Unknown " ++ t ++ " '" ++ show (Qualified (Just mn') name) ++ "'"+ -- Update names so unqualified references become qualified, and locally qualified references+ -- are replaced with their canoncial qualified names (e.g. M.Map -> Data.Map.Map)+ update :: (Ord a, Show a) => String+ -> (ImportEnvironment -> M.Map (Qualified a) (Qualified a))+ -> (Exports -> a -> Bool)+ -> (Qualified a)+ -> Either String (Qualified a)+ update t getI checkE qname@(Qualified mn' name) = case (M.lookup qname (getI imports), mn') of+ (Just qname', _) -> return qname'+ (Nothing, Just mn'') -> do+ modExports <- getExports mn''+ if checkE modExports name+ then return qname+ else throwError $ "Unknown " ++ t ++ " '" ++ show (qname) ++ "'"+ _ -> throwError $ "Unknown " ++ t ++ " '" ++ show name ++ "'" -- Gets the exports for a module, or an error message if the module doesn't exist getExports :: ModuleName -> Either String Exports@@ -255,7 +252,9 @@ -- Add all of the exported declarations from a module to the global export environment addModule :: ExportEnvironment -> Module -> Either String ExportEnvironment- addModule env m@(Module mn ds _) = rethrowForModule m $ foldM (addDecl mn) (addEmptyModule env mn) ds+ addModule env m@(Module mn ds _) = do+ env' <- addEmptyModule env mn+ rethrowForModule m $ foldM (addDecl mn) env' ds -- Add a declaration from a module to the global export environment addDecl :: ModuleName -> ExportEnvironment -> Declaration -> Either String ExportEnvironment@@ -329,10 +328,10 @@ -- Finds the imports within a module, mapping the imported module name to an optional set of -- explicitly imported declarations. ---findImports :: [Declaration] -> M.Map ModuleName (Maybe ExplicitImports)+findImports :: [Declaration] -> M.Map ModuleName (Maybe ExplicitImports, Maybe ModuleName) findImports = foldl findImports' M.empty where- findImports' result (ImportDeclaration mn expl) = M.insert mn expl result+ findImports' result (ImportDeclaration mn expl qual) = M.insert mn (expl, qual) result findImports' result _ = result -- |@@ -343,17 +342,17 @@ foldM resolveImport' (ImportEnvironment M.empty M.empty M.empty M.empty) (M.toList scope) where -- A Map from module name to imports from that module, where Nothing indicates everything is to be imported- scope :: M.Map ModuleName (Maybe ExplicitImports)- scope = M.insert currentModule Nothing (findImports decls)- resolveImport' imp (mn, i) = do- m <- maybe (throwError $ "Cannot import unknown module '" ++ show mn ++ "'") return $ mn `M.lookup` env- resolveImport currentModule mn m imp i+ scope :: M.Map ModuleName (Maybe ExplicitImports, Maybe ModuleName)+ scope = M.insert currentModule (Nothing, Nothing) (findImports decls)+ resolveImport' imp (mn, (explImports, impQual)) = do+ modExports <- maybe (throwError $ "Cannot import unknown module '" ++ show mn ++ "'") return $ mn `M.lookup` env+ resolveImport currentModule mn modExports imp impQual explImports -- | -- Extends the local environment for a module by resolving an import of another module. ---resolveImport :: ModuleName -> ModuleName -> Exports -> ImportEnvironment -> Maybe ExplicitImports -> Either String ImportEnvironment-resolveImport currentModule importModule exps imps = maybe importAll (foldM importExplicit imps)+resolveImport :: ModuleName -> ModuleName -> Exports -> ImportEnvironment -> Maybe ModuleName -> Maybe ExplicitImports-> Either String ImportEnvironment+resolveImport currentModule importModule exps imps impQual = maybe importAll (foldM importExplicit imps) where -- Import everything from a module@@ -387,9 +386,9 @@ allExportedDataConstructors name = fromMaybe [] $ name `lookup` exportedTypes exps -- Add something to the ImportEnvironment if it does not already exist there- updateImports :: (Ord a, Show a) => M.Map a (Qualified a) -> a -> Either String (M.Map a (Qualified a))- updateImports m name = case M.lookup name m of- Nothing -> return $ M.insert name (Qualified (Just importModule) name) m+ updateImports :: (Ord a, Show a) => M.Map (Qualified a) (Qualified a) -> a -> Either String (M.Map (Qualified a) (Qualified a))+ updateImports m name = case M.lookup (Qualified impQual name) m of+ Nothing -> return $ M.insert (Qualified impQual name) (Qualified (Just importModule) name) m Just (Qualified Nothing _) -> error "Invalid state in updateImports" Just x@(Qualified (Just mn) _) -> throwError $ if mn == currentModule || importModule == currentModule
src/Language/PureScript/Sugar/Operators.hs view
@@ -17,7 +17,7 @@ -- ----------------------------------------------------------------------------- -{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE Rank2Types, FlexibleContexts #-} module Language.PureScript.Sugar.Operators ( rebracket@@ -28,13 +28,14 @@ import Language.PureScript.Values import Control.Applicative-import Control.Arrow (first) import Control.Monad.State+import Control.Monad.Error.Class import Data.Function (on)-import Data.List (groupBy, sortBy)+import Data.Functor.Identity+import Data.List (sort, groupBy, sortBy) -import qualified Data.Map as M+import qualified Data.Data as D import qualified Data.Generics as G import qualified Data.Generics.Extras as G @@ -46,28 +47,41 @@ -- Remove explicit parentheses and reorder binary operator applications -- rebracket :: [Module] -> Either String [Module]-rebracket = go M.empty []+rebracket ms = do+ let fixities = concatMap collectFixities ms+ ensureNoDuplicates $ map fst fixities+ let opTable = customOperatorTable fixities+ mapM (rebracketModule opTable) ms++rebracketModule :: [[(Qualified Ident, Value -> Value -> Value, Associativity)]] -> Module -> Either String Module+rebracketModule opTable (Module mn ds exts) = Module mn <$> (removeParens <$> G.everywhereM' (G.mkM (matchOperators opTable)) ds) <*> pure exts++removeParens :: (D.Data d) => d -> d+removeParens = G.everywhere (G.mkT go) where- go _ rb [] = return . reverse $ rb- go m rb (Module name ds exps : ms) = do- m' <- M.union m <$> collectFixities m name ds- let opTable = customOperatorTable m'- ds' <- G.everywhereM' (G.mkM (matchOperators name opTable)) ds- go m' (Module name (G.everywhere (G.mkT removeParens) ds') exps : rb) ms+ go (Parens val) = val+ go val = val -removeParens :: Value -> Value-removeParens (Parens val) = val-removeParens val = val+collectFixities :: Module -> [(Qualified Ident, Fixity)]+collectFixities (Module moduleName ds _) = concatMap collect ds+ where+ collect :: Declaration -> [(Qualified Ident, Fixity)]+ collect (FixityDeclaration fixity name) = [(Qualified (Just moduleName) (Op name), fixity)]+ collect _ = [] -customOperatorTable :: M.Map (Qualified Ident) Fixity -> [[(Qualified Ident, Value -> Value -> Value, Associativity)]]+ensureNoDuplicates :: [Qualified Ident] -> Either String ()+ensureNoDuplicates m = go $ sort m+ where+ go [] = return ()+ go [_] = return ()+ go (x : y : _) | x == y = throwError $ "Redefined fixity for " ++ show x+ go (_ : rest) = go rest++customOperatorTable :: [(Qualified Ident, Fixity)] -> [[(Qualified Ident, Value -> Value -> Value, Associativity)]] customOperatorTable fixities = let- -- We make the assumption here that infix operators are not qualified. The parser currently enforces this.- -- The fixity map can therefore map from module name/ident pairs to fixities, where the module name is the name- -- of the module imported into, not from. This is useful in matchOp, but here we have to discard the module name to- -- make sure that the generated code is correct.- applyUserOp (Qualified _ name) t1 = App (App (Var (Qualified Nothing name)) t1)- userOps = map (\(name, Fixity a p) -> (name, applyUserOp name, p, a)) . M.toList $ fixities+ applyUserOp ident t1 = App (App (Var ident) t1)+ userOps = map (\(name, Fixity a p) -> (name, applyUserOp name, p, a)) fixities sorted = sortBy (flip compare `on` (\(_, _, p, _) -> p)) userOps groups = groupBy ((==) `on` (\(_, _, p, _) -> p)) sorted in@@ -75,8 +89,8 @@ type Chain = [Either Value (Qualified Ident)] -matchOperators :: ModuleName -> [[(Qualified Ident, Value -> Value -> Value, Associativity)]] -> Value -> Either String Value-matchOperators moduleName ops = G.everywhereM' (G.mkM parseChains)+matchOperators :: [[(Qualified Ident, Value -> Value -> Value, Associativity)]] -> Value -> Either String Value+matchOperators ops = parseChains where parseChains :: Value -> Either String Value parseChains b@BinaryNoParens{} = bracketChain (extendChain b)@@ -86,33 +100,34 @@ extendChain other = [Left other] bracketChain :: Chain -> Either String Value bracketChain = either (Left . show) Right . P.parse (P.buildExpressionParser opTable parseValue <* P.eof) "operator expression"- opTable = map (map (\(name, f, a) -> P.Infix (P.try (matchOp moduleName name) >> return f) (toAssoc a))) ops- ++ [[P.Infix (P.try (parseOp >>= \ident -> return (\t1 t2 -> App (App (Var ident) t1) t2))) P.AssocLeft]]+ opTable = [P.Infix (P.try (parseTicks >>= \ident -> return (\t1 t2 -> App (App (Var ident) t1) t2))) P.AssocLeft]+ : map (map (\(name, f, a) -> P.Infix (P.try (matchOp name) >> return f) (toAssoc a))) ops+ ++ [[ P.Infix (P.try (parseOp >>= \ident -> return (\t1 t2 -> App (App (Var ident) t1) t2))) P.AssocLeft ]] toAssoc :: Associativity -> P.Assoc toAssoc Infixl = P.AssocLeft toAssoc Infixr = P.AssocRight +token :: (P.Stream s Identity t, Show t) => (t -> Maybe a) -> P.Parsec s u a+token = P.token show (const (P.initialPos ""))+ parseValue :: P.Parsec Chain () Value-parseValue = P.token show (const (P.initialPos "")) (either Just (const Nothing)) P.<?> "expression"+parseValue = token (either Just (const Nothing)) P.<?> "expression" parseOp :: P.Parsec Chain () (Qualified Ident)-parseOp = P.token show (const (P.initialPos "")) (either (const Nothing) Just) P.<?> "operator"+parseOp = token (either (const Nothing) fromOp) P.<?> "operator"+ where+ fromOp q@(Qualified _ (Op _)) = Just q+ fromOp _ = Nothing -matchOp :: ModuleName -> Qualified Ident -> P.Parsec Chain () ()-matchOp moduleName op = do- ident <- parseOp- guard (qualify moduleName ident == qualify moduleName op)+parseTicks :: P.Parsec Chain () (Qualified Ident)+parseTicks = token (either (const Nothing) fromOp) P.<?> "infix function"+ where+ fromOp q@(Qualified _ (Ident _)) = Just q+ fromOp _ = Nothing -collectFixities :: M.Map (Qualified Ident) Fixity -> ModuleName -> [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)-collectFixities m _ [] = return m-collectFixities m moduleName (FixityDeclaration fixity name : rest) = do- let qual = Qualified (Just moduleName) (Op name)- when (qual `M.member` m) (Left $ "redefined fixity for " ++ show name)- collectFixities (M.insert qual fixity m) moduleName rest-collectFixities m moduleName (ImportDeclaration importedModule _ : rest) = do- let fs = [ (i, fixity) | (Qualified mn i, fixity) <- M.toList m, mn == Just importedModule ]- let m' = M.fromList (map (first (Qualified (Just moduleName))) fs)- collectFixities (m' `M.union` m) moduleName rest-collectFixities m moduleName (_:ds) = collectFixities m moduleName ds+matchOp :: Qualified Ident -> P.Parsec Chain () ()+matchOp op = do+ ident <- parseOp+ guard $ ident == op
src/Language/PureScript/TypeChecker.hs view
@@ -36,6 +36,7 @@ import Language.PureScript.Kinds import Language.PureScript.Declarations import Language.PureScript.Environment+import Language.PureScript.Pretty.Types addDataType :: ModuleName -> ProperName -> [String] -> [(ProperName, [Type])] -> Kind -> Check () addDataType moduleName name args dctors ctorKind = do@@ -86,8 +87,8 @@ env <- getEnv when (ctor `M.member` typeSynonyms env) $ throwError "Type synonym instances are disallowed" return ()-checkTypeClassInstance m (TypeApp ty (TypeVar _)) = checkTypeClassInstance m ty-checkTypeClassInstance _ _ = throwError "Type class instance must be of the form T a1 ... an"+checkTypeClassInstance m (TypeApp t1 t2) = checkTypeClassInstance m t1 >> checkTypeClassInstance m t2+checkTypeClassInstance _ ty = throwError $ "Type class instance head is invalid: " ++ prettyPrintType ty -- | -- Type check all declarations in a module@@ -173,15 +174,12 @@ env <- getEnv guardWith ("Fixity declaration with no binding: " ++ name) $ M.member (moduleName, Op name) $ names env return $ d : ds-typeCheckAll mainModuleName currentModule (d@(ImportDeclaration moduleName _) : rest) = do+typeCheckAll mainModuleName currentModule (d@(ImportDeclaration moduleName _ _) : rest) = do env <- getEnv- let instances = filter (\tcd ->- let Qualified (Just mn) _ = tcdName tcd in- moduleName == mn && tcdType tcd == TCDRegular- ) (typeClassDictionaries env)+ let instances = filter (\tcd -> let Qualified (Just mn) _ = tcdName tcd in moduleName == mn) (typeClassDictionaries env) forM_ instances $ \tcd -> do let (Qualified _ ident) = tcdName tcd- addTypeClassDictionaries [tcd { tcdName = Qualified (Just currentModule) ident, tcdType = TCDAlias (tcdName tcd) }]+ addTypeClassDictionaries [tcd { tcdName = Qualified (Just currentModule) ident, tcdType = TCDAlias (canonicalizeDictionary tcd) }] ds <- typeCheckAll mainModuleName currentModule rest return $ d : ds typeCheckAll mainModuleName moduleName (d@(TypeClassDeclaration pn args tys) : rest) = do
src/Language/PureScript/TypeChecker/Kinds.hs view
@@ -134,7 +134,7 @@ infer (TypeConstructor v) = do env <- liftCheck getEnv case M.lookup v (types env) of- Nothing -> UnifyT . lift . throwError $ "Unknown type constructor '" ++ show v ++ "'" ++ show (M.keys (types env))+ Nothing -> UnifyT . lift . throwError $ "Unknown type constructor '" ++ show v ++ "'" Just (kind, _) -> return kind infer (TypeApp t1 t2) = do k0 <- fresh
src/Language/PureScript/TypeChecker/Monad.hs view
@@ -153,11 +153,17 @@ modifyEnv f = modify (\s -> s { checkEnv = f (checkEnv s) }) -- |--- Run a computation in the Check monad, failing with an error, or succeeding with a return value and the final @Environment@.+-- Run a computation in the Check monad, starting with an empty @Environment@ -- runCheck :: Check a -> Either String (a, Environment)-runCheck c = do- (a, s) <- flip runStateT (CheckState initEnvironment 0 0 Nothing) $ unCheck c+runCheck = runCheck' initEnvironment++-- |+-- Run a computation in the Check monad, failing with an error, or succeeding with a return value and the final @Environment@.+--+runCheck' :: Environment -> Check a -> Either String (a, Environment)+runCheck' env c = do+ (a, s) <- flip runStateT (CheckState env 0 0 Nothing) $ unCheck c return (a, checkEnv s) -- |
src/Language/PureScript/TypeChecker/Types.hs view
@@ -118,6 +118,9 @@ unifyTypes' r1 r2@RCons{} = unifyRows r1 r2 unifyTypes' r1@REmpty r2 = unifyRows r1 r2 unifyTypes' r1 r2@REmpty = unifyRows r1 r2+ unifyTypes' t@(ConstrainedType _ _) _ = throwError $ "Attempted to unify a constrained type " ++ prettyPrintType t +++ " with another type."+ unifyTypes' t3 t4@(ConstrainedType _ _) = unifyTypes' t4 t3 unifyTypes' t3 t4 = throwError $ "Cannot unify " ++ prettyPrintType t3 ++ " with " ++ prettyPrintType t4 ++ "." -- |@@ -179,7 +182,7 @@ -- If the declaration is a function, it has access to other values in the binding group. -- If not, the generated code might fail at runtime since those values might be undefined. let dict' = if isFunction val then dict else M.empty- triple@(_, (val', ty)) <- case e of+ triple@(_, (_, ty)) <- case e of -- Typed declarations (ident, (val', Just (ty, checkType))) -> do -- Kind check@@ -201,8 +204,6 @@ when (Just moduleName == mainModuleName && fst e == Ident C.main) $ do [eff, a] <- replicateM 2 fresh ty =?= TypeApp (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Control", ProperName "Monad", ProperName "Eff"])) (ProperName "Eff"))) eff) a- -- Make sure unification variables do not escape- escapeCheck val' ty return triple forM tys $ \(ident, (val, ty)) -> do -- Replace type class dictionary placeholders with actual dictionaries@@ -245,21 +246,22 @@ replaceTypeClassDictionaries :: ModuleName -> Value -> Check Value replaceTypeClassDictionaries mn = everywhereM' (mkM go) where- go (TypeClassDictionary constraint dicts) = entails mn dicts constraint+ go (TypeClassDictionary constraint dicts) = do+ env <- getEnv+ entails env mn dicts constraint go other = return other -- | -- 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 :: ModuleName -> [TypeClassDictionaryInScope] -> (Qualified ProperName, [Type]) -> Check Value-entails moduleName context goal@(className, tys) = do- env <- getEnv- case go env goal of- [] -> throwError $ "No " ++ show className ++ " instance found for " ++ intercalate ", " (map prettyPrintType tys) ++ show (typeClassDictionaries env)+entails :: Environment -> ModuleName -> [TypeClassDictionaryInScope] -> (Qualified ProperName, [Type]) -> Check Value+entails env moduleName context goal@(className, tys) = do+ case go goal of+ [] -> throwError $ "No " ++ show className ++ " instance found for " ++ intercalate ", " (map prettyPrintType tys) (dict : _) -> return dict where- go env (className', tys') =+ go (className', tys') = [ mkDictionary (canonicalizeDictionary tcd) args | tcd <- context -- Choose type class dictionaries in scope in the current module@@ -269,14 +271,14 @@ -- Make sure the type unifies with the type in the type instance definition , subst <- maybeToList . (>>= verifySubstitution) . fmap concat $ zipWithM (typeHeadsAreEqual moduleName env) tys' (tcdInstanceTypes tcd) -- Solve any necessary subgoals- , args <- solveSubgoals env subst (tcdDependencies tcd) ]+ , args <- solveSubgoals subst (tcdDependencies tcd) ] -- 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 :: Environment -> [(String, Type)] -> Maybe [(Qualified ProperName, [Type])] -> [Maybe [Value]]- solveSubgoals _ _ Nothing = return Nothing- solveSubgoals env subst (Just subgoals) = do- dict <- mapM (go env . second (map (replaceAllTypeVars subst))) subgoals+ solveSubgoals :: [(String, Type)] -> Maybe [(Qualified ProperName, [Type])] -> [Maybe [Value]]+ solveSubgoals _ Nothing = return Nothing+ solveSubgoals subst (Just subgoals) = do+ dict <- mapM (go . second (map (replaceAllTypeVars subst))) subgoals return $ Just dict -- Make a dictionary from subgoal dictionaries by applying the correct function mkDictionary :: Qualified Ident -> Maybe [Value] -> Value@@ -288,54 +290,53 @@ filterModule (TypeClassDictionaryInScope { tcdName = Qualified (Just mn) _ }) | mn == moduleName = True filterModule (TypeClassDictionaryInScope { tcdName = Qualified Nothing _ }) = True filterModule _ = False- canonicalizeDictionary :: TypeClassDictionaryInScope -> Qualified Ident- canonicalizeDictionary (TypeClassDictionaryInScope { tcdType = TCDRegular, tcdName = nm }) = nm- canonicalizeDictionary (TypeClassDictionaryInScope { tcdType = TCDAlias nm }) = nm -- Ensure that a substitution is valid verifySubstitution :: [(String, Type)] -> Maybe [(String, Type)] verifySubstitution subst = do let grps = groupBy ((==) `on` fst) subst- guard (all ((==) 1 . length . nubBy ((==) `on` snd)) grps)+ guard (all (pairwise (unifiesWith env) . map snd) grps) return $ map head grps -- |+-- Check all values in a list pairwise match a predicate+--+pairwise :: (a -> a -> Bool) -> [a] -> Bool+pairwise _ [] = True+pairwise _ [_] = True+pairwise p (x : xs) = all (p x) xs && pairwise p xs++-- |+-- Check that two types unify+--+unifiesWith :: Environment -> Type -> Type -> Bool+unifiesWith _ (TUnknown _) _ = True+unifiesWith _ _ (TUnknown _) = True+unifiesWith _ (Skolem s1 _) (Skolem s2 _) | s1 == s2 = True+unifiesWith _ (TypeVar v1) (TypeVar v2) | v1 == v2 = True+unifiesWith _ (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = True+unifiesWith e (TypeApp h1 t1) (TypeApp h2 t2) = unifiesWith e h1 h2 && unifiesWith e t1 t2+unifiesWith e (SaturatedTypeSynonym name args) t2 =+ case expandTypeSynonym' e name args of+ Left _ -> False+ Right t1 -> unifiesWith e t1 t2+unifiesWith e t1 t2@(SaturatedTypeSynonym _ _) = unifiesWith e t2 t1+unifiesWith _ _ _ = False++-- | -- 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 :: ModuleName -> Environment -> Type -> Type -> Maybe [(String, Type)] typeHeadsAreEqual _ _ (Skolem s1 _) (Skolem s2 _) | s1 == s2 = Just []-typeHeadsAreEqual _ _ (TypeVar v) t = Just [(v, t)] typeHeadsAreEqual _ _ t (TypeVar v) = Just [(v, t)] typeHeadsAreEqual _ _ (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = Just []-typeHeadsAreEqual m e (TypeApp h1 (TypeVar v)) (TypeApp h2 arg) = (:) (v, arg) <$> typeHeadsAreEqual m e h1 h2-typeHeadsAreEqual m e t1@(TypeApp _ _) t2@(TypeApp _ (TypeVar _)) = typeHeadsAreEqual m e t2 t1+typeHeadsAreEqual m e (TypeApp h1 t1) (TypeApp h2 t2) = (++) <$> typeHeadsAreEqual m e h1 h2 <*> typeHeadsAreEqual m e t1 t2 typeHeadsAreEqual m e (SaturatedTypeSynonym name args) t2 = case expandTypeSynonym' e name args of Left _ -> Nothing Right t1 -> typeHeadsAreEqual m e t1 t2 typeHeadsAreEqual _ _ _ _ = Nothing -- |--- Ensure unsolved unification variables do not escape----escapeCheck :: Value -> Type -> UnifyT Type Check ()-escapeCheck value ty = do- subst <- unifyCurrentSubstitution <$> UnifyT get- let visibleUnknowns = nub $ unknowns $ subst $? ty- let allUnknowns = findAllTypes value- forM_ allUnknowns $ \t -> do- let unsolvedUnknowns = nub . unknowns $ subst $? t- guardWith "Escape check fails" $ null $ unsolvedUnknowns \\ visibleUnknowns---- |--- Find all type annotations occuring inside a value----findAllTypes :: Value -> [Type]-findAllTypes = everything (++) (mkQ [] go)- where- go (TypedValue _ _ ty) = [ty]- go _ = []---- | -- Ensure skolem variables do not escape their scope -- skolemEscapeCheck :: Value -> Check ()@@ -519,7 +520,7 @@ infer' (Abs (Right _) _) = error "Binder was not desugared" infer' (App f arg) = do f'@(TypedValue _ _ ft) <- infer f- (ret, app) <- checkFunctionApplication f' ft arg+ (ret, app) <- checkFunctionApplication f' ft arg Nothing return $ TypedValue True app ret infer' (Var var) = do Just moduleName <- checkCurrentModule <$> get@@ -716,7 +717,8 @@ return $ TypedValue True v t check' v@(BooleanLiteral _) t | t == tyBoolean = return $ TypedValue True v t-check' (ArrayLiteral vals) t@(TypeApp a ty) | a == tyArray = do+check' (ArrayLiteral vals) t@(TypeApp a ty) = do+ a =?= tyArray array <- ArrayLiteral <$> forM vals (`check` ty) return $ TypedValue True array t check' (Abs (Left arg) ret) ty@(TypeApp (TypeApp t argTy) retTy) | t == tyFunction = do@@ -726,8 +728,7 @@ check' (Abs (Right _) _) _ = error "Binder was not desugared" check' (App f arg) ret = do f'@(TypedValue _ _ ft) <- infer f- (ret', app) <- checkFunctionApplication f' ft arg- _ <- subsumes Nothing ret' ret+ (_, app) <- checkFunctionApplication f' ft arg (Just ret) return $ TypedValue True app ret check' v@(Var var) ty = do Just moduleName <- checkCurrentModule <$> get@@ -746,7 +747,7 @@ case val' of Nothing -> throwError "Unable to check type subsumption" Just val'' -> do- val''' <- if checkType then check val'' ty1 else return val''+ val''' <- if checkType then check val'' ty1' else return val'' return $ TypedValue checkType (TypedValue True val''' ty1) ty2 check' (Case vals binders) ret = do vals' <- mapM infer vals@@ -822,8 +823,8 @@ -- | -- Check the type of a function application, rethrowing errors to provide a better error message ---checkFunctionApplication :: Value -> Type -> Value -> UnifyT Type Check (Type, Value)-checkFunctionApplication fn fnTy arg = rethrow errorMessage $ checkFunctionApplication' fn fnTy arg+checkFunctionApplication :: Value -> Type -> Value -> Maybe Type -> UnifyT Type Check (Type, Value)+checkFunctionApplication fn fnTy arg ret = rethrow errorMessage $ checkFunctionApplication' fn fnTy arg ret where errorMessage msg = "Error applying function of type " ++ prettyPrintType fnTy@@ -833,30 +834,32 @@ -- | -- Check the type of a function application ---checkFunctionApplication' :: Value -> Type -> Value -> UnifyT Type Check (Type, Value)-checkFunctionApplication' fn (TypeApp (TypeApp tyFunction' argTy) retTy) arg = do+checkFunctionApplication' :: Value -> Type -> Value -> Maybe Type -> UnifyT Type Check (Type, Value)+checkFunctionApplication' fn (TypeApp (TypeApp tyFunction' argTy) retTy) arg ret = do tyFunction' =?= tyFunction- arg' <- check arg argTy+ _ <- maybe (return Nothing) (subsumes Nothing retTy) ret+ subst <- unifyCurrentSubstitution <$> UnifyT get+ arg' <- check arg (subst $? argTy) return (retTy, App fn arg')-checkFunctionApplication' fn (ForAll ident ty _) arg = do+checkFunctionApplication' fn (ForAll ident ty _) arg ret = do replaced <- replaceVarWithUnknown ident ty- checkFunctionApplication fn replaced arg-checkFunctionApplication' fn u@(TUnknown _) arg = do+ checkFunctionApplication fn replaced arg ret+checkFunctionApplication' fn u@(TUnknown _) arg ret = do arg' <- do TypedValue _ arg' t <- infer arg (arg'', t') <- instantiatePolyTypeWithUnknowns arg' t return $ TypedValue True arg'' t' let ty = (\(TypedValue _ _ t) -> t) arg'- ret <- fresh- u =?= function ty ret- return (ret, App fn arg')-checkFunctionApplication' fn (SaturatedTypeSynonym name tyArgs) arg = do+ ret' <- maybe fresh return ret+ u =?= function ty ret'+ return (ret', App fn arg')+checkFunctionApplication' fn (SaturatedTypeSynonym name tyArgs) arg ret = do ty <- introduceSkolemScope <=< expandTypeSynonym name $ tyArgs- checkFunctionApplication fn ty arg-checkFunctionApplication' fn (ConstrainedType constraints fnTy) arg = do+ checkFunctionApplication fn ty arg ret+checkFunctionApplication' fn (ConstrainedType constraints fnTy) arg ret = do dicts <- getTypeClassDictionaries- checkFunctionApplication' (foldl App fn (map (flip TypeClassDictionary dicts) constraints)) fnTy arg-checkFunctionApplication' _ fnTy arg = throwError $ "Cannot apply a function of type "+ checkFunctionApplication' (foldl App fn (map (flip TypeClassDictionary dicts) constraints)) fnTy arg ret+checkFunctionApplication' _ fnTy arg _ = throwError $ "Cannot apply a function of type " ++ prettyPrintType fnTy ++ " to argument " ++ prettyPrintValue arg
src/Language/PureScript/Values.hs view
@@ -174,6 +174,13 @@ | TCDAlias (Qualified Ident) deriving (Show, Eq, Data, Typeable) -- |+-- Find the original dictionary which a type class dictionary in scope refers to+--+canonicalizeDictionary :: TypeClassDictionaryInScope -> Qualified Ident+canonicalizeDictionary (TypeClassDictionaryInScope { tcdType = TCDRegular, tcdName = nm }) = nm+canonicalizeDictionary (TypeClassDictionaryInScope { tcdType = TCDAlias nm }) = nm++-- | -- A statement in a do-notation block -- data DoNotationElement
tests/Main.hs view
@@ -25,7 +25,7 @@ import System.Exit import System.Process import System.FilePath (pathSeparator)-import System.Directory (getCurrentDirectory, getDirectoryContents, findExecutable)+import System.Directory (getCurrentDirectory, getTemporaryDirectory, getDirectoryContents, findExecutable) import Text.Parsec (ParseError) import qualified Paths_purescript as Paths import qualified System.IO.UTF8 as U@@ -38,62 +38,66 @@ text <- U.readFile inputFile return $ P.runIndentParser inputFile P.parseModules text -compile :: P.Options -> [FilePath] -> IO (Either String String)+compile :: P.Options -> [FilePath] -> IO (Either String (String, String, P.Environment)) compile opts inputFiles = do modules <- readInput inputFiles case modules of Left parseError -> return (Left $ show parseError)- Right ms ->- case P.compile opts ms of- Left typeError ->- return (Left typeError)- Right (js, _, _) ->- return (Right js)+ Right ms -> return $ P.compile opts ms -assert :: P.Options -> [FilePath] -> (Either String String -> IO (Maybe String)) -> IO ()-assert opts inputFiles f = do- e <- compile opts inputFiles+assert :: FilePath -> P.Options -> FilePath -> (Either String (String, String, P.Environment) -> IO (Maybe String)) -> IO ()+assert preludeExterns opts inputFile f = do+ e <- compile opts [preludeExterns, inputFile] maybeErr <- f e case maybeErr of Just err -> putStrLn err >> exitFailure Nothing -> return () -assertCompiles :: FilePath -> IO ()-assertCompiles inputFile = do- putStrLn $ "assert " ++ inputFile ++ " compiles successfully"- prelude <- preludeFilename- assert (P.defaultOptions { P.optionsMain = Just "Main", P.optionsNoOptimizations = True, P.optionsModules = ["Main"] }) [prelude, inputFile] $ either (return . Just) $ \js -> do+assertCompiles :: FilePath -> FilePath -> FilePath -> IO ()+assertCompiles preludeJs preludeExterns inputFile = do+ putStrLn $ "Assert " ++ inputFile ++ " compiles successfully"+ let options = P.defaultOptions { P.optionsMain = Just "Main", P.optionsModules = ["Main"], P.optionsCodeGenModules = ["Main"] }+ assert preludeExterns options inputFile $ either (return . Just) $ \(js, _, _) -> do process <- findNodeProcess- result <- traverse (\node -> readProcessWithExitCode node [] js) process+ result <- traverse (\node -> readProcessWithExitCode node [] (preludeJs ++ js)) process case result of Just (ExitSuccess, out, _) -> putStrLn out >> return Nothing Just (ExitFailure _, _, err) -> return $ Just err Nothing -> return $ Just "Couldn't find node.js executable" -findNodeProcess :: IO (Maybe String)-findNodeProcess = runMaybeT . msum $ map (MaybeT . findExecutable) names- where names = ["nodejs", "node"]--assertDoesNotCompile :: FilePath -> IO ()-assertDoesNotCompile inputFile = do- putStrLn $ "assert " ++ inputFile ++ " does not compile"- assert P.defaultOptions [inputFile] $ \e ->+assertDoesNotCompile :: FilePath -> FilePath -> IO ()+assertDoesNotCompile preludeExterns inputFile = do+ putStrLn $ "Assert " ++ inputFile ++ " does not compile"+ assert preludeExterns P.defaultOptions inputFile $ \e -> case e of Left _ -> return Nothing Right _ -> return $ Just "Should not have compiled" +findNodeProcess :: IO (Maybe String)+findNodeProcess = runMaybeT . msum $ map (MaybeT . findExecutable) names+ where names = ["nodejs", "node"]+ main :: IO () main = do- cd <- getCurrentDirectory- putStrLn cd- let examples = cd ++ pathSeparator : "examples"- let passing = examples ++ pathSeparator : "passing"- passingTestCases <- getDirectoryContents passing- forM_ passingTestCases $ \inputFile -> when (".purs" `isSuffixOf` inputFile) $- assertCompiles (passing ++ pathSeparator : inputFile)- let failing = examples ++ pathSeparator : "failing"- failingTestCases <- getDirectoryContents failing- forM_ failingTestCases $ \inputFile -> when (".purs" `isSuffixOf` inputFile) $- assertDoesNotCompile (failing ++ pathSeparator : inputFile)- exitSuccess+ prelude <- preludeFilename+ putStrLn "Compiling Prelude"+ preludeResult <- compile P.defaultOptions [prelude]+ case preludeResult of+ Left err -> putStrLn err >> exitFailure+ Right (preludeJs, exts, _) -> do+ tmp <- getTemporaryDirectory+ let preludeExterns = tmp ++ pathSeparator : "prelude.externs"+ writeFile preludeExterns exts+ putStrLn $ "Wrote " ++ preludeExterns+ cd <- getCurrentDirectory+ let examples = cd ++ pathSeparator : "examples"+ let passing = examples ++ pathSeparator : "passing"+ passingTestCases <- getDirectoryContents passing+ forM_ passingTestCases $ \inputFile -> when (".purs" `isSuffixOf` inputFile) $+ assertCompiles preludeJs preludeExterns (passing ++ pathSeparator : inputFile)+ let failing = examples ++ pathSeparator : "failing"+ failingTestCases <- getDirectoryContents failing+ forM_ failingTestCases $ \inputFile -> when (".purs" `isSuffixOf` inputFile) $+ assertDoesNotCompile preludeExterns (failing ++ pathSeparator : inputFile)+ exitSuccess