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
29varDecl :: String -> Expression () -> VarDecl ()
31 VarDecl () (var x) (Just expr)
34internalImports :: Module.Name -> [VarDecl ()]
36 [ varDecl "_N" (obj ["Elm","Native"])
37 , include "_U" "Utils"
39 , include "_P" "Ports"
40 , varDecl Help.localModuleName (string (Module.nameToString name))
43 include :: String -> String -> VarDecl ()
45 varDecl alias (obj ["_N", modul, "make"] <| ref "_elm")
48_Utils :: String -> Expression ()
53_List :: String -> Expression ()
58literal :: Literal -> Expression ()
61 Chr c -> _Utils "chr" <| string [c]
63 IntNum n -> IntLit () n
64 FloatNum n -> NumLit () n
65 Boolean b -> BoolLit () b
68expression :: Canonical.Expr -> State Int (Expression ())
69expression (A region expr) =
71 Var var -> return $ Var.canonical var
73 Literal lit -> return $ literal lit
76 do lo' <- expression lo
78 return $ _List "range" `call` [lo',hi']
82 return $ DotRef () e' (var x)
86 return $ _Utils "remove" `call` [string x, e']
91 return $ _Utils "insert" `call` [string x, v', e']
95 fs' <- forM fs $ \(f,v) -> do
97 return $ ArrayLit () [string f, v']
98 return $ _Utils "replace" `call` [ArrayLit () fs', e']
101 do fields' <- forM fields $ \(f,e) -> do
102 (,) f <$> expression e
103 let fieldMap = List.foldl' combine Map.empty fields'
104 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
106 combine r (k,v) = Map.insertWith (++) k [v] r
107 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
108 Map.toList . Map.filter (not . null) $ Map.map tail fs
109 visible fs = map (first prop) . Map.toList $ Map.map head fs
111 Binop op e1 e2 -> binop region op e1 e2
113 Lambda p e@(A ann _) ->
114 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
115 body' <- expression body
116 return $ case length args < 2 || length args > 9 of
117 True -> foldr (==>) body' (map (:[]) args)
118 False -> ref ("F" ++ show (length args)) <| (args ==> body')
120 depattern (args, body) pattern =
122 P.Var x -> return (args ++ [ Var.varName x ], body)
123 _ -> do arg <- Case.newVar
124 return ( args ++ [arg]
125 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
127 (patterns, innerBody) = collect [p] e
129 collect patterns lexpr@(A _ expr) =
131 Lambda p e -> collect (p:patterns) e
132 _ -> (patterns, lexpr)
135 do func' <- expression func
136 args' <- mapM expression args
137 return $ case args' of
138 [arg] -> func' <| arg
139 _ | length args' <= 9 -> ref aN `call` (func':args')
140 | otherwise -> foldl1 (<|) (func':args')
142 aN = "A" ++ show (length args)
143 (func, args) = getArgs e1 [e2]
146 (A _ (App f arg)) -> getArgs f (arg : args)
150 do let (defs',e') = flattenLets defs e
151 stmts <- concat <$> mapM definition defs'
153 return $ function [] (stmts ++ [ ret exp ]) `call` []
156 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
157 return $ case last branches of
158 (A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
160 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
161 _ -> ifs branches' (throw "badIf" region)
163 safeIfs branches = ifs (init branches) (snd (last branches))
164 ifs branches finally = foldr iff finally branches
165 iff (if', then') else' = CondExpr () if' then' else'
168 do (tempVar,initialMatch) <- Case.toMatch cases
169 (revisedMatch, stmt) <-
171 A _ (Var (Var.Canonical Var.Local x)) ->
172 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
174 do e' <- expression e
175 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
176 match' <- match region revisedMatch
177 return (function [] (stmt ++ match') `call` [])
180 do es' <- mapM expression es
181 return $ _List "fromArray" <| ArrayLit () es'
184 do es' <- mapM expression es
185 return $ ObjectLit () (ctor : fields es')
187 ctor = (prop "ctor", string name)
188 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
191 return $ Var.value ["Text"] "markdown" `call` [ string md, string uid ]
193 pad = "<div style=\"height:0;width:0;\"> </div>"
194 md = pad ++ MD.toHtml doc ++ pad
196 GLShader _uid src _tipe ->
197 return $ ObjectLit () [(PropString () "src", literal (Str src))]
200 return $ obj ["_P","portIn"] `call`
201 [ string name, Port.incoming tipe ]
203 PortOut name tipe value ->
204 do value' <- expression value
205 return $ obj ["_P","portOut"] `call`
206 [ string name, Port.outgoing tipe, value' ]
209definition :: Canonical.Def -> State Int [Statement ()]
210definition (Canonical.Definition pattern expr@(A region _) _) = do
211 expr' <- expression expr
212 let assign x = varDecl x expr'
216 let op = LBracket () (ref "_op") (string x) in
217 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
219 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
222 let setField f = varDecl f (obj ["$",f]) in
223 return [ VarDeclStmt () (assign "$" : map setField fields) ]
225 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
226 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
228 vars = getVars patterns
231 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
235 decl x n = varDecl x (obj ["$","_" ++ show n])
237 | Help.isTuple name = assign "$" : vars
238 | otherwise = assign "_raw" : safeAssign : vars
240 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
241 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
242 exception = Help.throw "badCase" region
245 do defs' <- concat <$> mapM toDef vars
246 return (VarDeclStmt () [assign "_"] : defs')
248 vars = P.boundVarList pattern
249 mkVar = A region . localVar
251 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
253 definition $ Canonical.Definition (P.Var y) expr Nothing
256match :: Region -> Case.Match -> State Int [Statement ()]
259 Case.Match name clauses mtch' ->
260 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
261 mtch'' <- match region mtch'
262 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
264 isLiteral p = case p of
265 Case.Clause (Right _) _ _ -> True
269 | any isLiteral clauses = obj [name]
270 | otherwise = obj (Help.splitDots name ++ ["ctor"])
273 | or isChars = InfixExpr () OpAdd e (string "")
277 return [ ExprStmt () (Help.throw "badCase" region) ]
279 Case.Break -> return [BreakStmt () Nothing]
282 do e' <- expression e
285 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
290 Case.Other _ -> acc ++ [m]
291 _ -> dropEnd (acc ++ [m]) ms
294clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
295clause region variable (Case.Clause value vars mtch) =
296 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
298 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
301 Right (Chr c) -> (True, string [c])
305 Right (Boolean b) -> BoolLit () b
306 Right lit -> literal lit
307 Left (Var.Canonical _ name) ->
311flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
312flattenLets defs lexpr@(A _ expr) =
314 Let ds body -> flattenLets (defs ++ ds) body
318generate :: CanonicalModule -> String
320 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
321 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
324 names = Module.names modul
326 thisModule :: Expression ()
327 thisModule = obj ("_elm" : names ++ ["values"])
329 programStmts :: [Statement ()]
332 [ [ ExprStmt () (string "use strict") ]
333 , setup "_elm" (names ++ ["values"])
334 , [ IfSingleStmt () thisModule (ret thisModule) ]
335 , [ VarDeclStmt () localVars ]
341 localVars :: [VarDecl ()]
342 localVars = varDecl "_op" (ObjectLit () []) :
343 internalImports (Module.names modul) ++
346 explicitImports :: [VarDecl ()]
348 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
350 jsImport :: Module.Name -> VarDecl ()
352 varDecl (Var.moduleName name) $
353 obj ("Elm" : name ++ ["make"]) <| ref "_elm"
355 body :: [Statement ()]
356 body = concat $ evalState defs 0
358 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
360 setup namespace path = map create paths
362 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
363 paths = drop 2 . init . List.inits $ namespace : path
365 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
367 exs = map entry $ "_op" : concatMap extract (exports modul)
368 entry x = (prop x, ref x)
375 | otherwise -> [Var.varName x]
377 Var.Union _ (Var.Listing ctors _) ->
378 map Var.varName ctors
382 [x] -> VarDeclStmt () [ varDecl x expr ]
385 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
393 -> State Int (Expression ())
394binop region func@(Var.Canonical home op) e1 e2 =
396 (Var.Module ["Basics"], ">>") ->
397 do es <- mapM expression (collectLeftAssoc [e2] e1)
398 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
400 (Var.Module ["Basics"], "<<") ->
401 do es <- mapM expression (e1 : collectRightAssoc [] e2)
402 return $ ["$"] ==> foldr (<|) (ref "$") es
404 (Var.Module ["Basics"], "<|") ->
405 do e2' <- expression e2
406 es <- mapM expression (collectRightAssoc [] e1)
407 return $ foldr (<|) e2' es
409 (Var.BuiltIn, "::") ->
410 expression (A region (Data "::" [e1,e2]))
412 (Var.Module ["Basics"], _) ->
413 do e1' <- expression e1
415 case Map.lookup op basicOps of
420 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
423 do e1' <- expression e1
425 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
428 collectRightAssoc es e =
430 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
431 collectRightAssoc (es ++ [e1]) e2
434 collectLeftAssoc es e =
436 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
437 collectLeftAssoc (e2 : es) e1
441 Map.fromList (infixOps ++ specialOps)
444 let infixOp str op = (str, InfixExpr () op) in
449 , infixOp "&&" OpLAnd
454 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
455 , (,) "|>" $ flip (<|)
456 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
457 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
458 , (,) "<" $ cmp OpLT 0
459 , (,) ">" $ cmp OpGT 0
460 , (,) "<=" $ cmp OpLT 1
461 , (,) ">=" $ cmp OpGT (-1)
462 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
466 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