2module Generate.JavaScript (generate) where
4import Control.Applicative ((<$>),(<*>))
5import Control.Arrow (first,(***))
6import Control.Monad.State
7import qualified Data.List as List
8import qualified Data.Map as Map
9import qualified Data.Set as Set
10import Language.ECMAScript3.PrettyPrint
11import Language.ECMAScript3.Syntax
13import Generate.JavaScript.Helpers
14import qualified Generate.Cases as Case
15import qualified Generate.JavaScript.Ports as Port
16import qualified Generate.Markdown as MD
20import AST.Expression.General
21import qualified AST.Expression.Canonical as Canonical
22import qualified AST.Module as Module
23import qualified AST.Helpers as Help
25import qualified AST.Pattern as P
26import AST.PrettyPrint (renderPretty)
27import qualified AST.Variable as Var
29varDecl :: String -> Expression () -> VarDecl ()
31 VarDecl () (var x) (Just expr)
33include :: String -> String -> VarDecl ()
34include alias moduleName =
35 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
37internalImports :: String -> Statement ()
40 [ varDecl "_N" (obj "Elm.Native")
41 , include "_U" "_N.Utils"
42 , include "_L" "_N.List"
43 , include "_E" "_N.Error"
44 , varDecl "$moduleName" (string name)
47literal :: Literal -> Expression ()
50 Chr c -> obj "_U.chr" <| string [c]
52 IntNum n -> IntLit () n
53 FloatNum n -> NumLit () n
54 Boolean b -> BoolLit () b
56canonical :: Var.Canonical -> Expression ()
57canonical (Var.Canonical home name) =
58 case Help.isOp name of
59 True -> BracketRef () (dotSep (home' ++ ["_op"])) (string name)
60 False -> dotSep (home' ++ [map (swap '\'' '$') name])
62 swap from to c = if c == from then to else c
68 Var.Module path -> [ map (swap '.' '$') path ]
70expression :: Canonical.Expr -> State Int (Expression ())
71expression (A region expr) =
73 Var var -> return $ canonical var
75 Literal lit -> return $ literal lit
78 do lo' <- expression lo
80 return $ obj "_L.range" `call` [lo',hi']
84 return $ DotRef () e' (var x)
88 return $ obj "_U.remove" `call` [string x, e']
93 return $ obj "_U.insert" `call` [string x, v', e']
97 fs' <- forM fs $ \(f,v) -> do
99 return $ ArrayLit () [string f, v']
100 return $ obj "_U.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 () $ (prop "_", hidden fieldMap) : visible fieldMap
108 combine r (k,v) = Map.insertWith (++) k [v] r
109 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
110 Map.toList . Map.filter (not . null) $ Map.map tail fs
111 visible fs = map (first prop) . Map.toList $ Map.map head fs
113 Binop op e1 e2 -> binop region op e1 e2
115 Lambda p e@(A ann _) ->
116 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
117 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 depattern (args, body) pattern =
124 P.Var x -> return (args ++ [x], body)
125 _ -> do arg <- Case.newVar
126 return ( args ++ [arg]
127 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
129 (patterns, innerBody) = collect [p] e
131 collect patterns lexpr@(A _ expr) =
133 Lambda p e -> collect (p:patterns) e
134 _ -> (patterns, lexpr)
137 do func' <- expression func
138 args' <- mapM expression args
139 return $ case args' of
140 [arg] -> func' <| arg
141 _ | length args' <= 9 -> ref aN `call` (func':args')
142 | otherwise -> foldl1 (<|) (func':args')
144 aN = "A" ++ show (length args)
145 (func, args) = getArgs e1 [e2]
148 (A _ (App f arg)) -> getArgs f (arg : args)
152 do let (defs',e') = flattenLets defs e
153 stmts <- concat <$> mapM definition defs'
155 return $ function [] (stmts ++ [ ret exp ]) `call` []
158 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
159 return $ case last branches of
160 (A _ (Var (Var.Canonical (Var.Module "Basics") "otherwise")), _) ->
162 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
163 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (renderPretty region) ])
165 safeIfs branches = ifs (init branches) (snd (last branches))
166 ifs branches finally = foldr iff finally branches
167 iff (if', then') else' = CondExpr () if' then' else'
170 do (tempVar,initialMatch) <- Case.toMatch cases
171 (revisedMatch, stmt) <-
173 A _ (Var (Var.Canonical Var.Local x)) ->
174 return (Case.matchSubst [(tempVar,x)] initialMatch, [])
176 do e' <- expression e
177 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
178 match' <- match region revisedMatch
179 return (function [] (stmt ++ match') `call` [])
182 do es' <- mapM expression es
183 return $ obj "_L.fromArray" <| ArrayLit () es'
186 do es' <- mapM expression es
187 return $ ObjectLit () (ctor : fields es')
189 ctor = (prop "ctor", string name)
190 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
192 Markdown uid doc es ->
193 do es' <- mapM expression es
194 return $ obj "Text.markdown" `call` (string md : string uid : es')
196 pad = "<div style=\"height:0;width:0;\"> </div>"
197 md = pad ++ MD.toHtml doc ++ pad
200 return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
202 PortOut name tipe value ->
203 do value' <- expression value
204 return $ obj "Native.Ports.portOut" `call`
205 [ string name, Port.outgoing tipe, value' ]
207definition :: Canonical.Def -> State Int [Statement ()]
208definition (Canonical.Definition pattern expr@(A region _) _) = do
209 expr' <- expression expr
210 let assign x = varDecl x expr'
214 let op = LBracket () (ref "_op") (string x) in
215 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
217 return [ VarDeclStmt () [ assign x ] ]
220 let setField f = varDecl f (dotSep ["$",f]) in
221 return [ VarDeclStmt () (assign "$" : map setField fields) ]
223 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
224 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
226 vars = getVars patterns
229 P.Var x : rest -> (x:) `fmap` getVars rest
233 decl x n = varDecl x (dotSep ["$","_" ++ show n])
235 | Help.isTuple name = assign "$" : vars
236 | otherwise = assign "_raw" : safeAssign : vars
238 safeAssign = varDecl "$" (CondExpr () if' (obj "_raw") exception)
239 if' = InfixExpr () OpStrictEq (obj "_raw.ctor") (string name)
240 exception = obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]
243 do defs' <- concat <$> mapM toDef vars
244 return (VarDeclStmt () [assign "_"] : defs')
246 vars = P.boundVarList pattern
247 mkVar = A region . localVar
248 toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
249 in definition $ Canonical.Definition (P.Var y) expr Nothing
251match :: Region -> Case.Match -> State Int [Statement ()]
254 Case.Match name clauses mtch' ->
255 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
256 mtch'' <- match region mtch'
257 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
259 isLiteral p = case p of
260 Case.Clause (Right _) _ _ -> True
264 | any isLiteral clauses = obj name
265 | otherwise = dotSep (split name ++ ["ctor"])
268 | or isChars = InfixExpr () OpAdd e (string "")
272 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]) ]
274 Case.Break -> return [BreakStmt () Nothing]
277 do e' <- expression e
280 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
285 Case.Other _ -> acc ++ [m]
286 _ -> dropEnd (acc ++ [m]) ms
288clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
289clause region variable (Case.Clause value vars mtch) =
290 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
292 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
295 Right (Chr c) -> (True, string [c])
296 _ -> (,) False $ case value of
297 Right (Boolean b) -> BoolLit () b
298 Right lit -> literal lit
299 Left (Var.Canonical _ name) ->
302flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
303flattenLets defs lexpr@(A _ expr) =
305 Let ds body -> flattenLets (defs ++ ds) body
308generate :: CanonicalModule -> String
310 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
311 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
313 names = Module.names modul
315 thisModule = dotSep ("_elm" : names ++ ["values"])
318 [ [ ExprStmt () $ string "use strict" ]
319 , setup "_elm" (names ++ ["values"])
320 , [ IfSingleStmt () thisModule (ret thisModule) ]
321 , [ internalImports (List.intercalate "." names) ]
322 , map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
323 , [ assign ["_op"] (ObjectLit () []) ]
324 , concat $ evalState (mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)) 0
329 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
331 exs = map entry $ "_op" : concatMap extract (exports modul)
332 entry x = (prop x, ref x)
336 Var.Value x | Help.isOp x -> []
338 Var.ADT _ (Var.Listing ctors _) -> ctors
342 [x] -> VarDeclStmt () [ varDecl x expr ]
344 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
347 assign [dolarize name] $ dotSep ("Elm" : Help.splitDots name ++ ["make"]) <| ref "_elm"
349 setup namespace path = map create paths
351 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
352 paths = drop 2 . init . List.inits $ namespace : path
354binop :: Region -> Var.Canonical -> Canonical.Expr -> Canonical.Expr
355 -> State Int (Expression ())
356binop region (Var.Canonical home op) e1 e2 =
358 (Var.Module "Basics", ".") ->
359 do es <- mapM expression (e1 : collect [] e2)
360 return $ ["$"] ==> foldr (<|) (ref "$") es
362 (Var.Module "Basics", "<|") ->
363 do e2' <- expression e2
364 es <- mapM expression (collect [] e1)
365 return $ foldr (<|) e2' es
367 (Var.Module "List", "++") ->
368 do e1' <- expression e1
370 return $ obj "_L.append" `call` [e1', e2']
372 (Var.BuiltIn, "::") ->
373 expression (A region (Data "::" [e1,e2]))
375 (Var.Module "Basics", _) ->
376 do e1' <- expression e1
378 return $ case Map.lookup op basicOps of
380 Nothing -> ref "A2" `call` [ func, e1', e2' ]
383 do e1' <- expression e1
385 return (ref "A2" `call` [ func, e1', e2' ])
390 A _ (Binop (Var.Canonical (Var.Module "Basics") ".") e1 e2) ->
391 collect (es ++ [e1]) e2
394 func | Help.isOp op = BracketRef () (dotSep (path ++ ["_op"])) (string op)
395 | otherwise = dotSep (path ++ [op])
400 Var.Module m -> [dolarize m]
402 basicOps = Map.fromList (infixOps ++ specialOps)
405 let infixOp str op = (str, InfixExpr () op) in
410 , infixOp "&&" OpLAnd
415 [ (,) "^" $ \a b -> obj "Math.pow" `call` [a,b]
416 , (,) "|>" $ flip (<|)
417 , (,) "==" $ \a b -> obj "_U.eq" `call` [a,b]
418 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
419 , (,) "<" $ cmp OpLT 0
420 , (,) ">" $ cmp OpGT 0
421 , (,) "<=" $ cmp OpLT 1
422 , (,) ">=" $ cmp OpGT (-1)
423 , (,) "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
426 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