on master help sign in
Merge remote-tracking branch 'evan/dev' into webgl

Conflicts: (johnpmayer) compiler/Generate/JavaScript.hs compiler/SourceSyntax/Expression.hs compiler/Transform/Expression.hs compiler/Transform/SafeNames.hs compiler/Transform/Substitute.hs

commitbe9baab8be88452eb95e8878dfc553b0b2a03eb0 authorJohn P Mayer Jr <john.p.mayer.jr@gmail.com> dateWed 15 Jan 2014, 21:11 (13 years ago) parentf83b406b5e7311
35 files changed+749−517
Elm.cabal+4 compiler/Build/Dependencies.hs+46−42 compiler/Build/File.hs+129−78 compiler/Build/Print.hs+7−1 compiler/Compiler.hs+1−2 compiler/Docs.hs+3−4 compiler/Generate/JavaScript.hs+18−36 compiler/Generate/JavaScript/Helpers.hs+37 compiler/Generate/JavaScript/Ports.hs+130 compiler/Parse/Declaration.hs+2−3 compiler/Parse/Helpers.hs−1 compiler/Parse/Type.hs+7−7 compiler/SourceSyntax/Declaration.hs+10−14 compiler/SourceSyntax/Expression.hs+46−29 compiler/SourceSyntax/Pattern.hs+3−3 compiler/SourceSyntax/Type.hs+18−27 compiler/Transform/Canonicalize.hs+4−9 compiler/Transform/Check.hs+8−7 compiler/Transform/Declaration.hs+5−9 compiler/Transform/Expression.hs+6−7 compiler/Transform/SafeNames.hs+1−1 compiler/Transform/SortDefinitions.hs+1−1 compiler/Transform/Substitute.hs+1−1 compiler/Type/Alias.hs+14−14 compiler/Type/Constrain/Declaration.hs+6−12 compiler/Type/Constrain/Expression.hs+1−2 compiler/Type/Environment.hs+6−4 compiler/Type/ExtraChecks.hs+59−60 compiler/Type/Type.hs+9−5 libraries/Dict.elm+82−88 libraries/Native/JavaScript.js+1−1 libraries/Native/Ports.js+46−42 libraries/elm_dependencies.json+2 runtime/Init.js+34−7 tests/test-files/good/Box.elm+2
Elm.cabal+4
@@ -47,6 +47,8 @@ Library
4747 SourceSyntax.PrettyPrint,
4848 SourceSyntax.Type,
4949 Generate.JavaScript,
50+ Generate.JavaScript.Helpers,
51+ Generate.JavaScript.Ports,
5052 Generate.Noscript,
5153 Generate.Markdown,
5254 Generate.Html,
@@ -128,6 +130,8 @@ Executable elm
128130 SourceSyntax.PrettyPrint,
129131 SourceSyntax.Type,
130132 Generate.JavaScript,
133+ Generate.JavaScript.Helpers,
134+ Generate.JavaScript.Ports,
131135 Generate.Noscript,
132136 Generate.Markdown,
133137 Generate.Html,
compiler/Build/Dependencies.hs+46−42
@@ -1,5 +1,5 @@
11{-# OPTIONS_GHC -W #-}
2−module Build.Dependencies (getSortedDependencies) where
2+module Build.Dependencies (getSortedDependencies, readDeps) where
33
44import Control.Applicative
55import Control.Monad.Error
@@ -12,9 +12,9 @@ import qualified Data.Map as Map
1212import qualified Data.Maybe as Maybe
1313import qualified Data.Set as Set
1414import System.Directory
15−import System.Exit
1615import System.FilePath as FP
17−import System.IO
16+
17+import Build.Print (failure)
1818
1919import qualified SourceSyntax.Module as Module
2020import qualified Parse.Parse as Parse
@@ -26,7 +26,7 @@ getSortedDependencies :: [FilePath] -> Module.Interfaces -> FilePath -> IO [Stri
2626getSortedDependencies srcDirs builtIns root =
2727 do extras <- extraDependencies
2828 let allSrcDirs = srcDirs ++ Maybe.fromMaybe [] extras
29− result <- runErrorT $ readDeps allSrcDirs builtIns root
29+ result <- runErrorT $ readAllDeps allSrcDirs builtIns root
3030 case result of
3131 Right deps -> sortDeps deps
3232 Left err -> failure $ err ++ if Maybe.isJust extras then "" else msg
@@ -34,8 +34,6 @@ getSortedDependencies srcDirs builtIns root =
3434 Path.dependencyFile ++
3535 " file if you\nare trying to use a 3rd party library."
3636
37−failure msg = hPutStrLn stderr msg >> exitFailure
38−
3937extraDependencies :: IO (Maybe [FilePath])
4038extraDependencies =
4139 do exists <- doesFileExist Path.dependencyFile
@@ -76,46 +74,52 @@ sortDeps depends =
7674 mistakes = filter (\scc -> length scc > 1) sccs
7775 msg = "A cyclical module dependency or was detected in:\n"
7876
79−readDeps :: [FilePath] -> Module.Interfaces -> FilePath -> ErrorT String IO [Deps]
80−readDeps srcDirs builtIns root = do
81− let ifaces = (Set.fromList . Map.keys) builtIns
82− State.evalStateT (go ifaces root) Set.empty
77+readAllDeps :: [FilePath] -> Module.Interfaces -> FilePath -> ErrorT String IO [Deps]
78+readAllDeps srcDirs builtIns root =
79+ do let ifaces = (Set.fromList . Map.keys) builtIns
80+ State.evalStateT (go ifaces root) Set.empty
8381 where
8482 go :: Set.Set String -> FilePath -> State.StateT (Set.Set String) (ErrorT String IO) [Deps]
8583 go builtIns root = do
86− (root', txt) <- lift $ getFile srcDirs root
87− case Parse.dependencies txt of
88− Left err -> throwError $ msg ++ show err
89− where msg = "Error resolving dependencies in " ++ root' ++ ":\n"
90−
91− Right (name,deps) ->
92− do seen <- State.get
93− let realDeps = Set.difference (Set.fromList deps) builtIns
94− newDeps = Set.difference (Set.filter (not . isNative) realDeps) seen
95− State.put (Set.insert name (Set.union newDeps seen))
96− rest <- mapM (go builtIns . toFilePath) (Set.toList newDeps)
97− return ((makeRelative "." root', name, Set.toList realDeps) : concat rest)
98−
99−getFile :: [FilePath] -> FilePath -> ErrorT String IO (FilePath,String)
100−getFile [] path =
101− throwError $ unlines
102− [ "Could not find file: " ++ path
103− , " If it is not in the root directory of your project, use"
104− , " --src-dir to declare additional locations for source files."
105− , " If it is part of a 3rd party library, it needs to be declared"
106− , " as a dependency in the " ++ Path.dependencyFile ++ " file." ]
107−
108−getFile (dir:dirs) path = do
109− let path' = dir </> path
110− exists <- liftIO $ doesFileExist path'
111− case exists of
112− True -> (,) path' `fmap` liftIO (readFile path')
113− False -> getFile dirs path
114−
84+ root' <- lift $ findSrcFile srcDirs root
85+ (name, deps) <- lift $ readDeps root'
86+ seen <- State.get
87+ let realDeps = Set.difference (Set.fromList deps) builtIns
88+ newDeps = Set.difference (Set.filter (not . isNative) realDeps) seen
89+ State.put (Set.insert name (Set.union newDeps seen))
90+ rest <- mapM (go builtIns . toFilePath) (Set.toList newDeps)
91+ return ((makeRelative "." root', name, Set.toList realDeps) : concat rest)
92+
93+readDeps :: FilePath -> ErrorT String IO (String, [String])
94+readDeps path = do
95+ txt <- lift $ readFile path
96+ case Parse.dependencies txt of
97+ Left err -> throwError $ msg ++ show err
98+ where msg = "Error resolving dependencies in " ++ path ++ ":\n"
99+ Right o -> return o
100+
101+findSrcFile :: [FilePath] -> FilePath -> ErrorT String IO FilePath
102+findSrcFile dirs path = foldr tryDir notFound dirs
103+ where
104+ notFound = throwError $ unlines
105+ [ "Could not find file: " ++ path
106+ , " If it is not in the root directory of your project, use"
107+ , " --src-dir to declare additional locations for source files."
108+ , " If it is part of a 3rd party library, it needs to be declared"
109+ , " as a dependency in the " ++ Path.dependencyFile ++ " file." ]
110+ tryDir dir next = do
111+ let path' = dir </> path
112+ exists <- liftIO $ doesFileExist path'
113+ if exists
114+ then return path'
115+ else next
116+
117+isNative :: String -> Bool
115118isNative name = List.isPrefixOf "Native." name
116119
117120toFilePath :: String -> FilePath
118121toFilePath name = map swapDots name ++ ext
119− where swapDots '.' = '/'
120− swapDots c = c
121− ext = if isNative name then ".js" else ".elm"
122+ where
123+ swapDots '.' = '/'
124+ swapDots c = c
125+ ext = if isNative name then ".js" else ".elm"
compiler/Build/File.hs+129−78
@@ -1,92 +1,137 @@
11{-# OPTIONS_GHC -W #-}
22module Build.File (build) where
33
4−import Control.Monad (when)
5−import qualified Data.Binary as Binary
6−import qualified Data.Map as Map
4+import Control.Applicative ((<$>))
5+import Control.Monad.Error (runErrorT)
6+import Control.Monad.RWS.Strict
77import System.Directory
88import System.Exit
99import System.FilePath
1010import System.IO
1111
12+import qualified Data.Binary as Binary
13+import qualified Data.Maybe as Maybe
14+import qualified Data.Map as Map
15+import qualified Data.ByteString.Lazy as L
16+
17+import qualified Build.Dependencies as Deps
18+import qualified Build.Flags as Flag
19+import qualified Build.Interface as Interface
20+import qualified Build.Print as Print
21+import qualified Build.Source as Source
22+import qualified Build.Utils as Utils
23+import qualified Generate.JavaScript as JS
24+import qualified Parse.Module as Parser
25+import qualified SourceSyntax.Module as M
1226import qualified Transform.Canonicalize as Canonical
1327
14−import qualified Data.ByteString.Lazy as L
15−
16−import qualified Build.Utils as Utils
17−import qualified Build.Flags as Flag
18−import qualified Build.Interface as Interface
19−import qualified Build.Print as Print
20−import qualified Build.Source as Source
21−import qualified Generate.JavaScript as JS
22−import qualified Parse.Module as Parser
23−import qualified SourceSyntax.Module as M
24−
25−build :: Flag.Flags -> Int -> M.Interfaces -> String -> [FilePath]
26− -> IO (String, M.Interfaces)
27−build _ _ interfaces moduleName [] =
28− return (moduleName, interfaces)
29−build flags numModules interfaces _ (filePath:rest) =
30− do (name,interface) <-
31− build1 flags (numModules - length rest) numModules interfaces filePath
32− let interfaces' = Map.insert name interface interfaces
33− build flags numModules interfaces' name rest
34−
35−
36−build1 :: Flag.Flags -> Int -> Int -> M.Interfaces -> FilePath
37− -> IO (String, M.ModuleInterface)
38−build1 flags moduleNum numModules interfaces filePath =
39− do compiled <- alreadyCompiled flags filePath
40− case compiled of
41− False -> compile flags number interfaces filePath
42− True -> retrieve flags interfaces filePath
43− where
44− number = "[" ++ show moduleNum ++ " of " ++ show numModules ++ "]"
45−
46−
47−alreadyCompiled :: Flag.Flags -> FilePath -> IO Bool
48−alreadyCompiled flags filePath = do
49− existsi <- doesFileExist (Utils.elmi flags filePath)
50− existso <- doesFileExist (Utils.elmo flags filePath)
51− if not existsi || not existso
52− then return False
53− else do tsrc <- getModificationTime filePath
54− tint <- getModificationTime (Utils.elmo flags filePath)
55− return (tsrc <= tint)
56−
57−retrieve :: Flag.Flags -> Map.Map String M.ModuleInterface -> FilePath
58− -> IO (String, M.ModuleInterface)
59−retrieve flags interfaces filePath = do
60− iface <- Interface.load (Utils.elmi flags filePath)
28+-- Reader: Runtime flags, always accessible
29+-- Writer: Remember the last module to be accessed
30+-- State: Build up a map of the module interfaces
31+type BuildT m a = RWST Flag.Flags (Last String) BInterfaces m a
32+type Build a = BuildT IO a
33+
34+-- Interfaces, remembering if something was recompiled
35+type BInterfaces = Map.Map String (Bool, M.ModuleInterface)
36+
37+evalBuild :: Flag.Flags -> M.Interfaces -> Build () -> IO (Maybe String)
38+evalBuild flags interfaces b = do
39+ (_, s) <- evalRWST b flags (fmap notUpdated interfaces)
40+ return . getLast $ s
41+ where
42+ notUpdated i = (False, i)
43+
44+-- | Builds a list of files, returning the moduleName of the last one.
45+-- Returns \"\" if the list is empty
46+build :: Flag.Flags -> M.Interfaces -> [FilePath] -> IO String
47+build flags is = fmap (Maybe.fromMaybe "") . evalBuild flags is . buildAll
48+
49+buildAll :: [FilePath] -> Build ()
50+buildAll fs = mapM_ (uncurry build1) (zip [1..] fs)
51+ where build1 :: Integer -> FilePath -> Build ()
52+ build1 num fname = do
53+ shouldCompile <- shouldBeCompiled fname
54+ if shouldCompile
55+ then compile number fname
56+ else retrieve fname
57+
58+ where number = join ["[", show num, " of ", show total, "]"]
59+
60+ total = length fs
61+
62+shouldBeCompiled :: FilePath -> Build Bool
63+shouldBeCompiled filePath = do
64+ flags <- ask
65+ let alreadyCompiled = liftIO $ do
66+ existsi <- doesFileExist (Utils.elmi flags filePath)
67+ existso <- doesFileExist (Utils.elmo flags filePath)
68+ return $ existsi && existso
69+
70+ outDated = liftIO $ do
71+ tsrc <- getModificationTime filePath
72+ tint <- getModificationTime (Utils.elmo flags filePath)
73+ return (tsrc > tint)
74+
75+ dependenciesUpdated = do
76+ eDeps <- liftIO . runErrorT $ Deps.readDeps filePath
77+ case eDeps of
78+ -- Should never actually reach here
79+ Left err -> liftIO $ Print.failure err
80+ Right (_, deps) -> anyM wasCompiled deps
81+
82+
83+ in (not <$> alreadyCompiled) `orM` outDated `orM` dependenciesUpdated
84+
85+wasCompiled :: String -> Build Bool
86+wasCompiled modul = maybe False fst . Map.lookup modul <$> get
87+
88+-- Short-circuiting monadic (||)
89+infixr 2 `orM`
90+orM :: (Monad m) => m Bool -> m Bool -> m Bool
91+orM m1 m2 = do b1 <- m1
92+ if b1
93+ then return b1
94+ else m2
95+
96+anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
97+anyM f = foldr (orM . f) (return False)
98+
99+retrieve :: FilePath -> Build ()
100+retrieve filePath = do
101+ flags <- ask
102+ iface <- liftIO $ Interface.load (Utils.elmi flags filePath)
61103 case Interface.isValid filePath iface of
62104 Right (name, interface) ->
63− do when (Flag.print_types flags) (Print.interfaceTypes interfaces interface)
64− return (name, interface)
65− Left err ->
66− do hPutStrLn stderr err
67− exitFailure
68−
69−compile :: Flag.Flags -> String -> M.Interfaces -> FilePath
70− -> IO (String, M.ModuleInterface)
71−compile flags number interfaces filePath =
72− do source <- readFile filePath
73− let name = getName source
74− printStatus name
75−
76− createDirectoryIfMissing True (Flag.cache_dir flags)
77− createDirectoryIfMissing True (Flag.build_dir flags)
78−
79− metaModule <-
80− case Source.build (Flag.no_prelude flags) interfaces source of
105+ do binterfaces <- get
106+ let interfaces = snd <$> binterfaces
107+ liftIO $ when (Flag.print_types flags) (Print.interfaceTypes interfaces interface)
108+ update name interface False
109+
110+ Left err -> liftIO $ Print.failure err
111+
112+compile :: String -> FilePath -> Build ()
113+compile number filePath =
114+ do flags <- ask
115+ binterfaces <- get
116+ source <- liftIO $ readFile filePath
117+ let interfaces = snd <$> binterfaces
118+ name = getName source
119+ liftIO $ do
120+ printStatus name
121+ createDirectoryIfMissing True (Flag.cache_dir flags)
122+ createDirectoryIfMissing True (Flag.build_dir flags)
123+
124+ metaModule <-
125+ liftIO $ case Source.build (Flag.no_prelude flags) interfaces source of
81126 Right modul -> return modul
82127 Left errors -> do Print.errors errors
83128 exitFailure
84129
85− when (Flag.print_types flags) $ Print.metaTypes interfaces metaModule
86−
87− let intermediate = (name, Canonical.interface name $ M.metaToInterface metaModule)
88− generateCache intermediate metaModule
89− return intermediate
130+ liftIO $ when (Flag.print_types flags) $ Print.metaTypes interfaces metaModule
131+
132+ let newInters = Canonical.interface name $ M.metaToInterface metaModule
133+ generateCache name newInters metaModule
134+ update name newInters True
90135
91136 where
92137 getName source = case Parser.getModuleName source of
@@ -98,8 +143,14 @@ compile flags number interfaces filePath =
98143 , replicate (max 1 (20 - length name)) ' '
99144 , "( " ++ filePath ++ " )" ]
100145
101− generateCache intermediate metaModule = do
102− createDirectoryIfMissing True . dropFileName $ Utils.elmi flags filePath
103− writeFile (Utils.elmo flags filePath) (JS.generate metaModule)
104− withBinaryFile (Utils.elmi flags filePath) WriteMode $ \handle ->
105− L.hPut handle (Binary.encode intermediate)
146+ generateCache name interfs metaModule = do
147+ flags <- ask
148+ liftIO $ do
149+ createDirectoryIfMissing True . dropFileName $ Utils.elmi flags filePath
150+ writeFile (Utils.elmo flags filePath) (JS.generate metaModule)
151+ withBinaryFile (Utils.elmi flags filePath) WriteMode $ \handle ->
152+ L.hPut handle (Binary.encode (name, interfs))
153+
154+update :: String -> M.ModuleInterface -> Bool -> Build ()
155+update name inter wasUpdated = modify (Map.insert name (wasUpdated, inter))
156+ >> tell (Last . Just $ name)
compiler/Build/Print.hs+7−1
@@ -1,5 +1,8 @@
11module Build.Print where
22
3+import System.IO (hPutStrLn, stderr)
4+import System.Exit (exitFailure)
5+
36import qualified Data.Map as Map
47import qualified Data.List as List
58import qualified SourceSyntax.Module as M
@@ -29,4 +32,7 @@ types interfaces types' aliases imports =
2932
3033errors :: [P.Doc] -> IO ()
3134errors errs =
32− mapM_ print (List.intersperse (P.text " ") errs)
35+ mapM_ print (List.intersperse (P.text " ") errs)
36+
37+failure :: String -> IO a
38+failure msg = hPutStrLn stderr msg >> exitFailure
compiler/Compiler.hs+1−2
@@ -37,8 +37,7 @@ build flags rootFile =
3737 then getSortedDependencies (Flag.src_dir flags) builtIns rootFile
3838 else return [rootFile]
3939
40− (moduleName, _) <-
41− File.build flags (length files) builtIns "" files
40+ moduleName <- File.build flags builtIns files
4241
4342 js <- foldM appendToOutput BS.empty files
4443
compiler/Docs.hs+3−4
@@ -7,7 +7,7 @@ import System.FilePath
77import System.Exit
88import System.IO
99
10−import Control.Applicative ((<$>), (<*>))
10+import Control.Applicative ((<$>))
1111import Data.Aeson
1212import Data.Aeson.Encode.Pretty
1313import qualified Data.List as List
@@ -154,11 +154,10 @@ instance ToJSON Type where
154154 Lambda t1 t2 -> toJSON [ "->", toJSON t1, toJSON t2 ]
155155 Var x -> toJSON x
156156 Data name ts -> toJSON (toJSON name : map toJSON ts)
157− EmptyRecord -> object []
158157 Record fields ext -> object $ map (\(n,t) -> Text.pack n .= toJSON t) fields'
159158 where fields' = case ext of
160− EmptyRecord -> fields
161− _ -> ("_",ext) : fields
159+ Nothing -> fields
160+ Just x -> ("_", Var x) : fields
162161
163162ctorToJson tipe (ctor, tipes) =
164163 object [ "name" .= ctor
compiler/Generate/JavaScript.hs+18−36
@@ -8,7 +8,9 @@ import qualified Data.List as List
88import qualified Data.Map as Map
99import qualified Data.Set as Set
1010
11+import Generate.JavaScript.Helpers
1112import qualified Generate.Cases as Case
13+import qualified Generate.JavaScript.Ports as Port
1214import qualified Generate.Markdown as MD
1315import qualified SourceSyntax.Helpers as Help
1416import SourceSyntax.Literal
@@ -20,38 +22,15 @@ import Language.ECMAScript3.Syntax
2022import Language.ECMAScript3.PrettyPrint
2123import qualified Transform.SafeNames as MakeSafe
2224
23−split :: String -> [String]
24−split = go []
25− where
26− go vars str =
27− case break (=='.') str of
28− (x,_:rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
29− | otherwise -> go (vars ++ [x]) rest
30− (x,[]) -> vars ++ [x]
31−
32−var name = Id () name
33−ref name = VarRef () (var name)
34−prop name = PropId () (var name)
35−f <| x = CallExpr () f [x]
36−args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
37−function args stmts = FuncExpr () Nothing (map var args) stmts
38−call = CallExpr ()
39−string = StringLit ()
40−
41−dotSep vars =
42− case vars of
43− x:xs -> foldl (DotRef ()) (ref x) (map var xs)
44− [] -> error "dotSep must be called on a non-empty list of variables"
45−
46−obj = dotSep . split
47−
4825varDecl :: String -> Expression () -> VarDecl ()
4926varDecl x expr =
5027 VarDecl () (var x) (Just expr)
5128
29+include :: String -> String -> VarDecl ()
5230include alias moduleName =
5331 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
5432
33+internalImports :: String -> Statement ()
5534internalImports name =
5635 VarDeclStmt ()
5736 [ varDecl "_N" (obj "Elm.Native")
@@ -155,7 +134,7 @@ expression (L span expr) =
155134 do let (defs',e') = flattenLets defs e
156135 stmts <- concat <$> mapM definition defs'
157136 exp <- expression e'
158− return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
137+ return $ function [] (stmts ++ [ ret exp ]) `call` []
159138
160139 MultiIf branches ->
161140 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
@@ -199,13 +178,13 @@ expression (L span expr) =
199178 GLShader _uid src _tipe ->
200179 return . literal $ Str src
201180
202− PortIn name _ _ handler ->
203− do handler' <- expression handler
204− return $ obj "Native.Ports.portIn" `call` [ string name, handler' ]
181+ PortIn name tipe ->
182+ return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
205183
206− PortOut name _ signal ->
207− do signal' <- expression signal
208− return $ obj "Native.Ports.portOut" `call` [ string name, signal' ]
184+ PortOut name tipe value ->
185+ do value' <- expression value
186+ return $ obj "Native.Ports.portOut" `call`
187+ [ string name, Port.outgoing tipe, value' ]
209188
210189definition :: Def -> State Int [Statement ()]
211190definition (Definition pattern expr@(L span _) _) = do
@@ -251,7 +230,7 @@ definition (Definition pattern expr@(L span _) _) = do
251230 toDef y = let expr = L span $ Case (mkVar "$") [(pattern, mkVar y)]
252231 in definition $ Definition (PVar y) expr Nothing
253232
254−match :: (Show a) => a -> Case.Match -> State Int [Statement ()]
233+match :: SrcSpan -> Case.Match -> State Int [Statement ()]
255234match span mtch =
256235 case mtch of
257236 Case.Match name clauses mtch' ->
@@ -273,7 +252,7 @@ match span mtch =
273252 Case.Break -> return [BreakStmt () Nothing]
274253 Case.Other e ->
275254 do e' <- expression e
276− return [ ReturnStmt () (Just e') ]
255+ return [ ret e' ]
277256 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
278257 where
279258 dropEnd acc [] = acc
@@ -282,6 +261,7 @@ match span mtch =
282261 Case.Other _ -> acc ++ [m]
283262 _ -> dropEnd (acc ++ [m]) ms
284263
264+clause :: SrcSpan -> String -> Case.Clause -> State Int (Bool, CaseClause ())
285265clause span variable (Case.Clause value vars mtch) =
286266 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
287267 where
@@ -296,6 +276,7 @@ clause span variable (Case.Clause value vars mtch) =
296276 [] -> name
297277 is -> drop (last is + 1) name
298278
279+flattenLets :: [Def] -> LExpr -> ([Def], LExpr)
299280flattenLets defs lexpr@(L _ expr) =
300281 case expr of
301282 Let ds body -> flattenLets (defs ++ ds) body
@@ -311,13 +292,13 @@ generate unsafeModule =
311292 programStmts =
312293 concat
313294 [ setup (Just "_elm") (names modul ++ ["values"])
314− , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
295+ , [ IfSingleStmt () thisModule (ret thisModule) ]
315296 , [ internalImports (List.intercalate "." (names modul)) ]
316297 , concatMap jsImport . Set.toList . Set.fromList . map fst $ imports modul
317298 , [ assign ["_op"] (ObjectLit () []) ]
318299 , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
319300 , [ jsExports ]
320− , [ ReturnStmt () (Just thisModule) ]
301+ , [ ret thisModule ]
321302 ]
322303
323304 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
@@ -343,6 +324,7 @@ generate unsafeModule =
343324 Nothing -> tail . init $ List.inits path
344325 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
345326
327+binop :: SrcSpan -> String -> LExpr -> LExpr -> State Int (Expression ())
346328binop span op e1 e2 =
347329 case op of
348330 "Basics.." ->
compiler/Generate/JavaScript/Helpers.hsnew file+37
@@ -0,0 +1,37 @@
1+{-# OPTIONS_GHC -W #-}
2+module Generate.JavaScript.Helpers where
3+
4+import qualified SourceSyntax.Helpers as Help
5+import Language.ECMAScript3.Syntax
6+
7+split :: String -> [String]
8+split = go []
9+ where
10+ go vars str =
11+ case break (=='.') str of
12+ (x,_:rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
13+ | otherwise -> go (vars ++ [x]) rest
14+ (x,[]) -> vars ++ [x]
15+
16+var name = Id () name
17+ref name = VarRef () (var name)
18+prop name = PropId () (var name)
19+f <| x = CallExpr () f [x]
20+ret e = ReturnStmt () (Just e)
21+args ==> e = FuncExpr () Nothing (map var args) [ ret e ]
22+function args stmts = FuncExpr () Nothing (map var args) stmts
23+call = CallExpr ()
24+string = StringLit ()
25+
26+dotSep vars =
27+ case vars of
28+ x:xs -> foldl (DotRef ()) (ref x) (map var xs)
29+ [] -> error "dotSep must be called on a non-empty list of variables"
30+
31+obj = dotSep . split
32+
33+equal a b = InfixExpr () OpStrictEq a b
34+instanceof tipe x =
35+ InfixExpr () OpLAnd (typeof "object" x) (InfixExpr () OpInstanceof x (ref tipe))
36+typeof tipe x = equal (PrefixExpr () PrefixTypeof x) (string tipe)
37+member field x = InfixExpr () OpIn (string field) x
compiler/Generate/JavaScript/Ports.hsnew file+130
@@ -0,0 +1,130 @@
1+{-# OPTIONS_GHC -W #-}
2+module Generate.JavaScript.Ports (incoming, outgoing) where
3+
4+import Generate.JavaScript.Helpers
5+import qualified SourceSyntax.Helpers as Help
6+import SourceSyntax.Type as T
7+import Language.ECMAScript3.Syntax
8+
9+data JSType = JSNumber | JSBoolean | JSString | JSArray | JSObject [String]
10+ deriving Show
11+
12+check :: Expression () -> JSType -> Expression () -> Expression ()
13+check x jsType continue =
14+ CondExpr () (jsFold OpLOr checks x) continue throw
15+ where
16+ jsFold op checks value = foldl1 (InfixExpr () op) (map ($value) checks)
17+ throw = obj "_E.raise" <| InfixExpr () OpAdd msg x
18+ msg = string ("invalid input, expecting " ++ show jsType ++ " but got ")
19+ checks = case jsType of
20+ JSNumber -> [typeof "number"]
21+ JSBoolean -> [typeof "boolean"]
22+ JSString -> [typeof "string", instanceof "String"]
23+ JSArray -> [instanceof "Array"]
24+ JSObject fields -> [jsFold OpLAnd (typeof "object" : map member fields)]
25+
26+incoming :: Type -> Expression ()
27+incoming tipe =
28+ case tipe of
29+ Data "Signal.Signal" [t] ->
30+ obj "Native.Ports.incomingSignal" <| incoming t
31+ _ -> ["v"] ==> inc tipe (ref "v")
32+
33+inc :: Type -> Expression () -> Expression ()
34+inc tipe x =
35+ case tipe of
36+ Lambda _ _ -> error "functions should not be allowed through input ports"
37+ Var _ -> error "type variables should not be allowed through input ports"
38+ Data ctor []
39+ | ctor == "Int" -> elm JSNumber
40+ | ctor == "Float" -> elm JSNumber
41+ | ctor == "Bool" -> elm JSBoolean
42+ | ctor == "String" -> elm JSString
43+ | ctor == "JavaScript.JSNumber" -> js JSNumber
44+ | ctor == "JavaScript.JSBool" -> js JSBoolean
45+ | ctor == "JavaScript.JSString" -> js JSString
46+ where
47+ elm checks = check x checks (obj ("_J.to" ++ ctor) <| x)
48+ js checks = check x checks x
49+
50+ Data ctor [t]
51+ | ctor == "Maybe.Maybe" ->
52+ CondExpr () (equal x (NullLit ()))
53+ (obj "Maybe.Nothing")
54+ (obj "Maybe.Just" <| inc t x)
55+
56+ | ctor == "_List" ->
57+ check x JSArray (obj "_J.toList" <| array)
58+ where
59+ array = DotRef () x (var "map") <| incoming t
60+
61+ Data ctor ts | Help.isTuple ctor -> check x JSArray tuple
62+ where
63+ tuple = ObjectLit () $ (PropId () (var "ctor"), string ctor) : values
64+ values = zipWith convert [0..] ts
65+ convert n t = ( PropId () $ var ('_':show n)
66+ , inc t (BracketRef () x (IntLit () n)))
67+
68+ Data _ _ -> error "bad ADT got to port generation code"
69+
70+ Record _ (Just _) -> error "bad record got to port generation code"
71+
72+ Record fields Nothing -> check x (JSObject (map fst fields)) object
73+ where
74+ object = ObjectLit () $ (PropId () (var "_"), ObjectLit () []) : keys
75+ keys = map convert fields
76+ convert (f,t) = (PropId () (var f), inc t (DotRef () x (var f)))
77+
78+outgoing tipe =
79+ case tipe of
80+ Data "Signal.Signal" [t] ->
81+ obj "Native.Ports.outgoingSignal" <| outgoing t
82+ _ -> ["v"] ==> out tipe (ref "v")
83+
84+out :: Type -> Expression () -> Expression ()
85+out tipe x =
86+ case tipe of
87+ Lambda _ _
88+ | numArgs > 1 && numArgs < 10 ->
89+ func (ref ('A':show numArgs) `call` (x:values))
90+ | otherwise -> func (foldl (<|) x values)
91+ where
92+ ts = T.collectLambdas tipe
93+ numArgs = length ts - 1
94+ args = map (\n -> '_' : show n) [0..]
95+ values = zipWith inc (init ts) (map ref args)
96+ func body = function (take numArgs args)
97+ [ VarDeclStmt () [VarDecl () (var "_r") (Just body)]
98+ , ret (out (last ts) (ref "_r"))
99+ ]
100+
101+ Var _ -> error "type variables should not be allowed through input ports"
102+ Data ctor []
103+ | ctor `elem` ["Int","Float","Bool","String"] -> obj ("_J.from" ++ ctor) <| x
104+ | ctor `elem` jsPrims -> x
105+ where
106+ jsPrims = map ("JavaScript.JS"++) ["Number","Bool","String"]
107+
108+ Data ctor [t]
109+ | ctor == "Maybe.Maybe" ->
110+ CondExpr () (equal (DotRef () x (var "ctor")) (string "Nothing"))
111+ (NullLit ())
112+ (DotRef () x (var "_0"))
113+
114+ | ctor == "_List" ->
115+ DotRef () (obj "_J.fromList" <| x) (var "map") <| outgoing t
116+
117+ Data ctor ts | Help.isTuple ctor ->
118+ ArrayLit () $ zipWith convert [0..] ts
119+ where
120+ convert n t = out t $ DotRef () x $ var ('_':show n)
121+
122+ Data _ _ -> error "bad ADT got to port generation code"
123+
124+ Record _ (Just _) -> error "bad record got to port generation code"
125+
126+ Record fields Nothing ->
127+ ObjectLit () keys
128+ where
129+ keys = map convert fields
130+ convert (f,t) = (PropId () (var f), out t (DotRef () x (var f)))
compiler/Parse/Declaration.hs+2−3
@@ -59,6 +59,5 @@ port =
5959 name <- lowVar
6060 whitespace
6161 let port' op ctor expr = do { try op ; whitespace ; ctor name <$> expr }
62− D.Port <$> choice [ port' hasType D.PortAnnotation Type.expr
63− , port' (string "<-") D.SendDefinition Expr.expr
64− , port' (string "->") D.RecvDefinition Expr.expr ]
62+ D.Port <$> choice [ port' hasType D.PPAnnotation Type.expr
63+ , port' equals D.PPDef Expr.expr ]
compiler/Parse/Helpers.hs−1
@@ -7,7 +7,6 @@ import Control.Monad
77import Control.Monad.State
88import Data.Char (isUpper)
99import qualified Data.Set as Set
10−import Data.Map (Map)
1110import qualified Data.Map as Map
1211import qualified Language.GLSL.Parser as GLP
1312import Language.GLSL.Syntax
compiler/Parse/Type.hs+7−7
@@ -1,7 +1,7 @@
11{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
22module Parse.Type where
33
4−import Control.Applicative ((<$>),(<*>))
4+import Control.Applicative ((<$>),(<*>),(<*))
55import Data.List (intercalate)
66import Text.Parsec
77
@@ -23,19 +23,19 @@ tuple = do ts <- parens (commaSep expr)
2323record :: IParser T.Type
2424record =
2525 do char '{' ; whitespace
26− (ext,fs) <- extended <|> normal
26+ rcrd <- extended <|> normal
2727 dumbWhitespace ; char '}'
28− return (T.Record fs ext)
28+ return rcrd
2929 where
30− normal = (,) T.EmptyRecord <$> commaSep fields
30+ normal = flip T.Record Nothing <$> commaSep field
3131
3232 -- extended record types require at least one field
3333 extended = do
34− ext <- try (const <$> tvar <*> (whitespace >> string "|"))
34+ ext <- try (lowVar <* (whitespace >> string "|"))
3535 whitespace
36− (,) ext <$> commaSep1 fields
36+ flip T.Record (Just ext) <$> commaSep1 field
3737
38− fields = do
38+ field = do
3939 lbl <- rLabel
4040 whitespace >> hasType >> whitespace
4141 (,) lbl <$> expr
compiler/SourceSyntax/Declaration.hs+10−14
@@ -22,14 +22,13 @@ data Derivation = Json | JS | Binary | New
2222 deriving (Eq, Show)
2323
2424data ParsePort
25− = PortAnnotation String T.Type
26− | SendDefinition String Expr.LParseExpr
27− | RecvDefinition String Expr.LParseExpr
25+ = PPAnnotation String T.Type
26+ | PPDef String Expr.LParseExpr
2827 deriving (Show)
2928
3029data Port
31− = Send String Expr.LExpr T.Type
32− | Recv String Expr.LExpr T.Type
30+ = Out String Expr.LExpr T.Type
31+ | In String T.Type
3332 deriving (Show)
3433
3534type ParseDeclaration = Declaration' ParsePort Expr.ParseDef
@@ -100,19 +99,16 @@ instance (Pretty port, Pretty def) => Pretty (Declaration' port def) where
10099instance Pretty ParsePort where
101100 pretty port =
102101 case port of
103− PortAnnotation name tipe -> prettyPort name ":" tipe
104− SendDefinition name expr -> prettyPort name "<-" expr
105− RecvDefinition name expr -> prettyPort name "->" expr
102+ PPAnnotation name tipe -> prettyPort name ":" tipe
103+ PPDef name expr -> prettyPort name "=" expr
106104
107105instance Pretty Port where
108106 pretty port =
109107 case port of
110− Send name expr tipe -> mkPort "<-" name expr tipe
111− Recv name expr tipe -> mkPort "->" name expr tipe
112− where
113− mkPort arrow name expr tipe =
114− P.vcat [ prettyPort name ":" tipe
115− , prettyPort name arrow expr ]
108+ In name tipe -> prettyPort name ":" tipe
109+ Out name expr tipe -> P.vcat [ prettyPort name ":" tipe
110+ , prettyPort name "=" expr ]
111+
116112
117113prettyPort :: (Pretty a) => String -> String -> a -> Doc
118114prettyPort name op e = P.text "port" <+> P.text name <+> P.text op <+> pretty e
compiler/SourceSyntax/Expression.hs+46−29
@@ -1,5 +1,12 @@
11{-# OPTIONS_GHC -Wall #-}
22module SourceSyntax.Expression where
3+{-| The Abstract Syntax Tree (AST) for expressions comes in a couple formats.
4+The first is the fully general version and is labeled with a prime (Expr').
5+The others are specialized versions of the AST that represent specific phases
6+of the compilation process. I expect there to be more phases as we begin to
7+enrich the AST with more information.
8+-}
9+
310
411import SourceSyntax.PrettyPrint
512import Text.PrettyPrint as P
@@ -8,25 +15,23 @@ import qualified SourceSyntax.Location as Location
815import qualified SourceSyntax.Pattern as Pattern
916import qualified SourceSyntax.Type as SrcType
1017import qualified SourceSyntax.Literal as Literal
11−import qualified Type.Type as Type
1218
13−{-| This is a located expression. -}
19+---- GENERAL AST ----
20+
21+{-| This is a located expression, meaning it is tagged with info about where it
22+came from in the source code. Expr' is defined in terms of LExpr' so that the
23+location information does not need to be an extra field on every constructor.
24+-}
1425type LExpr' def = Location.Located (Expr' def)
1526
16−{-| This is a fully general expression. The compilation process will enrich
17−this data structure with additional information after the initial parse.
18−The type holes let us reflect these structural changes in the types.
27+{-| This is a fully general Abstract Syntax Tree (AST) for expressions. It has
28+"type holes" that allow us to enrich the AST with additional information as we
29+move through the compilation process. The type holes let us show these
30+structural changes in the types. The only type hole right now is:
1931
2032 def: Parsing allows two kinds of definitions (type annotations or definitions),
2133 but later checks will see that they are well formed and combine them.
2234
23− t: for adding type information, currently unused
24−
25− v: for enriching variables with information like provenonce, currently unused
26−
27−Please don't get on my case to take the unused ones out. I have considered it.
28−It's a huge pain to add and remove, it will get used eventually, and it's not
29−hurting anything as it is.
3035-}
3136data Expr' def
3237 = Literal Literal.Literal
@@ -47,29 +52,38 @@ data Expr' def
4752 | Record [(String, LExpr' def)]
4853 | Markdown String String [LExpr' def]
4954 -- for type checking and code gen only
50− | PortIn String SrcType.Type Type.Variable (LExpr' def)
55+ | PortIn String SrcType.Type
5156 | PortOut String SrcType.Type (LExpr' def)
5257 | GLShader String String Literal.GLShaderTipe
5358
59+
60+---- SPECIALIZED ASTs ----
61+
62+{-| Expressions created by the parser. These use a split representation of type
63+annotations and definitions, which is how they appear in source code and how
64+they are parsed.
65+-}
5466type ParseExpr = Expr' ParseDef
5567type LParseExpr = LExpr' ParseDef
56−type LExpr = LExpr' Def
57−type Expr = Expr' Def
5868
59−{-| Style of definitions as they are parsed. Users specify them
60−separately, and other checks will make sure they can be combined.
61−-}
6269data ParseDef
6370 = Def Pattern.Pattern LParseExpr
6471 | TypeAnnotation String SrcType.Type
6572 deriving (Show)
6673
67−{-| After checking that type annotations and definitions are all
68−valid, they can be combined.
74+{-| "Normal" expressions. When the compiler checks that type annotations and
75+ports are all paired with definitions in the appropriate order, it collapses
76+them into a Def that is easier to work with in later phases of compilation.
6977-}
70−data Def = Definition Pattern.Pattern (LExpr' Def) (Maybe SrcType.Type)
78+type LExpr = LExpr' Def
79+type Expr = Expr' Def
80+
81+data Def = Definition Pattern.Pattern LExpr (Maybe SrcType.Type)
7182 deriving (Show)
7283
84+
85+---- UTILITIES ----
86+
7387tuple :: [LExpr' def] -> Expr' def
7488tuple es = Data ("_Tuple" ++ show (length es)) es
7589
@@ -98,8 +112,10 @@ instance Pretty def => Pretty (Expr' def) where
98112 P.text "-" <> prettyParens e
99113 Binop op e1 e2 -> P.sep [ prettyParens e1 <+> P.text op', prettyParens e2 ]
100114 where op' = if Help.isOp op then op else "`" ++ op ++ "`"
101− Lambda p e -> let (ps,body) = collectLambdas (Location.none $ Lambda p e)
102− in P.text "\\" <> P.sep ps <+> P.text "->" <+> pretty body
115+ Lambda p e -> P.text "\\" <> args <+> P.text "->" <+> pretty body
116+ where
117+ (ps,body) = collectLambdas (Location.none $ Lambda p e)
118+ args = P.sep (map Pattern.prettyParens ps)
103119 App _ _ -> P.hang func 2 (P.sep args)
104120 where func:args = map prettyParens (collectApps (Location.none expr))
105121 MultiIf branches -> P.text "if" $$ nest 3 (vcat $ map iff branches)
@@ -115,7 +131,9 @@ instance Pretty def => Pretty (Expr' def) where
115131 pretty' (p,b) = pretty p <+> P.text "->" <+> pretty b
116132 Data "::" [hd,tl] -> pretty hd <+> P.text "::" <+> pretty tl
117133 Data "[]" [] -> P.text "[]"
118− Data name es -> P.hang (P.text name) 2 (P.sep (map prettyParens es))
134+ Data name es
135+ | Help.isTuple name -> P.parens (commaCat (map pretty es))
136+ | otherwise -> P.hang (P.text name) 2 (P.sep (map prettyParens es))
119137 Access e x -> prettyParens e <> P.text "." <> variable x
120138 Remove e x -> P.braces (pretty e <+> P.text "-" <+> variable x)
121139 Insert (Location.L _ (Remove e y)) x v ->
@@ -139,7 +157,7 @@ instance Pretty def => Pretty (Expr' def) where
139157
140158 GLShader _ _ _ -> P.text "[glShader| ... |]"
141159
142− PortIn _ _ _ handler -> pretty handler
160+ PortIn name _ -> P.text $ "<port:" ++ name ++ ">"
143161
144162 PortOut _ _ signal -> pretty signal
145163
@@ -166,12 +184,11 @@ collectApps lexpr@(Location.L _ expr) =
166184 App a b -> collectApps a ++ [b]
167185 _ -> [lexpr]
168186
169−collectLambdas :: LExpr' def -> ([Doc], LExpr' def)
187+collectLambdas :: LExpr' def -> ([Pattern.Pattern], LExpr' def)
170188collectLambdas lexpr@(Location.L _ expr) =
171189 case expr of
172− Lambda pattern body ->
173− let (ps, body') = collectLambdas body
174− in (pretty pattern : ps, body')
190+ Lambda pattern body -> (pattern : ps, body')
191+ where (ps, body') = collectLambdas body
175192 _ -> ([], lexpr)
176193
177194prettyParens :: (Pretty def) => LExpr' def -> Doc
compiler/SourceSyntax/Pattern.hs+3−3
@@ -1,7 +1,7 @@
11{-# OPTIONS_GHC -Wall #-}
22module SourceSyntax.Pattern where
33
4−import SourceSyntax.Helpers as Help
4+import qualified SourceSyntax.Helpers as Help
55import SourceSyntax.PrettyPrint
66import Text.PrettyPrint as PP
77import qualified Data.Set as Set
@@ -51,7 +51,7 @@ instance Pretty Pattern where
5151 PData "::" _ -> True
5252 _ -> False
5353 PData name ps ->
54− if isTuple name then
54+ if Help.isTuple name then
5555 PP.parens . commaCat $ map pretty ps
5656 else hsep (PP.text name : map prettyParens ps)
5757
@@ -60,6 +60,6 @@ prettyParens pattern = parensIf needsThem (pretty pattern)
6060 where
6161 needsThem =
6262 case pattern of
63− PData name (_:_) | not (isTuple name) -> True
63+ PData name (_:_) | not (Help.isTuple name) -> True
6464 PAlias _ _ -> True
6565 _ -> False
compiler/SourceSyntax/Type.hs+18−27
@@ -1,4 +1,4 @@
1−{-# OPTIONS_GHC -Wall #-}
1+{-# OPTIONS_GHC -W #-}
22module SourceSyntax.Type where
33
44import Data.Binary
@@ -11,8 +11,7 @@ import Text.PrettyPrint as P
1111data Type = Lambda Type Type
1212 | Var String
1313 | Data String [Type]
14− | EmptyRecord
15− | Record [(String,Type)] Type
14+ | Record [(String,Type)] (Maybe String)
1615 deriving (Eq, Show)
1716
1817fieldMap :: [(String,a)] -> Map.Map String [a]
@@ -20,7 +19,7 @@ fieldMap fields =
2019 foldl (\r (x,t) -> Map.insertWith (++) x [t] r) Map.empty fields
2120
2221recordOf :: [(String,Type)] -> Type
23−recordOf fields = Record fields EmptyRecord
22+recordOf fields = Record fields Nothing
2423
2524listOf :: Type -> Type
2625listOf t = Data "_List" [t]
@@ -33,35 +32,30 @@ instance Pretty Type where
3332 pretty tipe =
3433 case tipe of
3534 Lambda _ _ -> P.sep [ t, P.sep (map (P.text "->" <+>) ts) ]
36− where t:ts = collectLambdas tipe
35+ where
36+ t:ts = map prettyLambda (collectLambdas tipe)
37+ prettyLambda t = case t of
38+ Lambda _ _ -> P.parens (pretty t)
39+ _ -> pretty t
40+
3741 Var x -> P.text x
3842 Data "_List" [t] -> P.brackets (pretty t)
3943 Data name tipes
4044 | Help.isTuple name -> P.parens . P.sep . P.punctuate P.comma $ map pretty tipes
4145 | otherwise -> P.hang (P.text name) 2 (P.sep $ map prettyParens tipes)
42− EmptyRecord -> P.braces P.empty
43− Record _ _ -> P.braces $ case ext of
44− EmptyRecord -> prettyFields
45− _ -> P.hang (pretty ext <+> P.text "|") 4 prettyFields
46+ Record fields ext ->
47+ P.braces $ case ext of
48+ Nothing -> prettyFields
49+ Just x -> P.hang (P.text x <+> P.text "|") 4 prettyFields
4650 where
47− (fields, ext) = collectRecords tipe
4851 prettyField (f,t) = P.text f <+> P.text ":" <+> pretty t
4952 prettyFields = commaSep . map prettyField $ fields
5053
51−collectLambdas :: Type -> [Doc]
54+collectLambdas :: Type -> [Type]
5255collectLambdas tipe =
5356 case tipe of
54− Lambda arg@(Lambda _ _) body -> P.parens (pretty arg) : collectLambdas body
55− Lambda arg body -> pretty arg : collectLambdas body
56− _ -> [pretty tipe]
57−
58−collectRecords :: Type -> ([(String,Type)], Type)
59−collectRecords = go []
60− where
61− go fields tipe =
62− case tipe of
63− Record fs ext -> go (fs ++ fields) ext
64− _ -> (fields, tipe)
57+ Lambda arg body -> arg : collectLambdas body
58+ _ -> [tipe]
6559
6660prettyParens :: Type -> Doc
6761prettyParens tipe = parensIf needed (pretty tipe)
@@ -83,10 +77,8 @@ instance Binary Type where
8377 putWord8 1 >> put x
8478 Data ctor tipes ->
8579 putWord8 2 >> put ctor >> put tipes
86− EmptyRecord ->
87− putWord8 3
8880 Record fs ext ->
89− putWord8 4 >> put fs >> put ext
81+ putWord8 3 >> put fs >> put ext
9082
9183 get = do
9284 n <- getWord8
@@ -94,6 +86,5 @@ instance Binary Type where
9486 0 -> Lambda <$> get <*> get
9587 1 -> Var <$> get
9688 2 -> Data <$> get <*> get
97− 3 -> return EmptyRecord
98− 4 -> Record <$> get <*> get
89+ 3 -> Record <$> get <*> get
9990 _ -> error "Error reading a valid type from serialized string"
compiler/Transform/Canonicalize.hs+4−9
@@ -4,6 +4,7 @@ module Transform.Canonicalize (interface, metadataModule) where
44import Control.Arrow ((***))
55import Control.Applicative (Applicative,(<$>),(<*>))
66import Control.Monad.Identity
7+import qualified Data.Traversable as T
78import qualified Data.Map as Map
89import qualified Data.Set as Set
910import qualified Data.List as List
@@ -46,8 +47,7 @@ renameType renamer tipe =
4647 Type.Lambda a b -> Type.Lambda <$> rnm a <*> rnm b
4748 Type.Var _ -> return tipe
4849 Type.Data name ts -> Type.Data <$> renamer name <*> mapM rnm ts
49− Type.EmptyRecord -> return tipe
50− Type.Record fields ext -> Type.Record <$> mapM rnm' fields <*> rnm ext
50+ Type.Record fields ext -> Type.Record <$> mapM rnm' fields <*> return ext
5151 where rnm' (f,t) = (,) f <$> rnm t
5252
5353metadataModule :: Interfaces -> MetadataModule -> Either [Doc] MetadataModule
@@ -150,9 +150,7 @@ rename env (L s expr) =
150150 rename' (Definition p body mtipe) =
151151 Definition <$> format (renamePattern env' p)
152152 <*> rename env' body
153− <*> case mtipe of
154− Nothing -> return Nothing
155− Just tipe -> Just <$> renameType' env' tipe
153+ <*> T.traverse (renameType' env') mtipe
156154
157155 Var x -> Var <$> format (replace "variable" env x)
158156
@@ -167,10 +165,7 @@ rename env (L s expr) =
167165
168166 Markdown uid md es -> Markdown uid md <$> mapM rnm es
169167
170− PortIn name st tt handler ->
171− do st' <- renameType' env st
172− handler' <- rnm handler
173− return $ PortIn name st' tt handler'
168+ PortIn name st -> PortIn name <$> renameType' env st
174169
175170 PortOut name st signal -> PortOut name <$> renameType' env st <*> rnm signal
176171
compiler/Transform/Check.hs+8−7
@@ -1,6 +1,7 @@
11{-# OPTIONS_GHC -Wall #-}
22module Transform.Check (mistakes) where
33
4+import qualified Control.Arrow as Arrow
45import qualified Data.List as List
56import qualified Data.Maybe as Maybe
67import qualified Data.Set as Set
@@ -44,10 +45,11 @@ duplicates decls =
4445 unzip [ (pat,expr) | D.Definition (E.Definition pat expr _) <- decls ]
4546
4647 (portNames, portExprs) =
47− unzip $ flip map [ port | D.Port port <- decls ] $ \port ->
48+ Arrow.second concat $ unzip $
49+ flip map [ port | D.Port port <- decls ] $ \port ->
4850 case port of
49− D.Send name expr _ -> (name,expr)
50− D.Recv name expr _ -> (name,expr)
51+ D.Out name expr _ -> (name, [expr])
52+ D.In name _ -> (name, [])
5153
5254 getNames = Set.toList . Pattern.boundVars
5355
@@ -100,8 +102,8 @@ illFormedTypes decls = map report (Maybe.mapMaybe isIllFormed (aliases ++ adts))
100102 T.Lambda t1 t2 -> Set.union (freeVars t1) (freeVars t2)
101103 T.Var x -> Set.singleton x
102104 T.Data _ ts -> Set.unions (map freeVars ts)
103− T.EmptyRecord -> Set.empty
104− T.Record fields ext -> Set.unions (freeVars ext : map (freeVars . snd) fields)
105+ T.Record fields ext -> Set.unions (ext' : map (freeVars . snd) fields)
106+ where ext' = maybe Set.empty Set.singleton ext
105107
106108 undeclared tvars tipes = Set.difference used declared
107109 where
@@ -145,8 +147,7 @@ infiniteTypeAliases decls =
145147 T.Lambda a b -> infinite a || infinite b
146148 T.Var _ -> False
147149 T.Data name' ts -> name == name' || any infinite ts
148− T.EmptyRecord -> False
149− T.Record fields ext -> infinite ext || any (infinite . snd) fields
150+ T.Record fields _ -> any (infinite . snd) fields
150151
151152 indented :: D.Declaration -> Doc
152153 indented decl = P.text "\n " <> pretty decl <> P.text "\n"
compiler/Transform/Declaration.hs+5−9
@@ -52,16 +52,12 @@ combineAnnotations = go
5252 -- combine ports
5353 Port port : portRest ->
5454 case port of
55− SendDefinition name _ -> Left (msg name)
56− RecvDefinition name _ -> Left (msg name)
57− PortAnnotation name tipe ->
55+ PPDef name _ -> Left (msg name)
56+ PPAnnotation name tipe ->
5857 case portRest of
59− Port (SendDefinition name' expr) : rest | name == name' ->
58+ Port (PPDef name' expr) : rest | name == name' ->
6059 do expr' <- exprCombineAnnotations expr
61− (:) (Port (Send name expr' tipe)) <$> go rest
60+ (:) (Port (Out name expr' tipe)) <$> go rest
6261
63− Port (RecvDefinition name' expr) : rest | name == name' ->
64− do expr' <- exprCombineAnnotations expr
65− (:) (Port (Recv name expr' tipe)) <$> go rest
62+ _ -> (:) (Port (In name tipe)) <$> go portRest
6663
67− _ -> Left (msg name)
compiler/Transform/Expression.hs+6−7
@@ -5,12 +5,11 @@ import Control.Applicative ((<$>),(<*>))
55import SourceSyntax.Expression
66import SourceSyntax.Location
77import qualified SourceSyntax.Type as ST
8−import qualified Type.Type as TT
98
109crawlLet :: ([def] -> Either a [def']) -> LExpr' def -> Either a (LExpr' def')
11−crawlLet = crawl (\_ _ _ -> return ()) (\_ _ -> return ())
10+crawlLet = crawl (\_ _ -> return ()) (\_ _ -> return ())
1211
13−checkPorts :: (String -> ST.Type -> TT.Variable -> Either a ())
12+checkPorts :: (String -> ST.Type -> Either a ())
1413 -> (String -> ST.Type -> Either a ())
1514 -> LExpr
1615 -> Either a LExpr
@@ -21,7 +20,7 @@ checkPorts inCheck outCheck expr =
2120 do _ <- checkPorts inCheck outCheck body
2221 return def
2322
24−crawl :: (String -> ST.Type -> TT.Variable -> Either a ())
23+crawl :: (String -> ST.Type -> Either a ())
2524 -> (String -> ST.Type -> Either a ())
2625 -> ([def] -> Either a [def'])
2726 -> LExpr' def
@@ -49,9 +48,9 @@ crawl portInCheck portOutCheck defsTransform = go
4948 Markdown uid md es -> Markdown uid md <$> mapM go es
5049 Let defs body -> Let <$> defsTransform defs <*> go body
5150 GLShader uid src gltipe -> return $ GLShader uid src gltipe
52− PortIn name st tt handler ->
53− do portInCheck name st tt
54− PortIn name st tt <$> go handler
51+ PortIn name st ->
52+ do portInCheck name st
53+ return $ PortIn name st
5554 PortOut name st signal ->
5655 do portOutCheck name st
5756 PortOut name st <$> go signal
compiler/Transform/SafeNames.hs+1−1
@@ -50,7 +50,7 @@ expression (L loc expr) =
5050 Record fs -> Record (map (var *** f) fs)
5151 Markdown uid md es -> Markdown uid md (map f es)
5252 GLShader _ _ _ -> expr
53− PortIn name st tt handler -> PortIn name st tt (f handler)
53+ PortIn name st -> PortIn name st
5454 PortOut name st signal -> PortOut name st (f signal)
5555
5656definition :: Def -> Def
compiler/Transform/SortDefinitions.hs+1−1
@@ -87,7 +87,7 @@ reorder (L s expr) =
8787
8888 PortOut name st signal -> PortOut name st <$> reorder signal
8989
90− PortIn name st tt handler -> PortIn name st tt <$> reorder handler
90+ PortIn name st -> return $ PortIn name st
9191
9292 -- Actually do some reordering
9393 Let defs body ->
compiler/Transform/Substitute.hs+1−1
@@ -39,5 +39,5 @@ subst old new expr =
3939 Literal _ -> expr
4040 Markdown uid md es -> Markdown uid md (map f es)
4141 GLShader _ _ _ -> expr
42− PortIn name st tt handler -> PortIn name st tt (f handler)
42+ PortIn name st -> PortIn name st
4343 PortOut name st signal -> PortOut name st (f signal)
compiler/Type/Alias.hs+14−14
@@ -1,3 +1,4 @@
1+{-# OPTIONS_GHC -W #-}
12module Type.Alias (realias, rules, canonicalRealias, Rules) where
23
34import Control.Applicative ((<$>),(<*>))
@@ -30,10 +31,9 @@ localizer moduleImports = go
3031 go tipe =
3132 case tipe of
3233 Var _ -> tipe
33− EmptyRecord -> tipe
3434 Lambda t1 t2 -> Lambda (go t1) (go t2)
3535 Data name ts -> Data (localize name) (map go ts)
36− Record fs ext -> Record (map (second go) fs) (go ext)
36+ Record fs ext -> Record (map (second go) fs) ext
3737
3838 byMethod = foldr (\(n,m) d -> Map.insertWith (++) n [m] d)
3939 Map.empty moduleImports
@@ -83,10 +83,9 @@ canonicalRealias aliases tipe =
8383 tipe' =
8484 case tipe of
8585 Var _ -> tipe
86− EmptyRecord -> tipe
8786 Lambda t1 t2 -> Lambda (f t1) (f t2)
8887 Data name ts -> Data name (map f ts)
89− Record fs ext -> Record (map (second f) fs) (f ext)
88+ Record fs ext -> Record (map (second f) fs) ext
9089
9190allEqual [] = True
9291allEqual (x:xs) = all (==x) xs
@@ -102,8 +101,7 @@ bestType tipes = fst $ List.minimumBy (\a b -> compare (snd a) (snd b)) pairs
102101 Lambda t1 t2 -> numFields t1 + numFields t2
103102 Var _ -> 0
104103 Data _ ts -> sum (map numFields ts)
105− EmptyRecord -> 0
106− Record fields ext -> length fields + sum (map (numFields . snd) fields) + numFields ext
104+ Record fields _ -> length fields + sum (map (numFields . snd) fields)
107105
108106diff :: Type -> Type -> Maybe [(String,Type)]
109107diff general specific =
@@ -113,11 +111,11 @@ diff general specific =
113111 (Data gname gts, Data sname sts)
114112 | gname == sname && length gts == length sts ->
115113 concat <$> zipWithM diff gts sts
116− (EmptyRecord, EmptyRecord) -> Just []
117− (Record _ _, Record _ _) ->
118− let (gfs,gext) = collectRecords general
119− (sfs,sext) = collectRecords specific
120− gfields = collectFields gfs
114+ (Record [] Nothing, Record [] Nothing) -> Just []
115+ (Record _ _, Record [] Nothing) -> Nothing
116+ (Record [] Nothing, Record _ _) -> Nothing
117+ (Record gfs gext, Record sfs sext) ->
118+ let gfields = collectFields gfs
121119 sfields = collectFields sfs
122120
123121 overlap = Map.intersectionWith (\gs ss -> length gs == length ss) sfields gfields
@@ -126,10 +124,12 @@ diff general specific =
126124 case isAligned of
127125 False -> Nothing
128126 True -> let remaining = Map.difference sfields gfields
129− sext' = if Map.null remaining then sext else
130− Record (flattenFields remaining) sext
127+ sext' = case sext of
128+ Just x | Map.null remaining -> Var x
129+ _ -> Record (flattenFields remaining) sext
130+ gext' = maybe (Record [] Nothing) Var gext
131131 matchMap = Map.intersectionWith (zipWith diff) gfields sfields
132− in concat <$> sequence (diff gext sext' : concat (Map.elems matchMap))
132+ in concat <$> sequence (diff gext' sext' : concat (Map.elems matchMap))
133133 (_,_) -> Nothing
134134
135135collectFields fields =
compiler/Type/Constrain/Declaration.hs+6−12
@@ -6,8 +6,6 @@ import qualified SourceSyntax.Expression as E
66import qualified SourceSyntax.Location as L
77import qualified SourceSyntax.Pattern as P
88import qualified SourceSyntax.Type as T
9−import qualified Type.Type as Type
10−import System.IO.Unsafe
119
1210toExpr :: [Declaration] -> [E.Def]
1311toExpr = concatMap toDefs
@@ -28,17 +26,15 @@ toDefs decl =
2826 TypeAlias name _ tipe@(T.Record fields ext) _ ->
2927 [ definition name (buildFunction record vars) (foldr T.Lambda tipe args) ]
3028 where
31− args = case ext of
32− T.EmptyRecord -> map snd fields
33− _ -> map snd fields ++ [ext]
29+ args = map snd fields ++ maybe [] (\x -> [T.Var x]) ext
3430
3531 var = L.none . E.Var
3632 vars = take (length args) arguments
3733
3834 efields = zip (map fst fields) (map var vars)
3935 record = case ext of
40− T.EmptyRecord -> L.none $ E.Record efields
41− _ -> foldl (\r (f,v) -> L.none $ E.Insert r f v) (var $ last vars) efields
36+ Nothing -> L.none $ E.Record efields
37+ Just _ -> foldl (\r (f,v) -> L.none $ E.Insert r f v) (var $ last vars) efields
4238
4339 -- Type aliases must be added to an extended equality dictionary,
4440 -- but they do not require any basic constraints.
@@ -47,12 +43,10 @@ toDefs decl =
4743
4844 Port port ->
4945 case port of
50− Send name expr@(L.L s _) tipe ->
46+ Out name expr@(L.L s _) tipe ->
5147 [ definition name (L.L s $ E.PortOut name tipe expr) tipe ]
52− Recv name expr@(L.L s _) tipe ->
53− unsafePerformIO $ do
54− tvar <- Type.var Type.Flexible
55− return $ [ definition name (L.L s $ E.PortIn name tipe tvar expr) tipe ]
48+ In name tipe ->
49+ [ definition name (L.none $ E.PortIn name tipe) tipe ]
5650
5751 -- no constraints are needed for fixity declarations
5852 Fixity _ _ _ -> []
compiler/Type/Constrain/Expression.hs+1−2
@@ -156,8 +156,7 @@ constrain env (L span expr) tipe =
156156 (clet [Scheme rqs fqs (clet [monoscheme header] c2) header ]
157157 (c1 /\ c))
158158
159− PortIn _ _ tt handler ->
160− constrain env handler (VarN tt)
159+ PortIn _ _ -> return true
161160
162161 PortOut _ _ signal ->
163162 constrain env signal tipe
compiler/Type/Environment.hs+6−4
@@ -162,7 +162,9 @@ instantiator env sourceType = go sourceType
162162 _ -> error $ "\nCould not find type constructor '" ++
163163 name ++ "' while checking types."
164164
165− Src.EmptyRecord -> return (TermN EmptyRecord1)
166−
167− Src.Record fields ext ->
168− TermN <$> (Record1 <$> Traverse.traverse (mapM go) (Src.fieldMap fields) <*> go ext)
165+ Src.Record fields ext -> do
166+ fields' <- Traverse.traverse (mapM go) (Src.fieldMap fields)
167+ ext' <- case ext of
168+ Nothing -> return $ TermN EmptyRecord1
169+ Just x -> go (Src.Var x)
170+ return $ TermN (Record1 fields' ext')
compiler/Type/ExtraChecks.hs+59−60
@@ -14,12 +14,12 @@ import qualified Type.State as TS
1414import qualified Type.Alias as Alias
1515import Text.PrettyPrint as P
1616import SourceSyntax.PrettyPrint (pretty)
17+import qualified SourceSyntax.Helpers as Help
1718import qualified SourceSyntax.Type as T
1819import qualified SourceSyntax.Expression as E
1920import qualified SourceSyntax.Location as L
2021import qualified Transform.Expression as Expr
2122import qualified Data.Traversable as Traverse
22−import System.IO.Unsafe
2323
2424throw err = Left [ P.vcat err ]
2525
@@ -43,70 +43,69 @@ mainType rules env = mainCheck rules <$> Traverse.traverse toSrcType env
4343 , P.nest 4 . pretty $ Alias.realias rules mainType
4444 , P.text " " ]
4545
46+data Direction = In | Out
47+
4648portTypes :: Alias.Rules -> E.LExpr -> Either [P.Doc] ()
4749portTypes rules expr =
48− const () <$> Expr.checkPorts checkIn checkOut expr
50+ const () <$> Expr.checkPorts (check In) (check Out) expr
4951 where
50− showType tipe = (P.nest 4 . pretty $ Alias.realias rules tipe) <> P.text "\n"
51−
52− checkIn name st tt =
53− do t <- getSignal name st
54− case unsafePerformIO (toSrcType tt) of
55− T.Lambda a b ->
56− do isJsType "coming in" name a
57− if b `elem` okayTypes then return () else throw msg
58− where
59− okayTypes = [ t
60− , T.Data "Maybe.Maybe" [t]
61− , T.Data "Either.Either" [T.Data "String.String" [], t]
62− ]
63− msg = [ P.text $ "Type Error: the handler for port '" ++ name ++
64− "' returns values of type:\n"
65− , showType b
66− , P.text $ "but the port's type annotation requires that the handler returns values"
67− , P.text $ "with one of the following types:\n"
68− ] ++ map showType okayTypes ++
69− [ P.text "If the handler returns a Maybe or Either, invalid values will cause an"
70− , P.text "error to be thrown to the JS error handler associated with this port." ]
71−
72− tipe ->
73− throw [ P.text $ "Type Error: the handler for port '" ++ name ++
74− "' must be a function!"
75− , P.text "Instead it was given a value of type:\n"
76− , showType tipe
77− ]
78−
79− checkOut name st =
80− do t <- getSignal name st
81− isJsType "sent out" name t
82−
83− getSignal name tipe =
84− case tipe of
85− T.Data "Signal.Signal" [t] -> return t
86− _ -> throw [ P.text $ "Type Error: port '" ++ name ++ "' must be a Signal,"
87− , P.text $ "but the type annotation says it has non-Signal type:\n"
88− , showType tipe
89− , P.text $ "Maybe instead you want the following type?\n"
90− , showType (T.Data "Signal.Signal" [tipe])
91− ]
92−
93− isJsType msg name tipe =
52+ check = isValid False False
53+ isValid seenFunc seenSignal direction name tipe =
54+ let valid = isValid seenFunc seenSignal direction name in
9455 case tipe of
95− T.Data ctor ts | okay ctor -> mapM_ (isJsType msg name) ts
96− | otherwise -> throw $ err msg "Elm values"
97− T.Var _ -> throw $ err msg "type variables"
98− T.Lambda _ _ -> throw $ err msg "Elm functions"
99− T.EmptyRecord -> throw $ err msg "Elm records"
100− T.Record _ _ -> throw $ err msg "Elm records"
56+ T.Data ctor ts
57+ | isJs ctor || isElm ctor -> mapM_ valid ts
58+ | ctor == "Signal.Signal" -> handleSignal ts
59+ | otherwise -> err' True "an unsupported type"
60+
61+ T.Var _ -> err "free type variables"
62+
63+ T.Lambda _ _ ->
64+ case direction of
65+ In -> err "functions"
66+ Out | seenFunc -> err "higher-order functions"
67+ | seenSignal -> err "signals that contain functions"
68+ | otherwise ->
69+ mapM_ (isValid True seenSignal direction name) (T.collectLambdas tipe)
70+
71+ T.Record _ (Just _) -> err "extended records with free type variables"
72+
73+ T.Record fields Nothing ->
74+ mapM_ (\(k,v) -> (,) k <$> valid v) fields
75+
10176 where
102− okay ctor = and [ List.isPrefixOf "JavaScript." ctor
103− , 1 == length (filter (=='.') ctor) ]
104−
105− err msg kind =
106− [ P.text $ "Type Error: the values " ++ msg ++ " through port '" ++ name ++
107− "' must be JavaScript values."
108− , P.text $ "The values sent through this port contain " ++ kind ++ " with type:\n"
109− , showType tipe
77+ isJs ctor =
78+ List.isPrefixOf "JavaScript." ctor
79+ && length (filter (=='.') ctor) == 1
80+
81+ isElm ctor =
82+ ctor `elem` ["Int","Float","String","Bool","Maybe.Maybe","_List"]
83+ || Help.isTuple ctor
84+
85+ handleSignal ts
86+ | seenFunc = err "functions that involve signals"
87+ | seenSignal = err "signals-of-signals"
88+ | otherwise = mapM_ (isValid seenFunc True direction name) ts
89+
90+ dir inMsg outMsg = case direction of { In -> inMsg ; Out -> outMsg }
91+ txt = P.text . concat
92+
93+ err = err' False
94+ err' couldBeAlias kind =
95+ throw $
96+ [ txt [ "Type Error: the value ", dir "coming in" "sent out"
97+ , " through port '", name, "' is invalid." ]
98+ , txt [ "It contains ", kind, ":\n" ]
99+ , (P.nest 4 . pretty $ Alias.realias rules tipe) <> P.text "\n"
100+ , txt [ "Acceptable values for ", dir "incoming" "outgoing"
101+ , " ports include JavaScript values and" ]
102+ , txt [ "the following Elm values: Ints, Floats, Bools, Strings, Maybes," ]
103+ , txt [ "Lists, Tuples, ", dir "" "first-order functions, ", "and concrete records." ]
104+ ] ++ if couldBeAlias then aliasWarning else []
105+
106+ aliasWarning =
107+ [ txt [ "\nType aliases are not expanded for this check (yet) so you need to do that" ]
108+ , txt [ "manually for now (e.g. {x:Int,y:Int} instead of a type alias of that type)." ]
110109 ]
111110
112111occurs :: (String, Variable) -> StateT TS.SolverState IO ()
compiler/Type/Type.hs+9−5
@@ -364,11 +364,15 @@ toSrcType variable = do
364364 return (Src.Data name (ts ++ [b']))
365365 Fun1 a b -> Src.Lambda <$> toSrcType a <*> toSrcType b
366366 Var1 a -> toSrcType a
367− EmptyRecord1 -> return Src.EmptyRecord
368− Record1 fs ext -> do
369− fs' <- traverse (mapM toSrcType) fs
370− let fs'' = concat [ map ((,) name) ts | (name,ts) <- Map.toList fs' ]
371− Src.Record fs'' <$> toSrcType ext
367+ EmptyRecord1 -> return $ Src.Record [] Nothing
368+ Record1 tfields extension -> do
369+ fields' <- traverse (mapM toSrcType) tfields
370+ let fields = concat [ map ((,) name) ts | (name,ts) <- Map.toList fields' ]
371+ ext' <- toSrcType extension
372+ return $ case ext' of
373+ Src.Record fs ext -> Src.Record (fs ++ fields) ext
374+ Src.Var x -> Src.Record fields (Just x)
375+ _ -> error "Used toSrcType on a type that is not well-formed"
372376 Nothing ->
373377 case name desc of
374378 Just x@(c:_) | Char.isLower c -> return (Src.Var x)
libraries/Dict.elm+82−88
@@ -34,15 +34,35 @@ Insert, remove, and query operations all take *O(log n)* time.
3434import open Basics
3535import open Maybe
3636import Native.Error
37−import List as List
37+import List
38+import String
3839import Native.Utils
3940
4041-- BBlack and NBlack should only be used during the deletion
4142-- algorithm. Any other occurrence is a bug and should fail an assert.
42−data NColor = Red | Black
43− | BBlack | NBlack
44−
45−data LeafColor = LBlack | LBBlack
43+data NColor = Red
44+ | Black
45+ -- ^ Double Black, counts as 2 blacks for the invariant
46+ | BBlack
47+ -- ^ Negative Black, counts as -1 blacks for the invariant
48+ | NBlack
49+
50+
51+showNColor : NColor -> String
52+showNColor c = case c of
53+ Red -> "Red"
54+ Black -> "Black"
55+ BBlack -> "BBlack"
56+ NBlack -> "NBlack"
57+
58+data LeafColor = LBlack
59+ -- ^ Double Black, counts as 2
60+ | LBBlack
61+
62+showLColor : LeafColor -> String
63+showLColor c = case c of
64+ LBlack -> "LBlack"
65+ LBBlack -> "LBBlack"
4666
4767data Dict k v = RBNode NColor k v (Dict k v) (Dict k v)
4868 | RBEmpty LeafColor
@@ -93,51 +113,6 @@ member : comparable -> Dict comparable v -> Bool
93113-- Does t contain k?
94114member k t = isJust <| lookup k t
95115
96−rotateLeft : Dict k v -> Dict k v
97−rotateLeft t =
98− case t of
99− RBNode cy ky vy a (RBNode cz kz vz b c) -> RBNode cy kz vz (RBNode Red ky vy a b) c
100− _ -> Native.Error.raise "rotateLeft of a node without enough children"
101−
102−-- rotateRight -- the reverse, and
103−-- makes Y have Z's color, and makes Z Red.
104−rotateRight : Dict k v -> Dict k v
105−rotateRight t =
106− case t of
107− RBNode cz kz vz (RBNode cy ky vy a b) c -> RBNode cz ky vy a (RBNode Red kz vz b c)
108− _ -> Native.Error.raise "rotateRight of a node without enough children"
109−
110−rotateLeftIfNeeded : Dict k v -> Dict k v
111−rotateLeftIfNeeded t =
112− case t of
113− RBNode _ _ _ _ (RBNode Red _ _ _ _) -> rotateLeft t
114− _ -> t
115−
116−rotateRightIfNeeded : Dict k v -> Dict k v
117−rotateRightIfNeeded t =
118− case t of
119− RBNode _ _ _ (RBNode Red _ _ (RBNode Red _ _ _ _) _) _ -> rotateRight t
120− _ -> t
121−
122−otherColor c = case c of { Red -> Black ; Black -> Red }
123−
124−color_flip : Dict k v -> Dict k v
125−color_flip t =
126− case t of
127− RBNode c1 bk bv (RBNode c2 ak av la ra) (RBNode c3 ck cv lc rc) ->
128− RBNode (otherColor c1) bk bv
129− (RBNode (otherColor c2) ak av la ra)
130− (RBNode (otherColor c3) ck cv lc rc)
131− _ -> Native.Error.raise "color_flip called on a Empty or Node with a Empty child"
132−
133−color_flipIfNeeded : Dict k v -> Dict k v
134−color_flipIfNeeded t =
135− case t of
136− RBNode _ _ _ (RBNode Red _ _ _ _) (RBNode Red _ _ _ _) -> color_flip t
137− _ -> t
138−
139−fixUp t = color_flipIfNeeded (rotateRightIfNeeded (rotateLeftIfNeeded t))
140−
141116ensureBlackRoot : Dict k v -> Dict k v
142117ensureBlackRoot t =
143118 case t of
@@ -158,6 +133,11 @@ remove k t = let u _ = Nothing in
158133 update k u t
159134
160135data Flag = Insert | Remove | Same
136+showFlag : Flag -> String
137+showFlag f = case f of
138+ Insert -> "Insert"
139+ Remove -> "Remove"
140+ Same -> "Same"
161141
162142{-| Update the value of a dictionary for a specific key with a given function. -}
163143update : comparable -> (Maybe v -> Maybe v) -> Dict comparable v -> Dict comparable v
@@ -168,76 +148,87 @@ update k u t =
168148 Just v -> (Insert, RBNode Red k v empty empty)
169149 RBNode c k' v l r -> case Native.Utils.compare k k' of
170150 EQ -> case u (Just v) of
171− Nothing -> (Remove, rem t)
151+ Nothing -> (Remove, rem c l r)
172152 Just v' -> (Same, RBNode c k' v' l r)
173153 LT -> let (fl, l') = up l in
174154 case fl of
175155 Same -> (Same, RBNode c k' v l' r)
176− Insert -> (Insert, fixUp <| RBNode c k' v l' r)
156+ Insert -> (Insert, balance c k' v l' r)
177157 Remove -> (Remove, bubble c k' v l' r)
178158 GT -> let (fl, r') = up r in
179159 case fl of
180160 Same -> (Same, RBNode c k' v l r')
181− Insert -> (Insert, fixUp <| RBNode c k' v l r')
161+ Insert -> (Insert, balance c k' v l r')
182162 Remove -> (Remove, bubble c k' v l r')
183163 (fl, t') = up t
184164 in case fl of
185165 Same -> t'
186166 Insert -> ensureBlackRoot t'
187167 Remove -> blacken t'
188−
168+
189169{-| Create a dictionary with one key-value pair. -}
190170singleton : comparable -> v -> Dict comparable v
191171singleton k v = insert k v (RBEmpty LBlack)
192172
193−{- Remove helpers: everything from here to remove should only be used
194− internally by remove as they would otherwise break rb-invariants -}
195−
196173isBBlack : Dict k v -> Bool
197174isBBlack t = case t of
198− RBNode c _ _ _ _ -> case c of
199− BBlack -> True
200− _ -> False
175+ RBNode BBlack _ _ _ _ -> True
201176 RBEmpty LBBlack -> True
202177 _ -> False
203178
204179moreBlack : NColor -> NColor
205180moreBlack c = case c of
206− BBlack -> BBlack
207181 Black -> BBlack
208182 Red -> Black
209183 NBlack -> Red
184+ BBlack -> Native.Error.raise "Can't make a double black node more black!"
185+
210186lessBlack : NColor -> NColor
211187lessBlack c = case c of
212188 BBlack -> Black
213189 Black -> Red
214190 Red -> NBlack
215− NBlack -> NBlack
216−
217−moreBlackTree : Dict k v -> Dict k v
218−moreBlackTree t = case t of
219− RBNode c k v l r -> RBNode (moreBlack c) k v l r
220− RBEmpty _ -> RBEmpty LBBlack
191+ NBlack -> Native.Error.raise "Can't make a negative black node less black!"
221192
222193lessBlackTree : Dict k v -> Dict k v
223194lessBlackTree t = case t of
224195 RBNode c k v l r -> RBNode (lessBlack c) k v l r
225− RBEmpty _ -> RBEmpty LBlack
196+ RBEmpty LBBlack -> RBEmpty LBlack
197+
198+reportRemBug : String -> NColor -> String -> String -> a
199+reportRemBug msg c lgot rgot =
200+ Native.Error.raise . String.concat <| [
201+ "Internal red-black tree invariant violated, expected ",
202+ msg,
203+ "and got",
204+ showNColor c,
205+ " ",
206+ lgot,
207+ " ",
208+ rgot,
209+ "\nPlease report this bug to https://github.com/evancz/Elm/issues"
210+ ]
226211
227212-- Remove the top node from the tree, may leave behind BBlacks
228−rem : Dict k v -> Dict k v
229−rem t = case t of
230− RBNode c k v (RBEmpty _) (RBEmpty _) -> case c of
231− Red -> RBEmpty LBlack
213+rem : NColor -> Dict k v -> Dict k v -> Dict k v
214+rem c l r = case (l, r) of
215+ ((RBEmpty _), (RBEmpty _)) -> case c of
216+ Red -> RBEmpty LBlack
232217 Black -> RBEmpty LBBlack
233− RBNode Black _ _ (RBEmpty _) (RBNode _ k v l r) ->
234− RBNode Black k v l r
235− RBNode Black _ _ (RBNode _ k v l r) (RBEmpty _) ->
236− RBNode Black k v l r
218+ ((RBEmpty cl), (RBNode cr k' v' l' r')) ->
219+ case (c, cl, cr) of
220+ (Black, LBlack, Red) -> RBNode Black k' v' l' r'
221+ _ -> reportRemBug "Black, LBlack, Red" c (showLColor cl) (showNColor cr)
222+ ((RBNode cl k' v' l' r'), (RBEmpty cr)) ->
223+ case (c, cl, cr) of
224+ (Black, Red, LBlack) -> RBNode Black k' v' l' r'
225+ _ -> reportRemBug "Black, Red, LBlack" c (showNColor cl) (showLColor cr)
237226 -- l and r are both RBNodes
238− RBNode c _ _ l r ->
239− let (k, v) = max l
240− l' = remove_max l
227+ ((RBNode cl kl vl ll rl), (RBNode cr kr vr lr rr)) ->
228+ let l = RBNode cl kl vl ll rl
229+ r = RBNode cr kr vr lr rr
230+ (k, v) = max l
231+ l' = remove_max cl kl vl ll rl
241232 in bubble c k v l' r
242233
243234-- Kills a BBlack or moves it upward, may leave behind NBlack
@@ -245,19 +236,24 @@ bubble : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
245236bubble c k v l r = if isBBlack l || isBBlack r
246237 then balance (moreBlack c) k v (lessBlackTree l) (lessBlackTree r)
247238 else RBNode c k v l r
248−
239+
249240-- Removes rightmost node, may leave root as BBlack
250−remove_max : Dict k v -> Dict k v
251−remove_max t = case t of
252− RBNode c k v l (RBEmpty _) -> rem t
253− RBNode c k v l r -> bubble c k v l (remove_max r)
241+remove_max : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
242+remove_max c k v l r = case r of
243+ RBEmpty _ -> rem c l r
244+ RBNode cr kr vr lr rr
245+ -> bubble c k v l (remove_max cr kr vr lr rr)
254246
255247-- generalized tree balancing act
256248balance : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
257−balance c k v l r = balance_node (RBNode c k v l r)
249+balance c k v l r =
250+
251+ balance_node (RBNode c k v l r)
258252
259253blackish : Dict k v -> Bool
260−blackish (RBNode c _ _ _ _) = c == Black || c == BBlack
254+blackish t = case t of
255+ RBNode c _ _ _ _ -> c == Black || c == BBlack
256+ RBEmpty _ -> True
261257
262258balance_node : Dict k v -> Dict k v
263259balance_node t =
@@ -286,9 +282,7 @@ balance_node t =
286282 (RBNode Black _ _ _ _) ->
287283 RBNode Black yk yv (balance Black xk xv (redden a) b) (RBNode Black zk zv c d)
288284 _ -> t
289−
290285 _ -> t
291−
292286 else t
293287
294288-- make the top node black
libraries/Native/JavaScript.js+1−1
@@ -34,7 +34,7 @@ Elm.Native.JavaScript.make = function(elm) {
3434
3535 function toJS(v) {
3636 var type = typeof v;
37− if (type === 'number' || type === 'boolean') return v;
37+ if (type === 'number' || type === 'boolean' || type === 'string') return v;
3838 if (type === 'object' && '_' in v) {
3939 var obj = {};
4040 for (var k in v) {
libraries/Native/Ports.js+46−42
@@ -6,62 +6,66 @@ Elm.Native.Ports.make = function(elm) {
66
77 var Signal = Elm.Signal.make(elm);
88
9− // On failure, return message. On success, return the value in an array.
10− // Wrapping in an array allows the programmer to pass in a null value.
11− function processInput(converter, v) {
12− try { var elmValue = converter(v); }
13− catch(e) { return "The given value caused a runtime error!\n" + e.toString(); }
9+ function incomingSignal(converter) {
10+ return function(port) {
11+ var base = converter(port.internal.defaultValue);
12+ var signal = Signal.constant(base);
13+ port.internal.subscribe(function(v) {
14+ try {
15+ elm.notify(signal.id, converter(v));
16+ } catch(e) {
17+ port.internal.errorHandler(v);
18+ }
19+ });
20+ return signal;
21+ }
22+ }
1423
15− var ctor = elmValue.ctor;
16− if (ctor === 'Nothing' || ctor === 'Left') {
17− return "The port's conversion function failed.";
18− } else if (ctor === 'Just' || ctor === 'Right') {
19− return [elmValue._0];
24+ function outgoingSignal(converter) {
25+ return function(signal) {
26+ var subscribers = []
27+ function subscribe(handler) {
28+ subscribers.push(handler);
29+ }
30+ function unsubscribe(handler) {
31+ subscribers.pop(subscribers.indexOf(handler));
32+ }
33+ A2( Signal.lift, function(value) {
34+ var val = converter(value);
35+ var len = subscribers.length;
36+ for (var i = 0; i < len; ++i) {
37+ subscribers[i](val);
38+ }
39+ }, signal);
40+ return { subscribe:subscribe, unsubscribe:unsubscribe };
2041 }
21− return [elmValue];
2242 }
43+
2344 function portIn(name, converter) {
24− var port = elm.ports.incoming[name];
25− if (!port) {
45+ var value = elm.ports.incoming[name];
46+ if (!value) {
2647 throw new Error("Initialization Error: port '" + name +
2748 "' was not given an input!");
2849 }
2950 elm.ports.uses[name] += 1;
30− var result = processInput(converter, port.internal.defaultValue);
31− if (typeof result === 'string') {
32− throw new Error("Initialization Error: The default value for port '" +
33− name + "' is invalid.\n" + result);
51+ try {
52+ return converter(value);
53+ } catch(e) {
54+ throw new Error("Initialization Error on port '" + name + "': \n" + e.message);
3455 }
35− var signal = Signal.constant(result[0]);
36− port.internal.subscribe(function(v) {
37− var result = processInput(converter, v);
38− if (typeof result === 'string') {
39− port.internal.errorHandler(v)
40− } else {
41− elm.notify(signal.id, result[0]);
42− }
43− });
44− return signal;
4556 }
46− function portOut(name, signal) {
47− var subscribers = []
48− function subscribe(handler) {
49− subscribers.push(handler);
57+ function portOut(name, converter, value) {
58+ try {
59+ elm.ports.outgoing[name] = converter(value);
60+ } catch(e) {
61+ throw new Error("Initialization Error on port '" + name + "': \n" + e.message);
5062 }
51− function unsubscribe(handler) {
52− subscribers.pop(subscribers.indexOf(handler));
53− }
54− A2( Signal.lift, function(value) {
55− var len = subscribers.length;
56− for (var i = 0; i < len; ++i) {
57− subscribers[i](value);
58− }
59− }, signal);
60− elm.ports.outgoing[name] = { subscribe:subscribe, unsubscribe:unsubscribe };
61− return signal;
63+ return value;
6264 }
6365
6466 return elm.Native.Ports.values = {
67+ incomingSignal: incomingSignal,
68+ outgoingSignal: outgoingSignal,
6569 portOut: portOut,
6670 portIn: portIn
6771 };
libraries/elm_dependencies.json+2
@@ -16,6 +16,7 @@
1616 , "Graphics.Element"
1717 , "Graphics.Collage"
1818 , "Graphics.Input"
19+ , "Graphics.WebGL"
1920 , "Http"
2021 , "JavaScript"
2122 , "JavaScript.Experimental"
@@ -23,6 +24,7 @@
2324 , "Keyboard"
2425 , "List"
2526 , "Maybe"
27+ , "MJS"
2628 , "Mouse"
2729 , "Random"
2830 , "Regex"
runtime/Init.js+34−7
@@ -27,7 +27,7 @@ Elm.worker = function(module, ports) {
2727 return init(ElmRuntime.Display.NONE, {}, module, ports || {});
2828};
2929
30−Elm.input = function(defaultValue, errorHandler) {
30+Elm.signal = function(defaultValue, errorHandler) {
3131 var subscribers = []
3232 function subscribe(handler) {
3333 subscribers.push(handler);
@@ -133,7 +133,7 @@ function init(display, container, module, ports, moduleToReplace) {
133133 }
134134
135135 reportAnyErrors();
136− return { swap:swap, output:elm.ports.outgoing };
136+ return { swap:swap, ports:elm.ports.outgoing };
137137};
138138
139139function checkPorts(elm) {
@@ -142,8 +142,9 @@ function checkPorts(elm) {
142142 var uses = portUses[key]
143143 if (uses === 0) {
144144 throw new Error(
145− "Initialization Error: there is no port named '" + key + "'.\n" +
146− "You should probably remove that input from your initialization code.");
145+ "Initialization Error: provided port '" + key +
146+ "' to a module that does not take it as in input.\n" +
147+ "Remove '" + key + "' from the module initialization code.");
147148 } else if (uses > 1) {
148149 throw new Error(
149150 "Initialization Error: port '" + key +
@@ -179,23 +180,49 @@ function addReceivers(ports) {
179180 ports.log.subscribe(function(v) { console.log(v) });
180181 }
181182 if ('stdout' in ports) {
182− var handler = process ? function(v) { process.stdout.write(v); }
183+ var process = process || {};
184+ var handler = process.stdout
185+ ? function(v) { process.stdout.write(v); }
183186 : function(v) { console.log(v); };
184187 ports.stdout.subscribe(handler);
185188 }
186189 if ('stderr' in ports) {
187− var handler = process ? function(v) { process.stderr.write(v); }
190+ var process = process || {};
191+ var handler = process.stderr
192+ ? function(v) { process.stderr.write(v); }
188193 : function(v) { console.log('Error:' + v); };
189194 ports.stderr.subscribe(handler);
190195 }
191196 if ('title' in ports) {
192− ports.title.subscribe(function(v) { document.title = v; });
197+ if (typeof ports.title === 'string') {
198+ document.title = ports.title;
199+ } else {
200+ ports.title.subscribe(function(v) { document.title = v; });
201+ }
193202 }
194203 if ('redirect' in ports) {
195204 ports.redirect.subscribe(function(v) {
196205 if (v.length > 0) window.location = v;
197206 });
198207 }
208+ if ('favicon' in ports) {
209+ if (typeof ports.favicon === 'string') {
210+ changeFavicon(ports.favicon);
211+ } else {
212+ ports.favicon.subscribe(changeFavicon);
213+ }
214+ }
215+ function changeFavicon(src) {
216+ var link = document.createElement('link');
217+ var oldLink = document.getElementById('elm-favicon');
218+ link.id = 'elm-favicon';
219+ link.rel = 'shortcut icon';
220+ link.href = src;
221+ if (oldLink) {
222+ document.head.removeChild(oldLink);
223+ }
224+ document.head.appendChild(link);
225+ }
199226}
200227
201228function initGraphics(elm, Module) {
tests/test-files/good/Box.elm+2
@@ -1,4 +1,6 @@
11
2+module Box where
3+
24import MJS (V3,M4x4,v3,m4x4makeRotate,m4x4makePerspective,m4x4makeLookAt,m4x4mul)
35import Graphics.WebGL (Program, link, Triangle, zipTriangle, Buffer, bind, Model, encapsulate, webgl)
46import Window(dimensions)