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
13import AST.Expression.General
14import qualified AST.Expression.Canonical as Canonical
15import qualified AST.Helpers as Help
17import qualified AST.Module as Module
18import qualified AST.Pattern as P
19import qualified AST.Variable as Var
20import Elm.Utils ((|>))
21import Generate.JavaScript.Helpers as Help
22import qualified Generate.Cases as Case
23import qualified Generate.JavaScript.Port as Port
24import qualified Generate.JavaScript.Variable as Var
25import qualified Reporting.Annotation as A
26import qualified Reporting.Region as R
29internalImports :: Module.Name -> [VarDecl ()]
31 [ varDecl "_N" (obj ["Elm","Native"])
32 , include "_U" "Utils"
34 , varDecl Help.localModuleName (string (Module.nameToString name))
37 include :: String -> String -> VarDecl ()
39 varDecl alias (Help.make ["_N", modul])
42_Utils :: String -> Expression ()
47_List :: String -> Expression ()
52literal :: Literal -> Expression ()
55 Chr c -> _Utils "chr" <| string [c]
57 IntNum n -> IntLit () n
58 FloatNum n -> NumLit () n
59 Boolean b -> BoolLit () b
62expression :: Canonical.Expr -> State Int (Expression ())
63expression (A.A region expr) =
66 return $ Var.canonical var
72 do lo' <- expression lo
74 return $ _List "range" `call` [lo',hi']
78 return $ DotRef () e' (var (Var.varName field))
83 forM fields $ \(field, value) ->
84 do value' <- expression value
85 return $ ArrayLit () [ string (Var.varName field), value' ]
87 return $ _Utils "replace" `call` [ArrayLit () fields', e']
91 forM fields $ \(field, e) ->
92 (,) (Var.varName field) <$> expression e
95 List.foldl' combine Map.empty fields'
97 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
99 combine record (field, value) =
100 Map.insertWith (++) field [value] record
103 ObjectLit () . map (prop *** ArrayLit ()) $
104 Map.toList (Map.filter (not . null) (Map.map tail fs))
107 map (first prop) (Map.toList (Map.map head fs))
110 binop region op e1 e2
112 Lambda pattern rawBody@(A.A ann _) ->
113 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
114 body' <- expression body
116 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 =
123 return (args ++ [ Var.varName x ], body)
126 do arg <- Case.newVar
129 , A.A ann (Case (A.A ann (localVar arg)) [(pattern, body)])
132 (patterns, innerBody) =
133 collect [pattern] rawBody
135 collect patterns lexpr@(A.A _ expr) =
137 Lambda p e -> collect (p:patterns) e
138 _ -> (patterns, lexpr)
141 do func' <- expression func
142 args' <- mapM expression args
145 [arg] -> func' <| arg
146 _ | length args' <= 9 -> ref aN `call` (func':args')
147 | otherwise -> foldl1 (<|) (func':args')
149 aN = "A" ++ show (length args)
150 (func, args) = getArgs e1 [e2]
153 (A.A _ (App f arg)) -> getArgs f (arg : args)
157 do let (defs',e') = flattenLets defs e
158 stmts <- concat <$> mapM definition defs'
160 return $ function [] (stmts ++ [ ret exp ]) `call` []
163 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
165 case last branches of
166 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
168 (A.A _ (Literal (Boolean True)), _) ->
171 ifs branches' (throw "badIf" region)
173 safeIfs branches = ifs (init branches) (snd (last branches))
174 ifs branches finally = foldr iff finally branches
175 iff (if', then') else' = CondExpr () if' then' else'
178 do (tempVar,initialMatch) <- Case.toMatch cases
179 (revisedMatch, stmt) <-
181 A.A _ (Var (Var.Canonical Var.Local x)) ->
182 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
184 do e' <- expression e
185 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
186 match' <- match region revisedMatch
187 return (function [] (stmt ++ match') `call` [])
190 do es' <- mapM expression es
191 return $ _List "fromArray" <| ArrayLit () es'
194 do es' <- mapM expression es
195 return $ ObjectLit () (ctor : fields es')
197 ctor = (prop "ctor", string name)
199 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
201 GLShader _uid src _tipe ->
202 return $ ObjectLit () [(PropString () "src", literal (Str src))]
207 return (Port.inbound name portType)
209 Out name expr portType ->
210 do expr' <- expression expr
211 return (Port.outbound name expr' portType)
213 Task name expr portType ->
214 do expr' <- expression expr
215 return (Port.task name expr' portType)
218definition :: Canonical.Def -> State Int [Statement ()]
219definition (Canonical.Definition annPattern expr@(A.A region _) _) =
220 do expr' <- expression expr
221 let assign x = varDecl x expr'
222 let (A.A patternRegion pattern) = annPattern
226 let op = LBracket () (ref "_op") (string x) in
227 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
230 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
233 let setField f = varDecl f (obj ["$",f]) in
234 return [ VarDeclStmt () (assign "$" : map setField fields) ]
236 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
237 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
239 vars = getVars patterns
242 A.A _ (P.Var x) : rest ->
243 (Var.varName x :) `fmap` getVars rest
249 decl x n = varDecl x (obj ["$","_" ++ show n])
251 | Help.isTuple name = assign "$" : vars
252 | otherwise = assign "_raw" : safeAssign : vars
254 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
255 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
256 exception = Help.throw "badCase" region
259 do defs' <- concat <$> mapM toDef vars
260 return (VarDeclStmt () [assign "_"] : defs')
262 vars = P.boundVarList annPattern
263 mkVar = A.A region . localVar
265 let expr = A.A region $ Case (mkVar "_") [(annPattern, mkVar y)]
266 pat = A.A patternRegion (P.Var y)
268 definition (Canonical.Definition pat expr Nothing)
271match :: R.Region -> Case.Match -> State Int [Statement ()]
274 Case.Match name clauses mtch' ->
275 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
276 mtch'' <- match region mtch'
277 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
281 Case.Clause (Right _) _ _ -> True
285 | any isLiteral clauses = obj [name]
286 | otherwise = obj (Help.splitDots name ++ ["ctor"])
289 | or isChars = InfixExpr () OpAdd e (string "")
293 return [ ExprStmt () (Help.throw "badCase" region) ]
296 return [BreakStmt () Nothing]
299 do e' <- expression e
303 concat <$> mapM (match region) (dropEnd [] ms)
308 Case.Other _ -> acc ++ [m]
309 _ -> dropEnd (acc ++ [m]) ms
312clause :: R.Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
313clause region variable (Case.Clause value vars mtch) =
314 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
317 map (\n -> variable ++ "._" ++ show n) [0..]
321 Right (Chr c) -> (True, string [c])
325 Right (Boolean b) -> BoolLit () b
326 Right lit -> literal lit
327 Left (Var.Canonical _ name) ->
331flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
332flattenLets defs lexpr@(A.A _ expr) =
334 Let ds body -> flattenLets (defs ++ ds) body
338generate :: Module.CanonicalModule -> String
340 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
341 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
344 names = Module.names modul
346 thisModule :: Expression ()
347 thisModule = obj (localRuntime : names ++ ["values"])
349 programStmts :: [Statement ()]
352 [ [ ExprStmt () (string "use strict") ]
353 , setup localRuntime (names ++ ["values"])
354 , [ IfSingleStmt () thisModule (ret thisModule) ]
355 , [ VarDeclStmt () localVars ]
361 localVars :: [VarDecl ()]
363 varDecl "_op" (ObjectLit () [])
364 : internalImports (Module.names modul)
367 explicitImports :: [VarDecl ()]
374 jsImport :: Module.Name -> VarDecl ()
376 varDecl (Var.moduleName name) $
377 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
379 body :: [Statement ()]
381 concat (evalState defs 0)
384 Module.program (Module.body modul)
389 setup namespace path =
393 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
401 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
403 exs = map entry $ "_op" : concatMap extract (exports modul)
404 entry x = (prop x, ref x)
411 | otherwise -> [Var.varName x]
413 Var.Union _ (Var.Listing ctors _) ->
414 map Var.varName ctors
418 [x] -> VarDeclStmt () [ varDecl x expr ]
421 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
429 -> State Int (Expression ())
430binop region func@(Var.Canonical home op) e1 e2 =
432 (Var.Module ["Basics"], ">>") ->
433 do es <- mapM expression (collectLeftAssoc [e2] e1)
434 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
436 (Var.Module ["Basics"], "<<") ->
437 do es <- mapM expression (e1 : collectRightAssoc [] e2)
438 return $ ["$"] ==> foldr (<|) (ref "$") es
440 (Var.Module ["Basics"], "<|") ->
441 do e2' <- expression e2
442 es <- mapM expression (collectRightAssoc [] e1)
443 return $ foldr (<|) e2' es
445 (Var.BuiltIn, "::") ->
446 expression (A.A region (Data "::" [e1,e2]))
448 (Var.Module ["Basics"], _) ->
449 do e1' <- expression e1
451 case Map.lookup op basicOps of
456 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
459 do e1' <- expression e1
461 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
464 collectRightAssoc es e =
466 A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
467 collectRightAssoc (es ++ [e1]) e2
470 collectLeftAssoc es e =
472 A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
473 collectLeftAssoc (e2 : es) e1
477 Map.fromList (infixOps ++ specialOps)
480 let infixOp str op = (str, InfixExpr () op) in
485 , infixOp "&&" OpLAnd
490 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
491 , (,) "|>" $ flip (<|)
492 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
493 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
494 , (,) "<" $ cmp OpLT 0
495 , (,) ">" $ cmp OpGT 0
496 , (,) "<=" $ cmp OpLT 1
497 , (,) ">=" $ cmp OpGT (-1)
498 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
502 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