1module Generate.JavaScript (generate) where
3import Control.Applicative ((<$>),(<*>))
4import Control.Arrow (first,(***))
5import Control.Monad.State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.Set as Set
9import Language.ECMAScript3.PrettyPrint
10import Language.ECMAScript3.Syntax
12import Generate.JavaScript.Helpers as Help
13import qualified Generate.Cases as Case
14import qualified Generate.JavaScript.Ports as Port
15import qualified Generate.JavaScript.Variable as Var
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 qualified AST.Variable as Var
28varDecl :: String -> Expression () -> VarDecl ()
30 VarDecl () (var x) (Just expr)
32internalImports :: Module.Name -> [VarDecl ()]
34 [ varDecl "_N" (obj ["Elm","Native"])
35 , include "_U" "Utils"
37 , include "_P" "Ports"
38 , varDecl Help.localModuleName (string (Module.nameToString name))
41 include :: String -> String -> VarDecl ()
43 varDecl alias (obj ["_N", modul, "make"] <| ref "_elm")
45_Utils :: String -> Expression ()
46_Utils x = obj ["_U", x]
48_List :: String -> Expression ()
49_List x = obj ["_L", x]
51literal :: Literal -> Expression ()
54 Chr c -> _Utils "chr" <| string [c]
56 IntNum n -> IntLit () n
57 FloatNum n -> NumLit () n
58 Boolean b -> BoolLit () b
60expression :: Canonical.Expr -> State Int (Expression ())
61expression (A region expr) =
63 Var var -> return $ Var.canonical var
65 Literal lit -> return $ literal lit
68 do lo' <- expression lo
70 return $ _List "range" `call` [lo',hi']
74 return $ DotRef () e' (var x)
78 return $ _Utils "remove" `call` [string x, e']
83 return $ _Utils "insert" `call` [string x, v', e']
87 fs' <- forM fs $ \(f,v) -> do
89 return $ ArrayLit () [string f, v']
90 return $ _Utils "replace" `call` [ArrayLit () fs', e']
93 do fields' <- forM fields $ \(f,e) -> do
94 (,) f <$> expression e
95 let fieldMap = List.foldl' combine Map.empty fields'
96 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
98 combine r (k,v) = Map.insertWith (++) k [v] r
99 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
100 Map.toList . Map.filter (not . null) $ Map.map tail fs
101 visible fs = map (first prop) . Map.toList $ Map.map head fs
103 Binop op e1 e2 -> binop region op e1 e2
105 Lambda p e@(A ann _) ->
106 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
107 body' <- expression body
108 return $ case length args < 2 || length args > 9 of
109 True -> foldr (==>) body' (map (:[]) args)
110 False -> ref ("F" ++ show (length args)) <| (args ==> body')
112 depattern (args, body) pattern =
114 P.Var x -> return (args ++ [ Var.varName x ], body)
115 _ -> do arg <- Case.newVar
116 return ( args ++ [arg]
117 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
119 (patterns, innerBody) = collect [p] e
121 collect patterns lexpr@(A _ expr) =
123 Lambda p e -> collect (p:patterns) e
124 _ -> (patterns, lexpr)
127 do func' <- expression func
128 args' <- mapM expression args
129 return $ case args' of
130 [arg] -> func' <| arg
131 _ | length args' <= 9 -> ref aN `call` (func':args')
132 | otherwise -> foldl1 (<|) (func':args')
134 aN = "A" ++ show (length args)
135 (func, args) = getArgs e1 [e2]
138 (A _ (App f arg)) -> getArgs f (arg : args)
142 do let (defs',e') = flattenLets defs e
143 stmts <- concat <$> mapM definition defs'
145 return $ function [] (stmts ++ [ ret exp ]) `call` []
148 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
149 return $ case last branches of
150 (A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
152 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
153 _ -> ifs branches' (throw "badIf" region)
155 safeIfs branches = ifs (init branches) (snd (last branches))
156 ifs branches finally = foldr iff finally branches
157 iff (if', then') else' = CondExpr () if' then' else'
160 do (tempVar,initialMatch) <- Case.toMatch cases
161 (revisedMatch, stmt) <-
163 A _ (Var (Var.Canonical Var.Local x)) ->
164 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
166 do e' <- expression e
167 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
168 match' <- match region revisedMatch
169 return (function [] (stmt ++ match') `call` [])
172 do es' <- mapM expression es
173 return $ _List "fromArray" <| ArrayLit () es'
176 do es' <- mapM expression es
177 return $ ObjectLit () (ctor : fields es')
179 ctor = (prop "ctor", string name)
180 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
183 return $ Var.value ["Text"] "markdown" `call` [ string md, string uid ]
185 pad = "<div style=\"height:0;width:0;\"> </div>"
186 md = pad ++ MD.toHtml doc ++ pad
188 GLShader _uid src _tipe ->
189 return $ ObjectLit () [(PropString () "src", literal (Str src))]
192 return $ obj ["_P","portIn"] `call`
193 [ string name, Port.incoming tipe ]
195 PortOut name tipe value ->
196 do value' <- expression value
197 return $ obj ["_P","portOut"] `call`
198 [ string name, Port.outgoing tipe, value' ]
200definition :: Canonical.Def -> State Int [Statement ()]
201definition (Canonical.Definition pattern expr@(A region _) _) = do
202 expr' <- expression expr
203 let assign x = varDecl x expr'
207 let op = LBracket () (ref "_op") (string x) in
208 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
210 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
213 let setField f = varDecl f (obj ["$",f]) in
214 return [ VarDeclStmt () (assign "$" : map setField fields) ]
216 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
217 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
219 vars = getVars patterns
222 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
226 decl x n = varDecl x (obj ["$","_" ++ show n])
228 | Help.isTuple name = assign "$" : vars
229 | otherwise = assign "_raw" : safeAssign : vars
231 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
232 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
233 exception = Help.throw "badCase" region
236 do defs' <- concat <$> mapM toDef vars
237 return (VarDeclStmt () [assign "_"] : defs')
239 vars = P.boundVarList pattern
240 mkVar = A region . localVar
241 toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
242 in definition $ Canonical.Definition (P.Var y) expr Nothing
244match :: Region -> Case.Match -> State Int [Statement ()]
247 Case.Match name clauses mtch' ->
248 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
249 mtch'' <- match region mtch'
250 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
252 isLiteral p = case p of
253 Case.Clause (Right _) _ _ -> True
257 | any isLiteral clauses = obj [name]
258 | otherwise = obj (Help.splitDots name ++ ["ctor"])
261 | or isChars = InfixExpr () OpAdd e (string "")
265 return [ ExprStmt () (Help.throw "badCase" region) ]
267 Case.Break -> return [BreakStmt () Nothing]
270 do e' <- expression e
273 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
278 Case.Other _ -> acc ++ [m]
279 _ -> dropEnd (acc ++ [m]) ms
281clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
282clause region variable (Case.Clause value vars mtch) =
283 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
285 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
288 Right (Chr c) -> (True, string [c])
289 _ -> (,) False $ case value of
290 Right (Boolean b) -> BoolLit () b
291 Right lit -> literal lit
292 Left (Var.Canonical _ name) ->
295flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
296flattenLets defs lexpr@(A _ expr) =
298 Let ds body -> flattenLets (defs ++ ds) body
301generate :: CanonicalModule -> String
303 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
304 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
307 names = Module.names modul
309 thisModule :: Expression ()
310 thisModule = obj ("_elm" : names ++ ["values"])
312 programStmts :: [Statement ()]
315 [ [ ExprStmt () (string "use strict") ]
316 , setup "_elm" (names ++ ["values"])
317 , [ IfSingleStmt () thisModule (ret thisModule) ]
318 , [ VarDeclStmt () localVars ]
324 localVars :: [VarDecl ()]
325 localVars = varDecl "_op" (ObjectLit () []) :
326 internalImports (Module.names modul) ++
329 explicitImports :: [VarDecl ()]
331 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
333 jsImport :: Module.Name -> VarDecl ()
335 varDecl (Var.moduleName name) $
336 obj ("Elm" : name ++ ["make"]) <| ref "_elm"
338 body :: [Statement ()]
339 body = concat $ evalState defs 0
341 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
343 setup namespace path = map create paths
345 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
346 paths = drop 2 . init . List.inits $ namespace : path
348 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
350 exs = map entry $ "_op" : concatMap extract (exports modul)
351 entry x = (prop x, ref x)
355 Var.Value x | Help.isOp x -> []
356 | otherwise -> [Var.varName x]
357 Var.ADT _ (Var.Listing ctors _) -> map Var.varName ctors
361 [x] -> VarDeclStmt () [ varDecl x expr ]
363 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
365binop :: Region -> Var.Canonical -> Canonical.Expr -> Canonical.Expr
366 -> State Int (Expression ())
367binop region func@(Var.Canonical home op) e1 e2 =
369 (Var.Module ["Basics"], ">>") ->
370 do es <- mapM expression (collectLeftAssoc [e2] e1)
371 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
373 (Var.Module ["Basics"], "<<") ->
374 do es <- mapM expression (e1 : collectRightAssoc [] e2)
375 return $ ["$"] ==> foldr (<|) (ref "$") es
377 (Var.Module ["Basics"], "<|") ->
378 do e2' <- expression e2
379 es <- mapM expression (collectRightAssoc [] e1)
380 return $ foldr (<|) e2' es
382 (Var.BuiltIn, "::") ->
383 expression (A region (Data "::" [e1,e2]))
385 (Var.Module ["Basics"], _) ->
386 do e1' <- expression e1
388 return $ case Map.lookup op basicOps of
390 Nothing -> ref "A2" `call` [ Var.canonical func, e1', e2' ]
393 do e1' <- expression e1
395 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
398 collectRightAssoc es e =
400 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
401 collectRightAssoc (es ++ [e1]) e2
404 collectLeftAssoc es e =
406 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
407 collectLeftAssoc (e2 : es) e1
410 basicOps = Map.fromList (infixOps ++ specialOps)
413 let infixOp str op = (str, InfixExpr () op) in
418 , infixOp "&&" OpLAnd
423 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
424 , (,) "|>" $ flip (<|)
425 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
426 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
427 , (,) "<" $ cmp OpLT 0
428 , (,) ">" $ cmp OpGT 0
429 , (,) "<=" $ cmp OpLT 1
430 , (,) ">=" $ cmp OpGT (-1)
431 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
434 cmp op n a b = InfixExpr () op (_Utils "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