@@ -3,7 +3,7 @@ module Generate.JavaScript (showErr, jsModule) where
3import Control.Arrow (first,second)
3import Control.Arrow (first,second)
4import Control.Monad (liftM,(<=<),join,ap)
4import Control.Monad (liftM,(<=<),join,ap)
5import Data.Char (isAlpha,isDigit)
5import Data.Char (isAlpha,isDigit)
6−import Data.List (intercalate,sortBy,inits,foldl')
6+import Data.List (intercalate,inits,foldl')
7import qualified Data.Map as Map
7import qualified Data.Map as Map
8import qualified Data.Set as Set
8import qualified Data.Set as Set
9import Data.Either (partitionEithers)
9import Data.Either (partitionEithers)
@@ -13,7 +13,7 @@ import Unique
13import Generate.Cases
13import Generate.Cases
14import SourceSyntax.Everything hiding (parens)
14import SourceSyntax.Everything hiding (parens)
15import SourceSyntax.Location as Loc
15import SourceSyntax.Location as Loc
16−import Transform.SortDefinitions
16+import qualified Transform.SortDefinitions as SD
18showErr :: String -> String
18showErr :: String -> String
19showErr err = globalAssign "Elm.Main" (jsFunc "elm" body)
19showErr err = globalAssign "Elm.Main" (jsFunc "elm" body)
@@ -52,49 +52,50 @@ quoted s = "'" ++ concatMap f s ++ "'"
52globalAssign n e = "\n" ++ assign' n e ++ ";"
52globalAssign n e = "\n" ++ assign' n e ++ ";"
53assign' n e = n ++ " = " ++ e
53assign' n e = n ++ " = " ++ e
55−jsModule (Module names exports imports stmts) =
55+jsModule :: MetadataModule t v -> String
58+ body <- toJS (defs modul)
59+ foreignImport <- mapM importEvent (foreignImports modul)
60+ return $ concat [ setup ("Elm": names modul)
56− setup ("Elm":modNames) ++ globalAssign ("Elm." ++ modName) (jsFunc "elm" program)
61+ , globalAssign ("Elm." ++ modName)
62+ (jsFunc "elm" $ program body foreignImport) ]
58− modNames = if null names then ["Main"] else names
59− modName = intercalate "." modNames
64+ modName = intercalate "." (names modul)
60− includes = concatMap jsImport imports
65+ program body foreignImport =
61− body = stmtsToJS stmts
62− defs = assign "$op" "{}"
63− program = "\nvar " ++ usefulFuncs ++ ";" ++ defs ++ includes ++ body ++
66+ concat [ "\nvar " ++ usefulFuncs ++ ";"
68+ , concatMap jsImport (imports modul)
69+ , concat foreignImport
70+ , concatMap exportEvent $ foreignExports modul
64− setup ("elm":"Native":modNames) ++
72+ , setup ("elm" : "Native" : names modul)
65− assign "_" ("elm.Native." ++ modName ++ "||{}") ++
73+ , assign "_" ("elm.Native." ++ modName ++ "||{}")
66− getExports exports stmts ++ setup ("elm":modNames) ++
74+ , concatMap jsExport (exports modul)
75+ , setup ("elm" : names modul)
67− ret (assign' ("elm." ++ modName) "_") ++ "\n"
76+ , ret (assign' ("elm." ++ modName) "_")
68− setup modNames = concatMap (\n -> globalAssign n $ n ++ "||{}") .
78+ setup names = concatMap (\n -> globalAssign n $ n ++ "||{}") .
69− map (intercalate ".") . drop 2 . inits $ init modNames
79+ map (intercalate ".") . drop 2 . inits $ init names
70 usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
80 usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
72−getExports names stmts = "\n"++ intercalate ";\n" (op : map fnPair fns)
82+ jsExport x =
73− where exNames n = either derename id n `elem` names
83+ if isOp x then "\n_._op['" ++ x ++ "'] = _op['" ++ x ++ "'];"
74− exports | null names = concatMap get stmts
84+ else "\n_." ++ derename x ++ " = " ++ x ++ ";"
75− | otherwise = filter exNames (concatMap get stmts)
85+
76−
86+ importEvent (js,base,elm,_) =
77− (fns,ops) = partitionEithers exports
87+ do v <- toJS' base
78−
88+ return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal(elm).constant(" ++ v ++ ");"
79− opPair op = "'" ++ op ++ "' : $op['" ++ op ++ "']"
89+ , "\ndocument.addEventListener('", js
80− fnPair fn = let fn' = derename fn in "_." ++ fn' ++ " = " ++ fn
90+ , "_' + elm.id, function(e) { elm.notify(", elm
81−
91+ , ".id, e.value); });" ]
82− op = ("_.$op = "++) . jsObj $ map opPair ops
86− TypeAnnotation _ _ -> []
88− case Set.toList (boundVars pattern) of
89− [var@(c:_)] -> [if isOp c then Right var else Left var]
90− vars -> map Left vars
92− get s = case s of Definition def -> get' def
93− Datatype _ _ tcs -> map (Left . fst) tcs
94− ImportEvent _ _ x _ -> [ Left x ]
95− ExportEvent _ _ _ -> []
96− TypeAlias _ _ _ -> []
93+ exportEvent (js,elm,_) =
94+ concat [ "\nlift(function(v) { "
95+ , "var e = document.createEvent('Event');"
96+ , "e.initEvent('", js, "_' + elm.id, true, true);"
98+ , "document.dispatchEvent(e); return v; })(", elm, ");" ]
99jsImport (modul, how) =
100jsImport (modul, how) =
100 case how of
101 case how of
@@ -105,7 +106,7 @@ jsImport (modul, how) =
105 Importing vs -> include ++ named
106 Importing vs -> include ++ named
107 imprt v = assign' v ("_." ++ v)
108 imprt v = assign' v ("_." ++ v)
108− def (o:p) = imprt (if isOp o then "$op['" ++ o:p ++ "']" else deprime (o:p))
109+ def x = imprt $ if isOp x then "_op['" ++ x ++ "']" else deprime x
109 named = if null vs then "" else "\nvar " ++ intercalate ", " (map def vs) ++ ";"
110 named = if null vs then "" else "\nvar " ++ intercalate ", " (map def vs) ++ ";"
111 include = "\nvar _ = Elm." ++ modul ++ parens "elm" ++ ";" ++ setup modul
112 include = "\nvar _ = Elm." ++ modul ++ parens "elm" ++ ";" ++ setup modul
@@ -122,69 +123,31 @@ jsImport (modul, how) =
122 [] -> (reverse name, [])
123 [] -> (reverse name, [])
126−stmtsToJS :: [Declaration t v] -> String
127−stmtsToJS stmts = run $ do program <- mapM toJS (sortBy cmpStmt stmts)
128− return (concat program)
130− cmpStmt s1 s2 = compare (valueOf s1) (valueOf s2)
131− valueOf s = case s of
133− ImportEvent _ _ _ _ -> 2
134− Definition (Def f (L _ _ (Lambda _ _))) -> 3
135− Definition (Def (PVar name) _) ->
136− if derename name == "main" then 5 else 4
137− Definition (Def _ _) -> 4
138− Definition (TypeAnnotation _ _) -> 0
139− ExportEvent _ _ _ -> 6
140− TypeAlias _ _ _ -> 0
142class ToJS a where
126class ToJS a where
143 toJS :: a -> Unique String
127 toJS :: a -> Unique String
145instance ToJS (Def t v) where
130instance ToJS (Def t v) where
147 -- TODO: Make this handle patterns besides plain variables
132 -- TODO: Make this handle patterns besides plain variables
133+ toJS (Def (PVar x) e)
134+ | isOp x = globalAssign ("_op['" ++ x ++ "']") `liftM` toJS' e
135+ | otherwise = assign x `liftM` toJS' e
137+ toJS (Def pattern e) =
139+ let x = "_" ++ show n
140+ var = Loc.none . Var
141+ toDef y = Def (PVar y) (Loc.none $ Case (var x) [(pattern, var y)])
142+ stmt <- assign x `liftM` toJS' e
143+ vars <- toJS . map toDef . Set.toList $ SD.boundVars pattern
144+ return (stmt ++ vars)
149− toJS (Def (PVar x) e) = assign x `liftM` toJS' e
151− toJS (Def f e) = (assign f . wrapper . func) `liftM` toJS' e
153− func body = jsFunc (intercalate ", " as) (ret body)
154− wrapper e | length as == 1 = e
155− | otherwise = 'F' : show (length as) ++ parens e
156− toJS (OpDef op a1 a2 e) =
158− let func = "F2" ++ parens (jsFunc (a1 ++ ", " ++ a2) (ret body))
159− return (globalAssign ("$op['" ++ op ++ "']") func)
161 toJS (TypeAnnotation _ _) = return ""
146 toJS (TypeAnnotation _ _) = return ""
164−instance ToJS (Declaration t v) where
149+instance ToJS a => ToJS [a] where
167− Definition d -> toJS d
168− Datatype _ _ tcs -> concat `liftM` mapM (toJS . toDef) tcs
150+ toJS xs = concat `liftM` mapM toJS xs
169− where toDef (name,args) =
170− let vars = map (('a':) . show) [1..length args]
171− body = Loc.none (Data (derename name) (map (Loc.none . Var) vars))
172− func = foldr (\x e -> Loc.none (Lambda (PVar x) e)) body vars
173− in Definition $ Def (PVar name) func
175− ImportEvent js base elm _ ->
177− return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal(elm).constant(" ++ v ++ ");"
178− , "\ndocument.addEventListener('", js
179− , "_' + elm.id, function(e) { elm.notify(", elm
180− , ".id, e.value); });" ]
181− ExportEvent js elm _ ->
182− return $ concat [ "\nlift(function(v) { "
183− , "var e = document.createEvent('Event');"
184− , "e.initEvent('", js, "_' + elm.id, true, true);"
186− , "document.dispatchEvent(e); return v; })(", elm, ");" ]
187− TypeAlias n _ t -> return ""
189toJS' :: LExpr t v -> Unique String
152toJS' :: LExpr t v -> Unique String
190toJS' (L txt span expr) =
153toJS' (L txt span expr) =
@@ -254,7 +217,7 @@ instance ToJS (Expr t v) where
254 _ -> (patterns, lexpr)
217 _ -> (patterns, lexpr)
256 App e1 e2 -> jsApp e1 e2
219 App e1 e2 -> jsApp e1 e2
257− Let defs e -> jsLet defs e
220+ Let defs e -> jsLet $ SD.flattenLets defs e
259 ExplicitList es ->
222 ExplicitList es ->
260 do es' <- mapM toJS' es
223 do es' <- mapM toJS' es
@@ -302,15 +265,9 @@ multiIfToJS span ps =
302 e' <- toJS' e
265 e' <- toJS' e
303 return (b' ++ " ? " ++ e')
266 return (b' ++ " ? " ++ e')
305−jsLet defs e' = do ds <- jsDefs defs
268+jsLet (defs,e') = do ds <- mapM toJS defs
306− e <- toJS' e'
269+ e <- toJS' e'
307− return $ jsFunc "" (concat ds ++ ret e) ++ "()"
270+ return $ jsFunc "" (concat ds ++ ret e) ++ "()"
309− jsDefs defs = mapM toJS (sortBy f defs)
310− f a b = compare (valueOf a) (valueOf b)
311− valueOf (Def _ (L _ _ (Lambda _ _))) = 1
312− valueOf (Def _ _) = 2
313− valueOf (TypeAnnotation _ _) = 0
315caseToJS span e ps = do
272caseToJS span e ps = do
316 match <- caseToMatch ps
273 match <- caseToMatch ps
@@ -379,5 +336,5 @@ binop (o:p) e1 e2
379 "<~" -> "A2(lift," ++ e1 ++ "," ++ e2 ++ ")"
336 "<~" -> "A2(lift," ++ e1 ++ "," ++ e2 ++ ")"
380 "~" -> "A3(lift2,F2(function(f,x){return f(x)}),"++e1++","++e2++")"
337 "~" -> "A3(lift2,F2(function(f,x){return f(x)}),"++e1++","++e2++")"
381 _ | elem (o:p) ops -> parens (e1 ++ (o:p) ++ e2)
338 _ | elem (o:p) ops -> parens (e1 ++ (o:p) ++ e2)
382− | otherwise -> concat [ "$op['", o:p, "']"
339+ | otherwise -> concat [ "_op['", o:p, "']"
383 , parens e1, parens e2 ]
340 , parens e1, parens e2 ]