2module Generate.JavaScript (generate) where
4import Control.Arrow (first,(***))
5import Control.Applicative ((<$>),(<*>))
6import Control.Monad.State
7import qualified Data.List as List
8import qualified Data.Map as Map
9import qualified Data.Set as Set
11import Generate.JavaScript.Helpers
12import qualified Generate.Cases as Case
13import qualified Generate.JavaScript.Ports as Port
14import qualified Generate.Markdown as MD
15import qualified Parse.Helpers as PHelp
16import qualified SourceSyntax.Helpers as Help
17import SourceSyntax.Literal
18import SourceSyntax.Pattern as Pattern
19import SourceSyntax.Location
20import SourceSyntax.Expression
21import SourceSyntax.Module
22import Language.ECMAScript3.Syntax
23import Language.ECMAScript3.PrettyPrint
24import qualified Transform.SafeNames as MakeSafe
26varDecl :: String -> Expression () -> VarDecl ()
28 VarDecl () (var x) (Just expr)
30include :: String -> String -> VarDecl ()
31include alias moduleName =
32 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
34internalImports :: String -> Statement ()
37 [ varDecl "_N" (obj "Elm.Native")
38 , include "_U" "_N.Utils"
39 , include "_L" "_N.List"
40 , include "_E" "_N.Error"
41 , include "_J" "_N.JavaScript"
42 , varDecl "$moduleName" (string name)
45literal :: Literal -> Expression ()
48 Chr c -> obj "_U.chr" <| string [c]
50 IntNum n -> IntLit () n
51 FloatNum n -> NumLit () n
52 Boolean b -> BoolLit () b
54expression :: LExpr -> State Int (Expression ())
55expression (L span expr) =
57 Var x -> return $ ref x
58 Literal lit -> return $ literal lit
61 do lo' <- expression lo
63 return $ obj "_L.range" `call` [lo',hi']
67 return $ DotRef () e' (var x)
71 return $ obj "_U.remove" `call` [string x, e']
76 return $ obj "_U.insert" `call` [string x, v', e']
80 fs' <- forM fs $ \(f,v) -> do
82 return $ ArrayLit () [string f, v']
83 return $ obj "_U.replace" `call` [ArrayLit () fs', e']
86 do fields' <- forM fields $ \(f,e) -> do
87 (,) f <$> expression e
88 let fieldMap = List.foldl' combine Map.empty fields'
89 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
91 combine r (k,v) = Map.insertWith (++) k [v] r
92 prop = PropId () . var
93 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
94 Map.toList . Map.filter (not . null) $ Map.map tail fs
95 visible fs = map (first prop) . Map.toList $ Map.map head fs
97 Binop op e1 e2 -> binop span op e1 e2
100 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
101 body' <- expression body
102 return $ case length args < 2 || length args > 9 of
103 True -> foldr (==>) body' (map (:[]) args)
104 False -> ref ("F" ++ show (length args)) <| (args ==> body')
106 depattern (args, body) pattern =
108 PVar x -> return (args ++ [x], body)
109 _ -> do arg <- Case.newVar
110 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
112 (patterns, innerBody) = collect [p] e
114 collect patterns lexpr@(L _ expr) =
116 Lambda p e -> collect (p:patterns) e
117 _ -> (patterns, lexpr)
120 do func' <- expression func
121 args' <- mapM expression args
122 return $ case args' of
123 [arg] -> func' <| arg
124 _ | length args' <= 9 -> ref aN `call` (func':args')
125 | otherwise -> foldl1 (<|) (func':args')
127 aN = "A" ++ show (length args)
128 (func, args) = getArgs e1 [e2]
131 (L _ (App f arg)) -> getArgs f (arg : args)
135 do let (defs',e') = flattenLets defs e
136 stmts <- concat <$> mapM definition defs'
138 return $ function [] (stmts ++ [ ret exp ]) `call` []
141 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
142 return $ case last branches of
143 (L _ (Var "Basics.otherwise"), _) -> safeIfs branches'
144 (L _ (Literal (Boolean True)), _) -> safeIfs branches'
145 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
147 safeIfs branches = ifs (init branches) (snd (last branches))
148 ifs branches finally = foldr iff finally branches
149 iff (if', then') else' = CondExpr () if' then' else'
152 do (tempVar,initialMatch) <- Case.toMatch cases
153 (revisedMatch, stmt) <-
155 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
156 _ -> do e' <- expression e
157 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
158 match' <- match span revisedMatch
159 return (function [] (stmt ++ match') `call` [])
162 do es' <- mapM expression es
163 return $ obj "_J.toList" <| ArrayLit () es'
166 do es' <- mapM expression es
167 return $ ObjectLit () (ctor : fields es')
169 ctor = (prop "ctor", string name)
170 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
172 Markdown uid doc es ->
173 do es' <- mapM expression es
174 return $ obj "Text.markdown" `call` (string md : string uid : es')
176 pad = "<div style=\"height:0;width:0;\"> </div>"
177 md = pad ++ MD.toHtml doc ++ pad
180 return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
182 PortOut name tipe value ->
183 do value' <- expression value
184 return $ obj "Native.Ports.portOut" `call`
185 [ string name, Port.outgoing tipe, value' ]
187definition :: Def -> State Int [Statement ()]
188definition (Definition pattern expr@(L span _) _) = do
189 expr' <- expression expr
190 let assign x = varDecl x expr'
194 let op = LBracket () (ref "_op") (string x) in
195 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
197 return [ VarDeclStmt () [ assign x ] ]
200 let setField f = varDecl f (dotSep ["$",f]) in
201 return [ VarDeclStmt () (assign "$" : map setField fields) ]
203 PData name patterns | vars /= Nothing ->
204 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
206 vars = getVars patterns
209 PVar x : rest -> (x:) `fmap` getVars rest
213 decl x n = varDecl x (dotSep ["$","_" ++ show n])
215 | Help.isTuple name = assign "$" : vars
216 | otherwise = assign "$raw" : safeAssign : vars
218 safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
219 if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
220 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
223 do defs' <- concat <$> mapM toDef vars
224 return (VarDeclStmt () [assign "$"] : defs')
226 vars = Set.toList $ Pattern.boundVars pattern
228 toDef y = let expr = L span $ Case (mkVar "$") [(pattern, mkVar y)]
229 in definition $ Definition (PVar y) expr Nothing
231match :: SrcSpan -> Case.Match -> State Int [Statement ()]
234 Case.Match name clauses mtch' ->
235 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
236 mtch'' <- match span mtch'
237 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
239 isLiteral p = case p of
240 Case.Clause (Right _) _ _ -> True
242 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
244 | or isChars = InfixExpr () OpAdd e (string "")
248 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
250 Case.Break -> return [BreakStmt () Nothing]
252 do e' <- expression e
254 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
259 Case.Other _ -> acc ++ [m]
260 _ -> dropEnd (acc ++ [m]) ms
262clause :: SrcSpan -> String -> Case.Clause -> State Int (Bool, CaseClause ())
263clause span variable (Case.Clause value vars mtch) =
264 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
266 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
269 Right (Chr c) -> (True, string [c])
270 _ -> (,) False $ case value of
271 Right (Boolean b) -> BoolLit () b
272 Right lit -> literal lit
273 Left name -> string $ case List.elemIndices '.' name of
275 is -> drop (last is + 1) name
277flattenLets :: [Def] -> LExpr -> ([Def], LExpr)
278flattenLets defs lexpr@(L _ expr) =
280 Let ds body -> flattenLets (defs ++ ds) body
283generate :: MetadataModule -> String
284generate unsafeModule =
285 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
286 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
288 modul = MakeSafe.metadataModule unsafeModule
289 thisModule = dotSep ("_elm" : names modul ++ ["values"])
292 [ setup (Just "_elm") (names modul ++ ["values"])
293 , [ IfSingleStmt () thisModule (ret thisModule) ]
294 , [ internalImports (List.intercalate "." (names modul)) ]
295 , concatMap jsImport . Set.toList . Set.fromList . map fst $ imports modul
296 , [ assign ["_op"] (ObjectLit () []) ]
297 , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
302 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
304 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
305 entry x = (PropId () (var x), ref x)
309 [x] -> VarDeclStmt () [ varDecl x expr ]
311 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
313 jsImport modul = setup Nothing path ++ [ include ]
315 path = PHelp.splitDots modul
316 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
318 setup namespace path = map create paths
320 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
321 paths = case namespace of
322 Nothing -> tail . init $ List.inits path
323 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
325binop :: SrcSpan -> String -> LExpr -> LExpr -> State Int (Expression ())
329 do es <- mapM expression (e1 : collect [] e2)
330 return $ ["$"] ==> foldr (<|) (ref "$") es
332 do e2' <- expression e2
333 es <- mapM expression (collect [] e1)
334 return $ foldr (<|) e2' es
336 do e1' <- expression e1
338 return $ obj "_L.append" `call` [e1', e2']
339 "::" -> expression (L span (Data "::" [e1,e2]))
341 do e1' <- expression e1
343 return $ case Map.lookup op opDict of
345 Nothing -> ref "A2" `call` [ func, e1', e2' ]
349 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
352 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
353 | otherwise = dotSep parts
355 parts = PHelp.splitDots op
356 operator = last parts
358 opDict = Map.fromList (infixOps ++ specialOps)
360 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
361 infixOp str op = specialOp str (InfixExpr () op)
368 , infixOp "&&" OpLAnd
373 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
374 , specialOp "|>" $ flip (<|)
375 , specialOp "==" $ \a b -> obj "_U.eq" `call` [a,b]
376 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
377 , specialOp "<" $ cmp OpLT 0
378 , specialOp ">" $ cmp OpGT 0
379 , specialOp "<=" $ cmp OpLT 1
380 , specialOp ">=" $ cmp OpGT (-1)
381 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
384 cmp op n a b = InfixExpr () op (obj "_U.cmp" `call` [a,b]) (IntLit () n)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list