@@ -1,5 +1,4 @@
1−{-# OPTIONS -Wall #-}
2−
1+{-# OPTIONS_GHC -W #-}
32module Generate.JavaScript (generate) where
43
54import Control.Arrow (first,(***))
@@ -13,11 +12,10 @@ import qualified Generate.Cases as Case
1312import qualified Generate.Markdown as MD
1413import qualified SourceSyntax.Helpers as Help
1514import SourceSyntax.Literal
16−import SourceSyntax.Pattern
15+import SourceSyntax.Pattern as Pattern
1716import SourceSyntax.Location
1817import SourceSyntax.Expression
1918import SourceSyntax.Module
20−import qualified Transform.SortDefinitions as SD
2119import Language.ECMAScript3.Syntax
2220import Language.ECMAScript3.PrettyPrint
2321import qualified Transform.SafeNames as MakeSafe
@@ -27,8 +25,8 @@ split = go []
2725 where
2826 go vars str =
2927 case break (=='.') str of
30− (x,'.':rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
31− | otherwise -> go (vars ++ [x]) rest
28+ (x,_:rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
29+ | otherwise -> go (vars ++ [x]) rest
3230 (x,[]) -> vars ++ [x]
3331
3432var name = Id () name
@@ -40,7 +38,11 @@ function args stmts = FuncExpr () Nothing (map var args) stmts
4038call = CallExpr ()
4139string = StringLit ()
4240
43−dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
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+
4446obj = dotSep . split
4547
4648varDecl :: String -> Expression () -> VarDecl ()
@@ -52,24 +54,24 @@ include alias moduleName =
5254
5355internalImports name =
5456 VarDeclStmt ()
55− [ varDecl "N" (obj "Elm.Native")
56− , include "_N" "N.Utils"
57− , include "_L" "N.List"
58− , include "_E" "N.Error"
59− , include "_J" "N.JavaScript"
57+ [ varDecl "_N" (obj "Elm.Native")
58+ , include "_U" "_N.Utils"
59+ , include "_L" "_N.List"
60+ , include "_E" "_N.Error"
61+ , include "_J" "_N.JavaScript"
6062 , varDecl "$moduleName" (string name)
6163 ]
6264
6365literal :: Literal -> Expression ()
6466literal lit =
6567 case lit of
66− Chr c -> obj "_N.chr" <| string [c]
68+ Chr c -> obj "_U.chr" <| string [c]
6769 Str s -> string s
6870 IntNum n -> IntLit () n
6971 FloatNum n -> NumLit () n
7072 Boolean b -> BoolLit () b
7173
72−expression :: LExpr () () -> State Int (Expression ())
74+expression :: LExpr -> State Int (Expression ())
7375expression (L span expr) =
7476 case expr of
7577 Var x -> return $ ref x
@@ -86,19 +88,19 @@ expression (L span expr) =
8688
8789 Remove e x ->
8890 do e' <- expression e
89− return $ obj "_N.remove" `call` [string x, e']
91+ return $ obj "_U.remove" `call` [string x, e']
9092
9193 Insert e x v ->
9294 do v' <- expression v
9395 e' <- expression e
94− return $ obj "_N.insert" `call` [string x, v', e']
96+ return $ obj "_U.insert" `call` [string x, v', e']
9597
9698 Modify e fs ->
9799 do e' <- expression e
98100 fs' <- forM fs $ \(f,v) -> do
99101 v' <- expression v
100102 return $ ArrayLit () [string f, v']
101− return $ obj "_N.replace" `call` [ArrayLit () fs', e']
103+ return $ obj "_U.replace" `call` [ArrayLit () fs', e']
102104
103105 Record fields ->
104106 do fields' <- forM fields $ \(f,e) -> do
@@ -150,7 +152,7 @@ expression (L span expr) =
150152 _ -> (func, args)
151153
152154 Let defs e ->
153− do let (defs',e') = SD.flattenLets defs e
155+ do let (defs',e') = flattenLets defs e
154156 stmts <- concat <$> mapM definition defs'
155157 exp <- expression e'
156158 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
@@ -194,60 +196,62 @@ expression (L span expr) =
194196 pad = "<div style=\"height:0;width:0;\"> </div>"
195197 md = pad ++ MD.toHtml doc ++ pad
196198
197− GLShader uid src ->
199+ GLShader _ src ->
198200 return . literal $ Str src
199201
202+ PortIn name _ _ handler ->
203+ do handler' <- expression handler
204+ return $ obj "Native.Ports.portIn" `call` [ string name, handler' ]
205+
206+ PortOut name _ signal ->
207+ do signal' <- expression signal
208+ return $ obj "Native.Ports.portOut" `call` [ string name, signal' ]
209+
210+definition :: Def -> State Int [Statement ()]
211+definition (Definition pattern expr@(L span _) _) = do
212+ expr' <- expression expr
213+ let assign x = varDecl x expr'
214+ case pattern of
215+ PVar x
216+ | Help.isOp x ->
217+ let op = LBracket () (ref "_op") (string x) in
218+ return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
219+ | otherwise ->
220+ return [ VarDeclStmt () [ assign x ] ]
221+
222+ PRecord fields ->
223+ let setField f = varDecl f (dotSep ["$",f]) in
224+ return [ VarDeclStmt () (assign "$" : map setField fields) ]
225+
226+ PData name patterns | vars /= Nothing ->
227+ return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
228+ where
229+ vars = getVars patterns
230+ getVars patterns =
231+ case patterns of
232+ PVar x : rest -> (x:) `fmap` getVars rest
233+ [] -> Just []
234+ _ -> Nothing
235+
236+ decl x n = varDecl x (dotSep ["$","_" ++ show n])
237+ setup vars
238+ | Help.isTuple name = assign "$" : vars
239+ | otherwise = assign "$raw" : safeAssign : vars
240+
241+ safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
242+ if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
243+ exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
244+
245+ _ ->
246+ do defs' <- concat <$> mapM toDef vars
247+ return (VarDeclStmt () [assign "$"] : defs')
248+ where
249+ vars = Set.toList $ Pattern.boundVars pattern
250+ mkVar = L span . Var
251+ toDef y = let expr = L span $ Case (mkVar "$") [(pattern, mkVar y)]
252+ in definition $ Definition (PVar y) expr Nothing
200253
201−definition :: Def () () -> State Int [Statement ()]
202−definition def =
203− case def of
204− TypeAnnotation _ _ -> return []
205−
206− Def pattern expr@(L span _) -> do
207− expr' <- expression expr
208− let assign x = varDecl x expr'
209− case pattern of
210− PVar x
211− | Help.isOp x ->
212− let op = LBracket () (ref "_op") (string x) in
213− return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
214− | otherwise ->
215− return [ VarDeclStmt () [ assign x ] ]
216−
217− PRecord fields ->
218− let setField f = varDecl f (dotSep ["$",f]) in
219− return [ VarDeclStmt () (assign "$" : map setField fields) ]
220−
221− PData name patterns | vars /= Nothing ->
222− case vars of
223− Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
224− where
225− vars = getVars patterns
226− getVars patterns =
227− case patterns of
228− PVar x : rest -> (x:) `fmap` getVars rest
229− [] -> Just []
230− _ -> Nothing
231−
232− decl x n = varDecl x (dotSep ["$","_" ++ show n])
233− setup vars
234− | Help.isTuple name = assign "$" : vars
235− | otherwise = assign "$raw" : safeAssign : vars
236−
237− safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
238− if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
239− exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
240−
241− _ ->
242− do defs' <- concat <$> mapM toDef vars
243− return (VarDeclStmt () [assign "$"] : defs')
244− where
245− vars = Set.toList $ SD.boundVars pattern
246− mkVar = L span . Var
247− toDef y = definition $
248− Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
249−
250−match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
254+match :: (Show a) => a -> Case.Match -> State Int [Statement ()]
251255match span mtch =
252256 case mtch of
253257 Case.Match name clauses mtch' ->
@@ -292,8 +296,12 @@ clause span variable (Case.Clause value vars mtch) =
292296 [] -> name
293297 is -> drop (last is + 1) name
294298
299+flattenLets defs lexpr@(L _ expr) =
300+ case expr of
301+ Let ds body -> flattenLets (defs ++ ds) body
302+ _ -> (defs, lexpr)
295303
296−generate :: MetadataModule () () -> String
304+generate :: MetadataModule -> String
297305generate unsafeModule =
298306 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
299307 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
@@ -301,17 +309,16 @@ generate unsafeModule =
301309 modul = MakeSafe.metadataModule unsafeModule
302310 thisModule = dotSep ("_elm" : names modul ++ ["values"])
303311 programStmts =
304− concat [ setup (Just "_elm") (names modul ++ ["values"])
305− , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
306− , [ internalImports (List.intercalate "." (names modul)) ]
307− , concatMap jsImport (imports modul)
308− , concatMap importEvent (foreignImports modul)
309− , [ assign ["_op"] (ObjectLit () []) ]
310− , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
311− , map exportEvent $ foreignExports modul
312− , [ jsExports ]
313− , [ ReturnStmt () (Just thisModule) ]
314− ]
312+ concat
313+ [ setup (Just "_elm") (names modul ++ ["values"])
314+ , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
315+ , [ internalImports (List.intercalate "." (names modul)) ]
316+ , concatMap jsImport . Set.toList . Set.fromList . map fst $ imports modul
317+ , [ assign ["_op"] (ObjectLit () []) ]
318+ , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
319+ , [ jsExports ]
320+ , [ ReturnStmt () (Just thisModule) ]
321+ ]
315322
316323 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
317324 where
@@ -324,7 +331,7 @@ generate unsafeModule =
324331 _ -> ExprStmt () $
325332 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
326333
327− jsImport (modul,_) = setup Nothing path ++ [ include ]
334+ jsImport modul = setup Nothing path ++ [ include ]
328335 where
329336 path = split modul
330337 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
@@ -336,32 +343,6 @@ generate unsafeModule =
336343 Nothing -> tail . init $ List.inits path
337344 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
338345
339− addId js = InfixExpr () OpAdd (string (js++"_")) (obj "_elm.id")
340−
341− importEvent (js,base,elm,_) =
342− [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
343− , ExprStmt () $
344− obj "document.addEventListener" `call`
345− [ addId js
346− , function ["_e"]
347− [ ExprStmt () $ obj "_elm.notify" `call` [dotSep [elm,"id"], obj "_e.value"] ]
348− ]
349− ]
350−
351− exportEvent (js,elm,_) =
352− ExprStmt () $
353− ref "A2" `call`
354− [ obj "Signal.lift"
355− , function ["_v"]
356− [ VarDeclStmt () [varDecl "_e" $ obj "document.createEvent" <| string "Event"]
357− , ExprStmt () $
358− obj "_e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
359− , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "_e") "value") (ref "_v")
360− , ExprStmt () $ obj "document.dispatchEvent" <| ref "_e"
361− , ReturnStmt () (Just $ ref "_v")
362− ]
363− , ref elm ]
364−
365346binop span op e1 e2 =
366347 case op of
367348 "Basics.." ->
@@ -388,9 +369,6 @@ binop span op e1 e2 =
388369 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
389370 _ -> es ++ [e]
390371
391− js1 = expression e1
392− js2 = expression e2
393−
394372 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
395373 | otherwise = dotSep parts
396374 where
@@ -414,8 +392,8 @@ binop span op e1 e2 =
414392 specialOps = concat
415393 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
416394 , specialOp "|>" $ flip (<|)
417− , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
418− , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
395+ , specialOp "==" $ \a b -> obj "_U.eq" `call` [a,b]
396+ , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
419397 , specialOp "<" $ cmp OpLT 0
420398 , specialOp ">" $ cmp OpGT 0
421399 , specialOp "<=" $ cmp OpLT 1
@@ -423,4 +401,4 @@ binop span op e1 e2 =
423401 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
424402 ]
425403
426− cmp op n a b = InfixExpr () op (obj "_N.cmp" `call` [a,b]) (IntLit () n)
404+ cmp op n a b = InfixExpr () op (obj "_U.cmp" `call` [a,b]) (IntLit () n)