@@ -3,16 +3,20 @@ 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 qualified Data.List as List
7import qualified Data.Map as Map
7import qualified Data.Map as Map
8+import qualified Data.Set as Set
8import Data.Either (partitionEithers)
9import Data.Either (partitionEithers)
9import qualified Text.Pandoc as Pan
10import qualified Text.Pandoc as Pan
10−import Data.Maybe (maybeToList)
12import Unique
12import Unique
13import Generate.Cases
13import Generate.Cases
14−import SourceSyntax.Everything hiding (parens)
14+import SourceSyntax.Everything
15−import Types.Types ( Type(RecordT) )
15+import SourceSyntax.Location
16+import qualified Transform.SortDefinitions as SD
18+deprime :: String -> String
19+deprime = map (\c -> if c == '\'' then '$' else c)
17showErr :: String -> String
21showErr :: String -> String
18showErr err = globalAssign "Elm.Main" (jsFunc "elm" body)
22showErr err = globalAssign "Elm.Main" (jsFunc "elm" body)
@@ -24,19 +28,22 @@ indent = concatMap f
24 where f '\n' = "\n "
28 where f '\n' = "\n "
27−internalImports =
31+internalImports name =
28 [ ("N" , "Elm.Native"),
32 [ ("N" , "Elm.Native"),
29 ("_N", "N.Utils(elm)"),
33 ("_N", "N.Utils(elm)"),
30 ("_L", "N.List(elm)"),
34 ("_L", "N.List(elm)"),
31 ("_E", "N.Error(elm)"),
35 ("_E", "N.Error(elm)"),
32 ("_J", "N.JavaScript(elm)"),
36 ("_J", "N.JavaScript(elm)"),
33− ("_str", "_J.toString")
37+ ("_str", "_J.toString"),
38+ ("$moduleName", quoted name)
36parens s = "(" ++ s ++ ")"
41parens s = "(" ++ s ++ ")"
37brackets s = "{" ++ s ++ "}"
42brackets s = "{" ++ s ++ "}"
43+commaSep = List.intercalate ", "
44+dotSep = List.intercalate "."
38−jsObj = brackets . intercalate ", "
45+jsObj = brackets . commaSep
39−jsList ss = "["++ intercalate "," ss ++"]"
46+jsList ss = "["++ List.intercalate "," ss ++"]"
40jsFunc args body = "function(" ++ args ++ "){" ++ indent body ++ "}"
47jsFunc args body = "function(" ++ args ++ "){" ++ indent body ++ "}"
41assign x e = "\nvar " ++ x ++ " = " ++ e ++ ";"
48assign x e = "\nvar " ++ x ++ " = " ++ e ++ ";"
42ret e = "\nreturn "++ e ++";"
49ret e = "\nreturn "++ e ++";"
@@ -51,148 +58,116 @@ quoted s = "'" ++ concatMap f s ++ "'"
51globalAssign n e = "\n" ++ assign' n e ++ ";"
58globalAssign n e = "\n" ++ assign' n e ++ ";"
52assign' n e = n ++ " = " ++ e
59assign' n e = n ++ " = " ++ e
54−jsModule (Module names exports imports stmts) =
61+jsModule :: MetadataModule t v -> String
64+ body <- toJS . fst . SD.flattenLets [] $ program modul
65+ foreignImport <- mapM importEvent (foreignImports modul)
66+ return $ concat [ setup ("Elm": names modul)
55− setup ("Elm":modNames) ++ globalAssign ("Elm." ++ modName) (jsFunc "elm" program)
67+ , globalAssign ("Elm." ++ modName)
56− where
68+ (jsFunc "elm" $ makeProgram body foreignImport) ]
57− modNames = if null names then ["Main"] else names
58− modName = intercalate "." modNames
59− includes = concatMap jsImport imports
60− body = stmtsToJS stmts
61− defs = assign "$op" "{}"
62− program = "\nvar " ++ usefulFuncs ++ ";" ++ defs ++ includes ++ body ++
63− setup ("elm":"Native":modNames) ++
64− assign "_" ("elm.Native." ++ modName ++ "||{}") ++
65− getExports exports stmts ++ setup ("elm":modNames) ++
66− ret (assign' ("elm." ++ modName) "_") ++ "\n"
67− setup modNames = concatMap (\n -> globalAssign n $ n ++ "||{}") .
68− map (intercalate ".") . drop 2 . inits $ init modNames
69− usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
71−getExports names stmts = "\n"++ intercalate ";\n" (op : map fnPair fns)
72− where exNames n = either derename id n `elem` names
73− exports | null names = concatMap get stmts
74− | otherwise = filter exNames (concatMap get stmts)
76− (fns,ops) = partitionEithers exports
78− opPair op = "'" ++ op ++ "' : $op['" ++ op ++ "']"
79− fnPair fn = let fn' = derename fn in "_." ++ fn' ++ " = " ++ fn
81− op = ("_.$op = "++) . jsObj $ map opPair ops
83− get' (FnDef x _ _) = Just (Left x)
84− get' (OpDef op _ _ _) = Just (Right op)
85− get' (TypeAnnotation _ _) = Nothing
86− get s = case s of Definition d -> maybeToList (get' d)
87− Datatype _ _ tcs -> map (Left . fst) tcs
88− ImportEvent _ _ x _ -> [ Left x ]
89− ExportEvent _ _ _ -> []
90− TypeAlias _ _ _ -> []
93−jsImport (modul, how) =
95− As name -> assign name ("Elm." ++ modul ++ parens "elm")
96− Hiding vs -> include ++ " var hiding=" ++ (jsObj $ map (++":1") vs) ++
97− "; for(var k in _){if(k in hiding)continue;" ++
98− "eval('var '+k+'=_[\"'+k+'\"]')}"
99− Importing vs -> include ++ named
101− imprt v = assign' v ("_." ++ v)
102− def (o:p) = imprt (if isOp o then "$op['" ++ o:p ++ "']" else deprime (o:p))
103− named = if null vs then "" else "\nvar " ++ intercalate ", " (map def vs) ++ ";"
105− include = "\nvar _ = Elm." ++ modul ++ parens "elm" ++ ";" ++ setup modul
70+ modName = dotSep (names modul)
106− setup moduleName = " var " ++ concatMap (++";") (defs ++ [assign' moduleName "_"])
71+ makeProgram body foreignImport =
72+ concat [ "\nvar " ++ usefulFuncs ++ ";"
73+ , concatMap jsImport (imports modul)
74+ , concat foreignImport
77+ , concatMap exportEvent $ foreignExports modul
80+ setup names = concatMap (\n -> globalAssign n $ n ++ " || {}") .
81+ map dotSep . drop 2 . List.inits $ init names
82+ usefulFuncs = commaSep (map (uncurry assign') (internalImports modName))
84+ jsExports = setup ("elm" : names modul) ++
85+ ret (assign' ("elm." ++ modName) (brackets exs))
108− defs = map (\n -> assign' n (n ++ "||{}")) (subnames moduleName)
87+ exs = indent . commaSep . concatMap (pair . deprime) $ "_op" : exports modul
89+ | otherwise = ["\n" ++ x ++ " : " ++ x]
92+ importEvent (js,base,elm,_) =
94+ return $ concat [ "\nvar " ++ elm ++ "=Signal.constant(" ++ v ++ ");"
95+ , "\ndocument.addEventListener('", js
96+ , "_' + elm.id, function(e) { elm.notify(", elm
97+ , ".id, e.value); });" ]
99+ exportEvent (js,elm,_) =
100+ concat [ "\nA2( Signal.lift, function(v) { "
101+ , "var e = document.createEvent('Event');"
102+ , "e.initEvent('", js, "_' + elm.id, true, true);"
104+ , "document.dispatchEvent(e); return v; }, ", elm, ");" ]
106+jsImport (modul, method) =
107+ concat $ zipWith3 (\s n v -> s ++ assign' n v ++ ";") starters subnames values
109+ starters = "\nvar " : repeat "\n"
110+ values = map (\name -> name ++ " || {}") (init subnames) ++
111+ ["Elm." ++ modul ++ parens "elm"]
109− subnames = map (intercalate ".") . tail . inits . init . split
112+ subnames = map dotSep . tail . List.inits $ split modul
110− split names = case go [] names of
114+ split names = case go [] names of
111− (name, []) -> [name]
115+ (name, []) -> [name]
112− (name, ns) -> name : split ns
116+ (name, ns) -> name : split ns
113− go name str = case str of
117+ go name str = case str of
114− '.':rest -> (reverse name, rest)
118+ '.':rest -> (reverse name, rest)
115− c:rest -> go (c:name) rest
119+ c:rest -> go (c:name) rest
116− [] -> (reverse name, [])
120+ [] -> (reverse name, [])
120−stmtsToJS :: [Declaration t v] -> String
121−stmtsToJS stmts = run $ do program <- mapM toJS (sortBy cmpStmt stmts)
122− return (concat program)
124− cmpStmt s1 s2 = compare (valueOf s1) (valueOf s2)
125− valueOf s = case s of
127− ImportEvent _ _ _ _ -> 2
128− Definition (FnDef f [] _) ->
129− if derename f == "main" then 5 else 4
130− Definition (TypeAnnotation _ _) -> 0
132− ExportEvent _ _ _ -> 6
133− TypeAlias _ _ _ -> 0
135class ToJS a where
123class ToJS a where
136 toJS :: a -> Unique String
124 toJS :: a -> Unique String
138instance ToJS (Def t v) where
127instance ToJS (Def t v) where
139− toJS (FnDef x [] e) = assign x `liftM` toJS' e
128+
140− toJS (FnDef f as e) = (assign f . wrapper . func) `liftM` toJS' e
129+ -- TODO: Make this handle patterns besides plain variables
141− where
130+ toJS (Def (PVar x) e)
142− func body = jsFunc (intercalate ", " as) (ret body)
131+ | isOp x = globalAssign ("_op['" ++ x ++ "']") `liftM` toJS' e
143− wrapper e | length as == 1 = e
132+ | otherwise = assign (deprime x) `liftM` toJS' e
144− | otherwise = 'F' : show (length as) ++ parens e
133+
145− toJS (OpDef op a1 a2 e) =
134+ toJS (Def pattern e@(L s _)) =
146− do body <- toJS' e
135+ do n <- guid
147− let func = "F2" ++ parens (jsFunc (a1 ++ ", " ++ a2) (ret body))
136+ let x = "_" ++ show n
148− return (globalAssign ("$op['" ++ op ++ "']") func)
137+ var = L s . Var
138+ toDef y' = let y = deprime y' in
139+ Def (PVar y) (L s $ Case (var x) [(pattern, var y)])
140+ stmt <- assign x `liftM` toJS' e
141+ vars <- toJS . map toDef . Set.toList $ SD.boundVars pattern
142+ return (stmt ++ vars)
149 toJS (TypeAnnotation _ _) = return ""
144 toJS (TypeAnnotation _ _) = return ""
152−instance ToJS (Declaration t v) where
147+instance ToJS a => ToJS [a] where
155− Definition d -> toJS d
156− Datatype _ _ tcs -> concat `liftM` mapM (toJS . toDef) tcs
148+ toJS xs = concat `liftM` mapM toJS xs
157− where toDef (name,args) =
158− let vars = map (('a':) . show) [1..length args] in
159− Definition . FnDef name vars . none $
160− Data (derename name) (map (none . Var) vars)
161− ImportEvent js base elm _ ->
163− return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal(elm).constant(" ++ v ++ ");"
164− , "\ndocument.addEventListener('", js
165− , "_' + elm.id, function(e) { elm.notify(", elm
166− , ".id, e.value); });" ]
167− ExportEvent js elm _ ->
168− return $ concat [ "\nlift(function(v) { "
169− , "var e = document.createEvent('Event');"
170− , "e.initEvent('", js, "_' + elm.id, true, true);"
172− , "document.dispatchEvent(e); return v; })(", elm, ");" ]
173− TypeAlias n _ t -> return ""
175toJS' :: LExpr t v -> Unique String
150toJS' :: LExpr t v -> Unique String
176−toJS' (L txt span expr) =
151+toJS' (L span expr) =
177 case expr of
152 case expr of
178 MultiIf ps -> multiIfToJS span ps
153 MultiIf ps -> multiIfToJS span ps
179 Case e cases -> caseToJS span e cases
154 Case e cases -> caseToJS span e cases
180 _ -> toJS expr
155 _ -> toJS expr
182−remove x e = "_N.remove('" ++ x ++ "', " ++ e ++ ")"
157+remove x e = "_N.remove('" ++ deprime x ++ "', " ++ e ++ ")"
183−addField x v e = "_N.insert('" ++ x ++ "', " ++ v ++ ", " ++ e ++ ")"
158+addField x v e = "_N.insert('" ++ deprime x ++ "', " ++ v ++ ", " ++ e ++ ")"
184setField fs e = "_N.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
159setField fs e = "_N.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
185− where f (x,v) = "['" ++ x ++ "'," ++ v ++ "]"
160+ where f (x,v) = "['" ++ deprime x ++ "'," ++ v ++ "]"
186−access x e = e ++ "." ++ x
161+access x e = e ++ "." ++ deprime x
187makeRecord kvs = record `liftM` collect kvs
162makeRecord kvs = record `liftM` collect kvs
189 combine r (k,v) = Map.insertWith (++) k v r
164 combine r (k,v) = Map.insertWith (++) k v r
190− collect = liftM (foldl' combine Map.empty) . mapM prep
165+ collect = liftM (List.foldl' combine Map.empty) . mapM prep
191− prep (k, as, e@(L t s _)) =
166+ prep (k, e) =
192− do v <- toJS' (foldr (\x e -> L t s $ Lambda x e) e as)
167+ do v <- toJS' e
193− return (k,[v])
168+ return (deprime k, [v])
194 fields fs =
169 fields fs =
195− brackets ("\n "++intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
170+ brackets ("\n "++List.intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
196 hidden = fields . map (second jsList) .
171 hidden = fields . map (second jsList) .
197 filter (not . null . snd) . Map.toList . Map.map tail
172 filter (not . null . snd) . Map.toList . Map.map tail
198 record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
173 record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
@@ -211,7 +186,7 @@ instance ToJS Literal where
211instance ToJS (Expr t v) where
186instance ToJS (Expr t v) where
212 toJS expr =
187 toJS expr =
213 case expr of
188 case expr of
214− Var x -> return x
189+ Var x -> return (deprime x)
215 Literal lit -> toJS lit
190 Literal lit -> toJS lit
216 Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
191 Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
217 Access e x -> access x `liftM` toJS' e
192 Access e x -> access x `liftM` toJS' e
@@ -220,12 +195,30 @@ instance ToJS (Expr t v) where
220 Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
195 Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
221 setField fs' `liftM` toJS' e
196 setField fs' `liftM` toJS' e
222 Record fs -> makeRecord fs
197 Record fs -> makeRecord fs
223− Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
198+ Binop op e1 e2 -> binop op e1 e2
200+ Lambda p e@(L s _) -> liftM (fastFunc . ret) (toJS' body)
203+ | length args < 2 || length args > 9 = foldr jsFunc body args
204+ | otherwise = "F" ++ show (length args) ++ parens (jsFunc (commaSep args) body)
205+ (args, body) = first reverse $ foldr depattern ([], innerBody) (zip patterns [1..])
225− Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
207+ depattern (pattern,n) (args, body) =
209+ PVar x -> (deprime x : args, body)
210+ _ -> let arg = "arg" ++ show n
211+ in (arg:args, L s (Case (L s (Var arg)) [(pattern, body)]))
213+ (patterns, innerBody) = collect [p] e
215+ collect patterns lexpr@(L _ expr) =
217+ Lambda p e -> collect (p:patterns) e
218+ _ -> (patterns, lexpr)
227 App e1 e2 -> jsApp e1 e2
220 App e1 e2 -> jsApp e1 e2
228− Let defs e -> jsLet defs e
221+ Let defs e -> jsLet $ SD.flattenLets defs e
230 ExplicitList es ->
223 ExplicitList es ->
231 do es' <- mapM toJS' es
224 do es' <- mapM toJS' es
@@ -239,7 +232,7 @@ instance ToJS (Expr t v) where
239 _ -> jsObj $ ("ctor:" ++ show name) : fields
232 _ -> jsObj $ ("ctor:" ++ show name) : fields
240 where fields = zipWith (\n e -> "_" ++ show n ++ ":" ++ e) [0..] fs
233 where fields = zipWith (\n e -> "_" ++ show n ++ ":" ++ e) [0..] fs
242− Markdown doc -> return $ "text('" ++ pad ++ md ++ pad ++ "')"
235+ Markdown doc -> return $ "Text.text('" ++ pad ++ md ++ pad ++ "')"
243 where pad = "<div style=\"height:0;width:0;\"> </div>"
236 where pad = "<div style=\"height:0;width:0;\"> </div>"
244 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
237 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
@@ -248,12 +241,12 @@ jsApp e1 e2 =
248 as <- mapM toJS' args
241 as <- mapM toJS' args
249 return $ case as of
242 return $ case as of
250 [a] -> f ++ parens a
243 [a] -> f ++ parens a
251− _ -> "A" ++ show (length as) ++ parens (intercalate ", " (f:as))
244+ _ -> "A" ++ show (length as) ++ parens (commaSep (f:as))
253 (func, args) = go [e2] e1
246 (func, args) = go [e2] e1
254 go args e =
247 go args e =
255 case e of
248 case e of
256− (L _ _ (App e1 e2)) -> go (e2 : args) e1
249+ (L _ (App e1 e2)) -> go (e2 : args) e1
257 _ -> (e, args)
250 _ -> (e, args)
259formatMarkdown = concatMap f
252formatMarkdown = concatMap f
@@ -264,8 +257,8 @@ formatMarkdown = concatMap f
265multiIfToJS span ps =
258multiIfToJS span ps =
266 case last ps of
259 case last ps of
267− (L _ _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
260+ (L _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
268− _ -> format ("_E.If" ++ parens (quoted (show span))) ps
261+ _ -> format ("_E.If" ++ parens ("$moduleName," ++ quoted (show span))) ps
270 format base ps =
263 format base ps =
271 foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
264 foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
@@ -273,26 +266,18 @@ multiIfToJS span ps =
273 e' <- toJS' e
266 e' <- toJS' e
274 return (b' ++ " ? " ++ e')
267 return (b' ++ " ? " ++ e')
276−jsLet defs e' = do ds <- jsDefs defs
269+jsLet (defs,e') = do ds <- mapM toJS defs
277− e <- toJS' e'
270+ e <- toJS' e'
278− return $ jsFunc "" (concat ds ++ ret e) ++ "()"
271+ return $ jsFunc "" (concat ds ++ ret e) ++ "()"
280− jsDefs defs = mapM toJS (sortBy f defs)
281− f a b = compare (valueOf a) (valueOf b)
282− valueOf (FnDef _ [] _) = 2
283− valueOf (FnDef _ _ _) = 1
284− valueOf (OpDef _ _ _ _) = 1
285− valueOf (TypeAnnotation _ _) = 0
287caseToJS span e ps = do
273caseToJS span e ps = do
288− match <- caseToMatch ps
274+ (tempVar,match) <- caseToMatch ps
289 e' <- toJS' e
275 e' <- toJS' e
290− let (match',stmt) = case (match,e) of
276+ let (match',stmt) = case e of
291− (Match name _ _, L _ _ (Var x)) -> (matchSubst [(name,x)] match, "")
277+ L _ (Var x) -> (matchSubst [(tempVar,deprime x)] match, "")
292− (Match name _ _, _) -> (match, assign name e')
278+ _ -> (match, assign tempVar e')
294 matches <- matchToJS span match'
279 matches <- matchToJS span match'
295− return $ "function(){ " ++ stmt ++ matches ++ " }()"
280+ return $ jsFunc "" (stmt ++ matches) ++ "()"
297matchToJS span match =
282matchToJS span match =
298 case match of
283 case match of
@@ -305,7 +290,7 @@ matchToJS span match =
305 access = if any isLiteral clauses then "" else ".ctor"
290 access = if any isLiteral clauses then "" else ".ctor"
306 return $ concat [ "\nswitch (", name, access, ") {"
291 return $ concat [ "\nswitch (", name, access, ") {"
307 , indent cases, "\n}", finally ]
292 , indent cases, "\n}", finally ]
308− Fail -> return ("_E.Case" ++ parens (quoted (show span)))
293+ Fail -> return ("_E.Case" ++ parens ("$moduleName," ++ quoted (show span)))
309 Break -> return "break;"
294 Break -> return "break;"
310 Other e -> ret `liftM` toJS' e
295 Other e -> ret `liftM` toJS' e
311 Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
296 Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
@@ -320,36 +305,64 @@ clauseToJS span var (Clause value vars e) = do
320 let vars' = map (\n -> var ++ "._" ++ show n) [0..]
305 let vars' = map (\n -> var ++ "._" ++ show n) [0..]
321 s <- matchToJS span $ matchSubst (zip vars vars') e
306 s <- matchToJS span $ matchSubst (zip vars vars') e
322 return $ concat [ "\ncase ", case value of
307 return $ concat [ "\ncase ", case value of
323− Left name -> quoted name
324 Right (Boolean True) -> "true"
308 Right (Boolean True) -> "true"
325 Right (Boolean False) -> "false"
309 Right (Boolean False) -> "false"
326 Right lit -> show lit
310 Right lit -> show lit
311+ Left name -> quoted $ case List.elemIndices '.' name of
313+ is -> drop (last is + 1) name
327 , ":", indent s ]
314 , ":", indent s ]
329jsNil = "_L.Nil"
316jsNil = "_L.Nil"
330jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
317jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
331jsRange e1 e2 = "_L.range" ++ parens (e1 ++ "," ++ e2)
318jsRange e1 e2 = "_L.range" ++ parens (e1 ++ "," ++ e2)
332−jsCompare e1 e2 op = parens ("_N.cmp(" ++ e1 ++ "," ++ e2 ++ ").ctor" ++ op)
319+jsCompare e1 e2 op = parens ("_N.cmp(" ++ e1 ++ "," ++ e2 ++ ")" ++ op)
334−binop (o:p) e1 e2
322+binop op e1 e2
335− | isAlpha o || '_' == o = (o:p) ++ parens e1 ++ parens e2
323+ | op == "Basics.." =
324+ do ss <- mapM toJS' (e1 : collect [] e2)
325+ return . jsFunc "$" . ret $ apply "$" ss
326+ | op == "Basics.<|" =
328+ funcs <- mapM toJS' (collect [] e1)
329+ return (apply arg funcs)
336 | otherwise =
330 | otherwise =
337− let ops = ["+","-","*","/","&&","||"] in
331+ binopNormal op `liftM` toJS' e1 `ap` toJS' e2
338− case o:p of
332+ where
333+ apply = foldr (\f arg-> f ++ parens arg)
337+ L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
340+binopNormal op s1 s2 =
341+ case Map.lookup op opDict of
343+ Nothing -> "A2" ++ parens (commaSep [ prefix ++ func, s1, s2 ])
345+ func | isOp op' = "_op['" ++ op' ++ "']"
347+ (prefix, op') = case List.elemIndices '.' op of
349+ xs -> splitAt (last xs + 1) op
351+ opDict = Map.fromList $ basics ++
339− "::" -> jsCons e1 e2
352+ [ ("::", jsCons s1 s2)
340− "++" -> "_L.append" ++ parens (e1 ++ "," ++ e2)
353+ , ("List.++", "_L.append" ++ parens (s1 ++ "," ++ s2)) ]
341− "<|" -> e1 ++ parens e2
354+
342− "|>" -> e2 ++ parens e1
355+ ops = pow : map (\op -> (op, parens (s1 ++ op ++ s2))) ["+","-","*","/","&&","||"]
343− "." -> jsFunc "x" . ret $ e1 ++ parens (e2 ++ parens "x")
344− "^" -> "Math.pow(" ++ e1 ++ "," ++ e2 ++ ")"
356+ where pow = ("^" , "Math.pow(" ++ s1 ++ "," ++ s2 ++ ")")
358+ basics = ops ++ map (\(op,e) -> ("Basics." ++ op, e))
359+ (ops ++ [ ("<|", s1 ++ parens s2)
360+ , ("|>", s2 ++ parens s1)
345− "==" -> "_N.eq(" ++ e1 ++ "," ++ e2 ++ ")"
361+ , ("==", "_N.eq(" ++ s1 ++ "," ++ s2 ++ ")")
346− "/=" -> "!_N.eq(" ++ e1 ++ "," ++ e2 ++ ")"
362+ , ("/=", "!_N.eq(" ++ s1 ++ "," ++ s2 ++ ")")
347− "<" -> jsCompare e1 e2 "==='LT'"
363+ , ("<" , jsCompare s1 s2 "<0")
348− ">" -> jsCompare e1 e2 "==='GT'"
364+ , (">" , jsCompare s1 s2 ">0")
349− "<=" -> jsCompare e1 e2 "!=='GT'"
365+ , ("<=", jsCompare s1 s2 "<1")
350− ">=" -> jsCompare e1 e2 "!=='LT'"
366+ , (">=", jsCompare s1 s2 ">-1")
351− "<~" -> "A2(lift," ++ e1 ++ "," ++ e2 ++ ")"
367+ , ("div", parens (s1 ++ "/" ++ s2 ++ "|0"))
352− "~" -> "A3(lift2,F2(function(f,x){return f(x)}),"++e1++","++e2++")"
368+ ])
353− _ | elem (o:p) ops -> parens (e1 ++ (o:p) ++ e2)
354− | otherwise -> concat [ "$op['", o:p, "']"
355− , parens e1, parens e2 ]