@@ -1,361 +1,411 @@
1−module Generate.JavaScript (jsModule) where
1+module Generate.JavaScript where
3−import Control.Arrow (first,second)
3+import Control.Arrow (first,(***))
4+import Control.Applicative ((<$>),(<*>))
4−import Control.Monad (liftM,(<=<),join,ap)
5+import Control.Monad.State
5−import Data.Char (isAlpha,isDigit)
6import qualified Data.List as List
6import qualified Data.List as List
7import qualified Data.Map as Map
7import qualified Data.Map as Map
8import qualified Data.Set as Set
8import qualified Data.Set as Set
9−import Data.Either (partitionEithers)
10import qualified Text.Pandoc as Pan
9import qualified Text.Pandoc as Pan
13−import Generate.Cases
11+import qualified Generate.Cases as Case
14import SourceSyntax.Everything
12import SourceSyntax.Everything
15import SourceSyntax.Location
13import SourceSyntax.Location
16import qualified Transform.SortDefinitions as SD
14import qualified Transform.SortDefinitions as SD
15+import Language.ECMAScript3.Syntax
16+import Language.ECMAScript3.PrettyPrint
17+import Parse.Helpers (jsReserveds)
18−indent = concatMap f
19+makeSafe :: String -> String
19− where f '\n' = "\n "
20+makeSafe = List.intercalate "." . map dereserve . split . deprime
22+ deprime = map (\c -> if c == '\'' then '$' else c)
23+ dereserve x = case Set.member x jsReserveds of
30+ case break (=='.') str of
31+ (x,'.':rest) | isOp x -> vars ++ [x ++ '.' : rest]
32+ | otherwise -> go (vars ++ [x]) rest
33+ (x,[]) -> vars ++ [x]
35+var name = Id () (makeSafe name)
36+ref name = VarRef () (var name)
37+prop name = PropId () (var name)
38+f <| x = CallExpr () f [x]
39+args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
40+function args stmts = FuncExpr () Nothing (map var args) stmts
44+dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
48+ VarDecl () (var x) (Just expr)
50+include alias moduleName =
51+ varDecl alias (obj (moduleName ++ ".make") <| ref "elm")
22internalImports name =
53internalImports name =
23− [ ("N" , "Elm.Native"),
55+ [ varDecl "N" (obj "Elm.Native")
24− ("_N", "N.Utils(elm)"),
56+ , include "_N" "N.Utils"
25− ("_L", "N.List(elm)"),
57+ , include "_L" "N.List"
26− ("_E", "N.Error(elm)"),
58+ , include "_E" "N.Error"
27− ("_J", "N.JavaScript(elm)"),
59+ , include "_J" "N.JavaScript"
28− ("_str", "_J.toString"),
29− ("$moduleName", quoted name)
60+ , varDecl "$moduleName" (string name)
32−parens s = "(" ++ s ++ ")"
63+literal lit =
33−brackets s = "{" ++ s ++ "}"
64+ case lit of
34−commaSep = List.intercalate ", "
65+ Chr c -> obj "_N.chr" <| string [c]
35−dotSep = List.intercalate "."
66+ Str s -> string s
36−jsObj = brackets . commaSep
67+ IntNum n -> IntLit () n
37−jsList ss = "["++ List.intercalate "," ss ++"]"
68+ FloatNum n -> NumLit () n
38−jsFunc args body = "function(" ++ args ++ "){" ++ indent body ++ "}"
69+ Boolean b -> BoolLit () b
39−assign x e = "\nvar " ++ x ++ " = " ++ e ++ ";"
70+
40−ret e = "\nreturn "++ e ++";"
71+expression :: LExpr () () -> State Int (Expression ())
41−quoted s = "'" ++ concatMap f s ++ "'"
72+expression (L span expr) =
42− where f '\n' = "\\n"
73+ case expr of
43− f '\'' = "\\'"
74+ Var x -> return $ ref x
44− f '\t' = "\\t"
75+ Literal lit -> return $ literal lit
46− f '\\' = "\\\\"
77+ Range lo hi ->
47− f c = [c]
78+ do lo' <- expression lo
48−
79+ hi' <- expression hi
49−globalAssign n e = "\n" ++ assign' n e ++ ";"
80+ return $ obj "_L.range" `call` [lo',hi']
50−assign' n e = n ++ " = " ++ e
81+
83+ do e' <- expression e
84+ return $ DotRef () e' (var x)
87+ do e' <- expression e
88+ return $ obj "_N.remove" `call` [string x, e']
91+ do v' <- expression v
93+ return $ obj "_N.insert" `call` [string x, v', e']
96+ do e' <- expression e
97+ fs' <- forM fs $ \(f,v) -> do
99+ return $ ArrayLit () [string f, v']
100+ return $ obj "_N.replace" `call` [ArrayLit () fs', e']
103+ do fields' <- forM fields $ \(f,e) -> do
104+ (,) f <$> expression e
105+ let fieldMap = List.foldl' combine Map.empty fields'
106+ return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
108+ combine r (k,v) = Map.insertWith (++) k [v] r
109+ prop = PropId () . var
110+ hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
111+ Map.toList . Map.filter (not . null) $ Map.map tail fs
112+ visible fs = map (first prop) . Map.toList $ Map.map head fs
114+ Binop op e1 e2 -> binop span op e1 e2
116+ Lambda p e@(L s _) ->
117+ do body' <- expression body
118+ return $ case length args < 2 || length args > 9 of
119+ True -> foldr (==>) body' (map (:[]) args)
120+ False -> ref ("F" ++ show (length args)) <| (args ==> body')
122+ (args, body) = foldr depattern ([], innerBody) (zip patterns [0..])
124+ depattern (pattern,n) (args, body) =
126+ PVar x -> (args ++ [x], body)
127+ _ -> let arg = "arg" ++ show n
128+ in (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
130+ (patterns, innerBody) = collect [p] e
132+ collect patterns lexpr@(L _ expr) =
134+ Lambda p e -> collect (p:patterns) e
135+ _ -> (patterns, lexpr)
138+ do func' <- expression func
139+ args' <- mapM expression args
140+ return $ case args' of
141+ [arg] -> func' <| arg
142+ _ | length args' <= 9 -> ref aN `call` (func':args')
143+ | otherwise -> foldl1 (<|) (func':args')
145+ aN = "A" ++ show (length args)
146+ (func, args) = getArgs e1 [e2]
149+ (L _ (App f arg)) -> getArgs f (arg : args)
153+ do let (defs',e') = SD.flattenLets defs e
154+ stmts <- concat <$> mapM definition defs'
155+ exp <- expression e'
156+ return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
159+ do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
160+ return $ case last branches of
161+ (L _ (Var "Basics.otherwise"), e) -> ifs (init branches') (snd (last branches'))
163+ (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
165+ ifs branches finally = foldr iff finally branches
166+ iff (if', then') else' = CondExpr () if' then' else'
169+ do (tempVar,initialMatch) <- Case.toMatch cases
170+ (revisedMatch, stmt) <-
172+ L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
173+ _ -> do e' <- expression e
174+ return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
175+ match' <- match span revisedMatch
176+ return (function [] (stmt ++ match') `call` [])
179+ do es' <- mapM expression es
180+ return $ obj "_J.toList" <| ArrayLit () es'
183+ do es' <- mapM expression es
184+ return $ ObjectLit () (ctor : fields es')
186+ ctor = (prop "ctor", string (makeSafe name))
187+ fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
189+ Markdown doc _ -> return $ obj "Text.text" <| string (pad ++ md ++ pad)
190+ where pad = "<div style=\"height:0;width:0;\"> </div>"
191+ md = Pan.writeHtmlString Pan.def doc
193+definition :: Def () () -> State Int [Statement ()]
196+ TypeAnnotation _ _ -> return []
198+ Def pattern expr@(L span _) -> do
199+ expr' <- expression expr
200+ let assign x = varDecl x expr'
204+ let op = LBracket () (ref "_op") (string x) in
205+ return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
207+ return [ VarDeclStmt () [ assign x ] ]
210+ let setField f = varDecl f (dotSep ["$",f]) in
211+ return [ VarDeclStmt () (assign "$" : map setField fields) ]
213+ PData name patterns | vars /= Nothing ->
215+ Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
217+ vars = getVars patterns
220+ PVar x : rest -> (x:) `fmap` getVars rest
224+ decl x n = varDecl x (dotSep ["$","_" ++ show n])
226+ | isTuple name = assign "$" : vars
227+ | otherwise = safeAssign : vars
229+ safeAssign = varDecl "$" (CondExpr () if' expr' exception)
230+ if' = InfixExpr () OpStrictEq (obj "$.ctor") (string name)
231+ exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
234+ do defs' <- concat <$> mapM toDef vars
235+ return (VarDeclStmt () [assign "$"] : defs')
237+ vars = Set.toList $ SD.boundVars pattern
238+ mkVar = L span . Var
239+ toDef y = definition $
240+ Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
242+match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
245+ Case.Match name clauses mtch' ->
246+ do clauses' <- mapM (clause span name) clauses
247+ mtch'' <- match span mtch'
248+ return (SwitchStmt () (access name) clauses' : mtch'')
250+ isLiteral p = case p of
251+ Case.Clause (Right _) _ _ -> True
253+ access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
256+ return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
258+ Case.Break -> return [BreakStmt () Nothing]
260+ do e' <- expression e
261+ return [ ReturnStmt () (Just e') ]
262+ Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
264+ dropEnd acc [] = acc
265+ dropEnd acc (m:ms) =
267+ Case.Other _ -> acc ++ [m]
268+ _ -> dropEnd (acc ++ [m]) ms
270+clause span variable (Case.Clause value vars mtch) =
271+ CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
273+ vars' = map (\n -> variable ++ "._" ++ show n) [0..]
274+ pattern = case value of
275+ Right (Boolean b) -> BoolLit () b
276+ Right lit -> literal lit
277+ Left name -> string $ case List.elemIndices '.' name of
279+ is -> drop (last is + 1) name
52−jsModule :: MetadataModule t v -> String
282+jsModule :: MetadataModule () () -> String
53jsModule modul =
283jsModule modul =
54− run $ do
284+ show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
55− body <- toJS . fst . SD.flattenLets [] $ program modul
285+ [ assign ("Elm" : names modul ++ ["make"]) (function ["elm"] programStmts) ]
56− foreignImport <- mapM importEvent (foreignImports modul)
57− return $ concat [ setup ("Elm": names modul)
58− , globalAssign ("Elm." ++ modName)
59− (jsFunc "elm" $ makeProgram body foreignImport) ]
61− modName = dotSep (names modul)
287+ thisModule = dotSep ("elm" : names modul ++ ["values"])
62− makeProgram body foreignImport =
288+ programStmts =
63− concat [ "\nvar " ++ usefulFuncs ++ ";"
289+ concat [ setup (Just "elm") (names modul ++ ["values"])
290+ , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
291+ , [ internalImports (List.intercalate "." (names modul)) ]
64 , concatMap jsImport (imports modul)
292 , concatMap jsImport (imports modul)
65− , concat foreignImport
293+ , concatMap importEvent (foreignImports modul)
66− , assign "_op" "{}"
294+ , [ assign ["_op"] (ObjectLit () []) ]
67− , body
295+ , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
68− , concatMap exportEvent $ foreignExports modul
296+ , map exportEvent $ foreignExports modul
69− , jsExports
297+ , [ jsExports ]
298+ , [ ReturnStmt () (Just thisModule) ]
71− setup names = concatMap (\n -> globalAssign n $ n ++ " || {}") .
72− map dotSep . drop 2 . List.inits $ init names
73− usefulFuncs = commaSep (map (uncurry assign') (internalImports modName))
75− jsExports = setup ("elm" : names modul) ++
301+ jsExports = assign ("elm" : names modul ++ ["values"]) (ObjectLit () exs)
76− ret (assign' ("elm." ++ modName) (brackets exs))
302+ where
303+ exs = map entry . filter (not . isOp) $ "_op" : exports modul
304+ entry x = (PropId () (var x), ref x)
308+ [x] -> VarDeclStmt () [ varDecl x expr ]
310+ AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
312+ jsImport (modul,_) = setup Nothing path ++ [ include ]
315+ include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "elm"
317+ setup namespace path = map create paths
78− exs = indent . commaSep . concatMap pair $ "_op" : exports modul
319+ create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
79− pair x | isOp x = []
320+ paths = case namespace of
80− | otherwise = ["\n" ++ x ++ " : " ++ x]
321+ Nothing -> tail . init $ List.inits path
322+ Just nmspc -> drop 2 . init . List.inits $ nmspc : path
324+ addId js = InfixExpr () OpAdd (string (js++"_")) (obj "elm.id")
83 importEvent (js,base,elm,_) =
326 importEvent (js,base,elm,_) =
84− do v <- toJS' base
327+ [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
85− return $ concat [ "\nvar " ++ elm ++ "=Signal.constant(" ++ v ++ ");"
328+ , ExprStmt () $
86− , "\ndocument.addEventListener('", js
329+ obj "document.addEventListener" `call`
87− , "_' + elm.id, function(e) { elm.notify(", elm
331+ , function ["e"]
88− , ".id, e.value); });" ]
332+ [ ExprStmt () $ obj "elm.notify" `call` [dotSep [elm,"id"], obj "e.value"] ]
90 exportEvent (js,elm,_) =
336 exportEvent (js,elm,_) =
91− concat [ "\nA2( Signal.lift, function(v) { "
339+ [ obj "Signal.lift"
92− , "var e = document.createEvent('Event');"
341+ [ VarDeclStmt () [varDecl "e" $ obj "document.createEvent" <| string "Event"]
93− , "e.initEvent('", js, "_' + elm.id, true, true);"
342+ , ExprStmt () $
94− , "e.value = v;"
343+ obj "e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
344+ , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "e") "value") (ref "v")
95− , "document.dispatchEvent(e); return v; }, ", elm, ");" ]
345+ , ExprStmt () $ obj "document.dispatchEvent" <| ref "e"
96−
346+ , ReturnStmt () (Just $ ref "v")
97−jsImport (modul, method) =
347+ ]
98− concat $ zipWith3 (\s n v -> s ++ assign' n v ++ ";") starters subnames values
348+ , ref elm ]
100− starters = "\nvar " : repeat "\n"
101− values = map (\name -> name ++ " || {}") (init subnames) ++
102− ["Elm." ++ modul ++ parens "elm"]
103− subnames = map dotSep . tail . List.inits $ split modul
105− split names = case go [] names of
106− (name, []) -> [name]
107− (name, ns) -> name : split ns
108− go name str = case str of
109− '.':rest -> (reverse name, rest)
110− c:rest -> go (c:name) rest
111− [] -> (reverse name, [])
115− toJS :: a -> Unique String
118−instance ToJS (Def t v) where
120− -- TODO: Make this handle patterns besides plain variables
121− toJS (Def (PVar x) e)
122− | isOp x = globalAssign ("_op['" ++ x ++ "']") `liftM` toJS' e
123− | otherwise = assign x `liftM` toJS' e
125− toJS (Def pattern e@(L s _)) =
127− let x = "_" ++ show n
129− toDef y = Def (PVar y) (L s $ Case (var x) [(pattern, var y)])
130− stmt <- assign x `liftM` toJS' e
131− vars <- toJS . map toDef . Set.toList $ SD.boundVars pattern
132− return (stmt ++ vars)
134− toJS (TypeAnnotation _ _) = return ""
137−instance ToJS a => ToJS [a] where
138− toJS xs = concat `liftM` mapM toJS xs
140−toJS' :: LExpr t v -> Unique String
141−toJS' (L span expr) =
143− MultiIf ps -> multiIfToJS span ps
144− Case e cases -> caseToJS span e cases
147−remove x e = "_N.remove('" ++ x ++ "', " ++ e ++ ")"
148−addField x v e = "_N.insert('" ++ x ++ "', " ++ v ++ ", " ++ e ++ ")"
149−setField fs e = "_N.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
150− where f (x,v) = "['" ++ x ++ "'," ++ v ++ "]"
151−access x e = e ++ "." ++ x
152−makeRecord kvs = record `liftM` collect kvs
154− combine r (k,v) = Map.insertWith (++) k v r
155− collect = liftM (List.foldl' combine Map.empty) . mapM prep
160− brackets ("\n "++List.intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
161− hidden = fields . map (second jsList) .
162− filter (not . null . snd) . Map.toList . Map.map tail
163− record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
166−instance ToJS Literal where
169− Chr c -> return $ quoted [c]
170− Str s -> return $ "_str" ++ parens (quoted s)
171− IntNum n -> return $ show n
172− FloatNum n -> return $ show n
173− Boolean b -> return $ if b then "true" else "false"
176−instance ToJS (Expr t v) where
180− Literal lit -> toJS lit
181− Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
182− Access e x -> access x `liftM` toJS' e
183− Remove e x -> remove x `liftM` toJS' e
184− Insert e x v -> addField x `liftM` toJS' v `ap` toJS' e
185− Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
186− setField fs' `liftM` toJS' e
187− Record fs -> makeRecord fs
188− Binop op e1 e2 -> binop op e1 e2
350+binop span op e1 e2 =
190− Lambda p e@(L s _) -> liftM fastFunc (toJS' body)
352+ "Basics.." ->
191− where
353+ do es <- mapM expression (e1 : collect [] e2)
192− fastFunc body
354+ return $ ["$"] ==> foldr (<|) (ref "$") es
193− | length args < 2 || length args > 9 =
355+ "Basics.<|" ->
194− foldr (\arg bod -> jsFunc arg (ret bod)) body args
356+ do e2' <- expression e2
195− | otherwise =
357+ es <- mapM expression (collect [] e1)
196− "F" ++ show (length args) ++ parens (jsFunc (commaSep args) (ret body))
358+ return $ foldr (<|) e2' es
198− (args, body) = first reverse $ foldr depattern ([], innerBody) (zip patterns [1..])
360+ do e1' <- expression e1
199−
361+ e2' <- expression e2
200− depattern (pattern,n) (args, body) =
362+ return $ obj "_L.append" `call` [e1', e2']
201− case pattern of
363+ "::" -> expression (L span (Data "::" [e1,e2]))
202− PVar x -> (x : args, body)
364+ _ ->
203− _ -> let arg = "arg" ++ show n
365+ do e1' <- expression e1
204− in (arg:args, L s (Case (L s (Var arg)) [(pattern, body)]))
366+ e2' <- expression e2
205−
367+ return $ case Map.lookup op opDict of
206− (patterns, innerBody) = collect [p] e
368+ Just f -> f e1' e2'
207−
369+ Nothing -> ref "A2" `call` [ func, e1', e2' ]
208− collect patterns lexpr@(L _ expr) =
210− Lambda p e -> collect (p:patterns) e
211− _ -> (patterns, lexpr)
213− App e1 e2 -> jsApp e1 e2
214− Let defs e -> jsLet $ SD.flattenLets defs e
217− do es' <- mapM toJS' es
218− return $ "_J.toList" ++ parens (jsList es')
221− do fs <- mapM toJS' es
222− return $ case name of
224− "::" -> jsCons (head fs) ((head . tail) fs)
225− _ -> jsObj $ ("ctor:" ++ show name) : fields
226− where fields = zipWith (\n e -> "_" ++ show n ++ ":" ++ e) [0..] fs
228− Markdown doc -> return $ "Text.text('" ++ pad ++ md ++ pad ++ "')"
229− where pad = "<div style=\"height:0;width:0;\"> </div>"
230− md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
234− as <- mapM toJS' args
236− [a] -> f ++ parens a
237− _ -> "A" ++ show (length as) ++ parens (commaSep (f:as))
239− (func, args) = go [e2] e1
242− (L _ (App e1 e2)) -> go (e2 : args) e1
245−formatMarkdown = concatMap f
246− where f '\'' = "\\'"
251−multiIfToJS span ps =
253− (L _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
254− _ -> format ("_E.If" ++ parens ("$moduleName," ++ quoted (show span))) ps
257− foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
258− f (b,e) = do b' <- toJS' b
260− return (b' ++ " ? " ++ e')
262−jsLet (defs,e') = do ds <- mapM toJS defs
264− return $ jsFunc "" (concat ds ++ ret e) ++ "()"
266−caseToJS span e ps = do
267− (tempVar,match) <- caseToMatch ps
269− let (match',stmt) = case e of
270− L _ (Var x) -> (matchSubst [(tempVar,x)] match, "")
271− _ -> (match, assign tempVar e')
272− matches <- matchToJS span match'
273− return $ jsFunc "" (stmt ++ matches) ++ "()"
275−matchToJS span match =
277− Match name clauses def ->
278− do cases <- concat `liftM` mapM (clauseToJS span name) clauses
279− finally <- matchToJS span def
280− let isLiteral p = case p of
281− Clause (Right _) _ _ -> True
283− access = if any isLiteral clauses then "" else ".ctor"
284− return $ concat [ "\nswitch (", name, access, ") {"
285− , indent cases, "\n}", finally ]
286− Fail -> return ("_E.Case" ++ parens ("$moduleName," ++ quoted (show span)))
287− Break -> return "break;"
288− Other e -> ret `liftM` toJS' e
289− Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
291− dropEnd acc [] = acc
292− dropEnd acc (m:ms) =
294− Other _ -> acc ++ [m]
295− _ -> dropEnd (acc ++ [m]) ms
297−clauseToJS span var (Clause value vars e) = do
298− let vars' = map (\n -> var ++ "._" ++ show n) [0..]
299− s <- matchToJS span $ matchSubst (zip vars vars') e
300− pattern <- case value of
301− Right (Boolean True) -> return "true"
302− Right (Boolean False) -> return "false"
303− Right lit -> toJS lit
304− Left name -> return . quoted $ case List.elemIndices '.' name of
306− is -> drop (last is + 1) name
307− return $ concat [ "\ncase ", pattern, ":", indent s ]
310−jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
311−jsRange e1 e2 = "_L.range" ++ parens (e1 ++ "," ++ e2)
312−jsCompare e1 e2 op = parens ("_N.cmp(" ++ e1 ++ "," ++ e2 ++ ")" ++ op)
316− | op == "Basics.." =
317− do ss <- mapM toJS' (e1 : collect [] e2)
318− return . jsFunc "$" . ret $ apply "$" ss
319− | op == "Basics.<|" =
321− funcs <- mapM toJS' (collect [] e1)
322− return (apply arg funcs)
324− binopNormal op `liftM` toJS' e1 `ap` toJS' e2
326− apply = foldr (\f arg-> f ++ parens arg)
328 collect es e =
371 collect es e =
329 case e of
372 case e of
330 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
373 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
331 _ -> es ++ [e]
374 _ -> es ++ [e]
333−binopNormal op s1 s2 =
376+ js1 = expression e1
334− case Map.lookup op opDict of
377+ js2 = expression e2
336− Nothing -> "A2" ++ parens (commaSep [ prefix ++ func, s1, s2 ])
379+ func | isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
338− func | isOp op' = "_op['" ++ op' ++ "']"
339− | otherwise = op'
380+ | otherwise = dotSep parts
340− (prefix, op') = case List.elemIndices '.' op of
381+ where
341− [] -> ("", op)
382+ parts = split op
342− xs -> splitAt (last xs + 1) op
383+ operator = last parts
344− opDict = Map.fromList $ basics ++
385+ opDict = Map.fromList (infixOps ++ specialOps)
345− [ ("::", jsCons s1 s2)
386+
346− , ("List.++", "_L.append" ++ parens (s1 ++ "," ++ s2)) ]
387+ specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
347−
388+ infixOp str op = specialOp str (InfixExpr () op)
348− ops = pow : map (\op -> (op, parens (s1 ++ op ++ s2))) ["+","-","*","/","&&","||"]
389+
349− where pow = ("^" , "Math.pow(" ++ s1 ++ "," ++ s2 ++ ")")
390+ infixOps = concat
350−
391+ [ infixOp "+" OpAdd
351− basics = ops ++ map (\(op,e) -> ("Basics." ++ op, e))
392+ , infixOp "-" OpSub
352− (ops ++ [ ("<|", s1 ++ parens s2)
393+ , infixOp "*" OpMul
353− , ("|>", s2 ++ parens s1)
394+ , infixOp "/" OpDiv
354− , ("==", "_N.eq(" ++ s1 ++ "," ++ s2 ++ ")")
395+ , infixOp "&&" OpLAnd
355− , ("/=", "!_N.eq(" ++ s1 ++ "," ++ s2 ++ ")")
396+ , infixOp "||" OpLOr
356− , ("<" , jsCompare s1 s2 "<0")
397+ ]
357− , (">" , jsCompare s1 s2 ">0")
398+
358− , ("<=", jsCompare s1 s2 "<1")
399+ specialOps = concat
359− , (">=", jsCompare s1 s2 ">-1")
400+ [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
360− , ("div", parens (s1 ++ "/" ++ s2 ++ "|0"))
401+ , specialOp "|>" $ flip (<|)
361− ])
402+ , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
403+ , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
404+ , specialOp "<" $ cmp OpLT 0
405+ , specialOp ">" $ cmp OpGT 0
406+ , specialOp "<=" $ cmp OpLT 1
407+ , specialOp ">=" $ cmp OpGT (-1)
408+ , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
411+ cmp op n a b = InfixExpr () op (obj "_N.cmp" `call` [a,b]) (IntLit () n)