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
19import AST.Expression.General
20import qualified AST.Expression.Canonical as Canonical
21import qualified AST.Module as Module
22import qualified AST.Helpers as Help
24import qualified AST.Pattern as P
25import qualified AST.Variable as Var
28internalImports :: Module.Name -> [VarDecl ()]
30 [ varDecl "_N" (obj ["Elm","Native"])
31 , include "_U" "Utils"
33 , include "_P" "Ports"
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 region expr) =
65 Var var -> return $ Var.canonical var
67 Literal lit -> return $ literal lit
70 do lo' <- expression lo
72 return $ _List "range" `call` [lo',hi']
76 return $ DotRef () e' (var (Var.varName field))
80 return $ _Utils "remove" `call` [ string (Var.varName field), e' ]
82 Insert e field value ->
83 do value' <- expression value
85 return $ _Utils "insert" `call` [ string (Var.varName field), value', e' ]
90 forM fields $ \(field, value) ->
91 do value' <- expression value
92 return $ ArrayLit () [ string (Var.varName field), value' ]
94 return $ _Utils "replace" `call` [ArrayLit () fields', e']
98 forM fields $ \(field, e) ->
99 (,) (Var.varName field) <$> expression e
102 List.foldl' combine Map.empty fields'
104 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
106 combine record (field, value) =
107 Map.insertWith (++) field [value] record
110 ObjectLit () . map (prop *** ArrayLit ()) $
111 Map.toList (Map.filter (not . null) (Map.map tail fs))
114 map (first prop) (Map.toList (Map.map head fs))
116 Binop op e1 e2 -> binop region op e1 e2
118 Lambda p e@(A ann _) ->
119 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
120 body' <- expression body
121 return $ case length args < 2 || length args > 9 of
122 True -> foldr (==>) body' (map (:[]) args)
123 False -> ref ("F" ++ show (length args)) <| (args ==> body')
125 depattern (args, body) pattern =
127 P.Var x -> return (args ++ [ Var.varName x ], body)
128 _ -> do arg <- Case.newVar
129 return ( args ++ [arg]
130 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
132 (patterns, innerBody) = collect [p] e
134 collect patterns lexpr@(A _ expr) =
136 Lambda p e -> collect (p:patterns) e
137 _ -> (patterns, lexpr)
140 do func' <- expression func
141 args' <- mapM expression args
142 return $ case args' of
143 [arg] -> func' <| arg
144 _ | length args' <= 9 -> ref aN `call` (func':args')
145 | otherwise -> foldl1 (<|) (func':args')
147 aN = "A" ++ show (length args)
148 (func, args) = getArgs e1 [e2]
151 (A _ (App f arg)) -> getArgs f (arg : args)
155 do let (defs',e') = flattenLets defs e
156 stmts <- concat <$> mapM definition defs'
158 return $ function [] (stmts ++ [ ret exp ]) `call` []
161 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
162 return $ case last branches of
163 (A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
165 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
166 _ -> ifs branches' (throw "badIf" region)
168 safeIfs branches = ifs (init branches) (snd (last branches))
169 ifs branches finally = foldr iff finally branches
170 iff (if', then') else' = CondExpr () if' then' else'
173 do (tempVar,initialMatch) <- Case.toMatch cases
174 (revisedMatch, stmt) <-
176 A _ (Var (Var.Canonical Var.Local x)) ->
177 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
179 do e' <- expression e
180 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
181 match' <- match region revisedMatch
182 return (function [] (stmt ++ match') `call` [])
185 do es' <- mapM expression es
186 return $ _List "fromArray" <| ArrayLit () es'
189 do es' <- mapM expression es
190 return $ ObjectLit () (ctor : fields es')
192 ctor = (prop "ctor", string name)
193 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
195 GLShader _uid src _tipe ->
196 return $ ObjectLit () [(PropString () "src", literal (Str src))]
199 return $ obj ["_P","portIn"] `call`
200 [ string name, Port.incoming tipe ]
202 PortOut name tipe value ->
203 do value' <- expression value
204 return $ obj ["_P","portOut"] `call`
205 [ string name, Port.outgoing tipe, value' ]
208definition :: Canonical.Def -> State Int [Statement ()]
209definition (Canonical.Definition pattern expr@(A region _) _) = do
210 expr' <- expression expr
211 let assign x = varDecl x expr'
215 let op = LBracket () (ref "_op") (string x) in
216 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
218 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
221 let setField f = varDecl f (obj ["$",f]) in
222 return [ VarDeclStmt () (assign "$" : map setField fields) ]
224 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
225 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
227 vars = getVars patterns
230 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
234 decl x n = varDecl x (obj ["$","_" ++ show n])
236 | Help.isTuple name = assign "$" : vars
237 | otherwise = assign "_raw" : safeAssign : vars
239 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
240 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
241 exception = Help.throw "badCase" region
244 do defs' <- concat <$> mapM toDef vars
245 return (VarDeclStmt () [assign "_"] : defs')
247 vars = P.boundVarList pattern
248 mkVar = A region . localVar
250 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
252 definition $ Canonical.Definition (P.Var y) expr Nothing
255match :: Region -> Case.Match -> State Int [Statement ()]
258 Case.Match name clauses mtch' ->
259 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
260 mtch'' <- match region mtch'
261 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
263 isLiteral p = case p of
264 Case.Clause (Right _) _ _ -> True
268 | any isLiteral clauses = obj [name]
269 | otherwise = obj (Help.splitDots name ++ ["ctor"])
272 | or isChars = InfixExpr () OpAdd e (string "")
276 return [ ExprStmt () (Help.throw "badCase" region) ]
278 Case.Break -> return [BreakStmt () Nothing]
281 do e' <- expression e
284 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
289 Case.Other _ -> acc ++ [m]
290 _ -> dropEnd (acc ++ [m]) ms
293clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
294clause region variable (Case.Clause value vars mtch) =
295 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
297 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
300 Right (Chr c) -> (True, string [c])
304 Right (Boolean b) -> BoolLit () b
305 Right lit -> literal lit
306 Left (Var.Canonical _ name) ->
310flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
311flattenLets defs lexpr@(A _ expr) =
313 Let ds body -> flattenLets (defs ++ ds) body
317generate :: CanonicalModule -> String
319 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
320 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
323 names = Module.names modul
325 thisModule :: Expression ()
326 thisModule = obj (localRuntime : names ++ ["values"])
328 programStmts :: [Statement ()]
331 [ [ ExprStmt () (string "use strict") ]
332 , setup localRuntime (names ++ ["values"])
333 , [ IfSingleStmt () thisModule (ret thisModule) ]
334 , [ VarDeclStmt () localVars ]
340 localVars :: [VarDecl ()]
341 localVars = varDecl "_op" (ObjectLit () []) :
342 internalImports (Module.names modul) ++
345 explicitImports :: [VarDecl ()]
347 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
349 jsImport :: Module.Name -> VarDecl ()
351 varDecl (Var.moduleName name) $
352 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
354 body :: [Statement ()]
355 body = concat $ evalState defs 0
357 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
359 setup namespace path = map create paths
361 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
362 paths = drop 2 . init . List.inits $ namespace : path
364 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
366 exs = map entry $ "_op" : concatMap extract (exports modul)
367 entry x = (prop x, ref x)
374 | otherwise -> [Var.varName x]
376 Var.Union _ (Var.Listing ctors _) ->
377 map Var.varName ctors
381 [x] -> VarDeclStmt () [ varDecl x expr ]
384 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
392 -> State Int (Expression ())
393binop region func@(Var.Canonical home op) e1 e2 =
395 (Var.Module ["Basics"], ">>") ->
396 do es <- mapM expression (collectLeftAssoc [e2] e1)
397 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
399 (Var.Module ["Basics"], "<<") ->
400 do es <- mapM expression (e1 : collectRightAssoc [] e2)
401 return $ ["$"] ==> foldr (<|) (ref "$") es
403 (Var.Module ["Basics"], "<|") ->
404 do e2' <- expression e2
405 es <- mapM expression (collectRightAssoc [] e1)
406 return $ foldr (<|) e2' es
408 (Var.BuiltIn, "::") ->
409 expression (A region (Data "::" [e1,e2]))
411 (Var.Module ["Basics"], _) ->
412 do e1' <- expression e1
414 case Map.lookup op basicOps of
419 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
422 do e1' <- expression e1
424 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
427 collectRightAssoc es e =
429 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
430 collectRightAssoc (es ++ [e1]) e2
433 collectLeftAssoc es e =
435 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
436 collectLeftAssoc (e2 : es) e1
440 Map.fromList (infixOps ++ specialOps)
443 let infixOp str op = (str, InfixExpr () op) in
448 , infixOp "&&" OpLAnd
453 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
454 , (,) "|>" $ flip (<|)
455 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
456 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
457 , (,) "<" $ cmp OpLT 0
458 , (,) ">" $ cmp OpGT 0
459 , (,) "<=" $ cmp OpLT 1
460 , (,) ">=" $ cmp OpGT (-1)
461 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
465 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