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))
82 return $ _Utils "remove" `call` [ string (Var.varName field), e' ]
84 Insert e field value ->
85 do value' <- expression value
87 return $ _Utils "insert" `call` [ string (Var.varName field), value', e' ]
92 forM fields $ \(field, value) ->
93 do value' <- expression value
94 return $ ArrayLit () [ string (Var.varName field), value' ]
96 return $ _Utils "replace" `call` [ArrayLit () fields', e']
100 forM fields $ \(field, e) ->
101 (,) (Var.varName field) <$> expression e
104 List.foldl' combine Map.empty fields'
106 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
108 combine record (field, value) =
109 Map.insertWith (++) field [value] record
112 ObjectLit () . map (prop *** ArrayLit ()) $
113 Map.toList (Map.filter (not . null) (Map.map tail fs))
116 map (first prop) (Map.toList (Map.map head fs))
119 binop region op e1 e2
121 Lambda pattern rawBody@(A.A ann _) ->
122 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
123 body' <- expression body
125 case length args < 2 || length args > 9 of
126 True -> foldr (==>) body' (map (:[]) args)
127 False -> ref ("F" ++ show (length args)) <| (args ==> body')
129 depattern (args, body) pattern =
132 return (args ++ [ Var.varName x ], body)
135 do arg <- Case.newVar
138 , A.A ann (Case (A.A ann (localVar arg)) [(pattern, body)])
141 (patterns, innerBody) =
142 collect [pattern] rawBody
144 collect patterns lexpr@(A.A _ expr) =
146 Lambda p e -> collect (p:patterns) e
147 _ -> (patterns, lexpr)
150 do func' <- expression func
151 args' <- mapM expression args
154 [arg] -> func' <| arg
155 _ | length args' <= 9 -> ref aN `call` (func':args')
156 | otherwise -> foldl1 (<|) (func':args')
158 aN = "A" ++ show (length args)
159 (func, args) = getArgs e1 [e2]
162 (A.A _ (App f arg)) -> getArgs f (arg : args)
166 do let (defs',e') = flattenLets defs e
167 stmts <- concat <$> mapM definition defs'
169 return $ function [] (stmts ++ [ ret exp ]) `call` []
172 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
174 case last branches of
175 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
177 (A.A _ (Literal (Boolean True)), _) ->
180 ifs branches' (throw "badIf" region)
182 safeIfs branches = ifs (init branches) (snd (last branches))
183 ifs branches finally = foldr iff finally branches
184 iff (if', then') else' = CondExpr () if' then' else'
187 do (tempVar,initialMatch) <- Case.toMatch cases
188 (revisedMatch, stmt) <-
190 A.A _ (Var (Var.Canonical Var.Local x)) ->
191 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
193 do e' <- expression e
194 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
195 match' <- match region revisedMatch
196 return (function [] (stmt ++ match') `call` [])
199 do es' <- mapM expression es
200 return $ _List "fromArray" <| ArrayLit () es'
203 do es' <- mapM expression es
204 return $ ObjectLit () (ctor : fields es')
206 ctor = (prop "ctor", string name)
208 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
210 GLShader _uid src _tipe ->
211 return $ ObjectLit () [(PropString () "src", literal (Str src))]
216 return (Port.inbound name portType)
218 Out name expr portType ->
219 do expr' <- expression expr
220 return (Port.outbound name expr' portType)
222 Task name expr portType ->
223 do expr' <- expression expr
224 return (Port.task name expr' portType)
227definition :: Canonical.Def -> State Int [Statement ()]
228definition (Canonical.Definition annPattern expr@(A.A region _) _) =
229 do expr' <- expression expr
230 let assign x = varDecl x expr'
231 let (A.A patternRegion pattern) = annPattern
235 let op = LBracket () (ref "_op") (string x) in
236 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
239 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
242 let setField f = varDecl f (obj ["$",f]) in
243 return [ VarDeclStmt () (assign "$" : map setField fields) ]
245 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
246 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
248 vars = getVars patterns
251 A.A _ (P.Var x) : rest ->
252 (Var.varName x :) `fmap` getVars rest
258 decl x n = varDecl x (obj ["$","_" ++ show n])
260 | Help.isTuple name = assign "$" : vars
261 | otherwise = assign "_raw" : safeAssign : vars
263 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
264 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
265 exception = Help.throw "badCase" region
268 do defs' <- concat <$> mapM toDef vars
269 return (VarDeclStmt () [assign "_"] : defs')
271 vars = P.boundVarList annPattern
272 mkVar = A.A region . localVar
274 let expr = A.A region $ Case (mkVar "_") [(annPattern, mkVar y)]
275 pat = A.A patternRegion (P.Var y)
277 definition (Canonical.Definition pat expr Nothing)
280match :: R.Region -> Case.Match -> State Int [Statement ()]
283 Case.Match name clauses mtch' ->
284 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
285 mtch'' <- match region mtch'
286 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
290 Case.Clause (Right _) _ _ -> True
294 | any isLiteral clauses = obj [name]
295 | otherwise = obj (Help.splitDots name ++ ["ctor"])
298 | or isChars = InfixExpr () OpAdd e (string "")
302 return [ ExprStmt () (Help.throw "badCase" region) ]
305 return [BreakStmt () Nothing]
308 do e' <- expression e
312 concat <$> mapM (match region) (dropEnd [] ms)
317 Case.Other _ -> acc ++ [m]
318 _ -> dropEnd (acc ++ [m]) ms
321clause :: R.Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
322clause region variable (Case.Clause value vars mtch) =
323 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
326 map (\n -> variable ++ "._" ++ show n) [0..]
330 Right (Chr c) -> (True, string [c])
334 Right (Boolean b) -> BoolLit () b
335 Right lit -> literal lit
336 Left (Var.Canonical _ name) ->
340flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
341flattenLets defs lexpr@(A.A _ expr) =
343 Let ds body -> flattenLets (defs ++ ds) body
347generate :: CanonicalModule -> String
349 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
350 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
353 names = Module.names modul
355 thisModule :: Expression ()
356 thisModule = obj (localRuntime : names ++ ["values"])
358 programStmts :: [Statement ()]
361 [ [ ExprStmt () (string "use strict") ]
362 , setup localRuntime (names ++ ["values"])
363 , [ IfSingleStmt () thisModule (ret thisModule) ]
364 , [ VarDeclStmt () localVars ]
370 localVars :: [VarDecl ()]
372 varDecl "_op" (ObjectLit () [])
373 : internalImports (Module.names modul)
376 explicitImports :: [VarDecl ()]
383 jsImport :: Module.Name -> VarDecl ()
385 varDecl (Var.moduleName name) $
386 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
388 body :: [Statement ()]
390 concat (evalState defs 0)
393 Module.program (Module.body modul)
398 setup namespace path =
402 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
410 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
412 exs = map entry $ "_op" : concatMap extract (exports modul)
413 entry x = (prop x, ref x)
420 | otherwise -> [Var.varName x]
422 Var.Union _ (Var.Listing ctors _) ->
423 map Var.varName ctors
427 [x] -> VarDeclStmt () [ varDecl x expr ]
430 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
438 -> State Int (Expression ())
439binop region func@(Var.Canonical home op) e1 e2 =
441 (Var.Module ["Basics"], ">>") ->
442 do es <- mapM expression (collectLeftAssoc [e2] e1)
443 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
445 (Var.Module ["Basics"], "<<") ->
446 do es <- mapM expression (e1 : collectRightAssoc [] e2)
447 return $ ["$"] ==> foldr (<|) (ref "$") es
449 (Var.Module ["Basics"], "<|") ->
450 do e2' <- expression e2
451 es <- mapM expression (collectRightAssoc [] e1)
452 return $ foldr (<|) e2' es
454 (Var.BuiltIn, "::") ->
455 expression (A.A region (Data "::" [e1,e2]))
457 (Var.Module ["Basics"], _) ->
458 do e1' <- expression e1
460 case Map.lookup op basicOps of
465 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
468 do e1' <- expression e1
470 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
473 collectRightAssoc es e =
475 A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
476 collectRightAssoc (es ++ [e1]) e2
479 collectLeftAssoc es e =
481 A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
482 collectLeftAssoc (e2 : es) e1
486 Map.fromList (infixOps ++ specialOps)
489 let infixOp str op = (str, InfixExpr () op) in
494 , infixOp "&&" OpLAnd
499 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
500 , (,) "|>" $ flip (<|)
501 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
502 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
503 , (,) "<" $ cmp OpLT 0
504 , (,) ">" $ cmp OpGT 0
505 , (,) "<=" $ cmp OpLT 1
506 , (,) ">=" $ cmp OpGT (-1)
507 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
511 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