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.Port as Port
15import qualified Generate.JavaScript.Variable as Var
19import AST.Expression.General
20import qualified AST.Expression.Canonical as Canonical
21import qualified AST.Helpers as Help
23import qualified AST.Module as Module
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 , varDecl Help.localModuleName (string (Module.nameToString name))
36 include :: String -> String -> VarDecl ()
38 varDecl alias (Help.make ["_N", modul])
41_Utils :: String -> Expression ()
46_List :: String -> Expression ()
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
61expression :: Canonical.Expr -> State Int (Expression ())
62expression (A region expr) =
65 return $ Var.canonical var
71 do lo' <- expression lo
73 return $ _List "range" `call` [lo',hi']
77 return $ DotRef () e' (var (Var.varName field))
81 return $ _Utils "remove" `call` [ string (Var.varName field), e' ]
83 Insert e field value ->
84 do value' <- expression value
86 return $ _Utils "insert" `call` [ string (Var.varName field), value', e' ]
91 forM fields $ \(field, value) ->
92 do value' <- expression value
93 return $ ArrayLit () [ string (Var.varName field), value' ]
95 return $ _Utils "replace" `call` [ArrayLit () fields', e']
99 forM fields $ \(field, e) ->
100 (,) (Var.varName field) <$> expression e
103 List.foldl' combine Map.empty fields'
105 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
107 combine record (field, value) =
108 Map.insertWith (++) field [value] record
111 ObjectLit () . map (prop *** ArrayLit ()) $
112 Map.toList (Map.filter (not . null) (Map.map tail fs))
115 map (first prop) (Map.toList (Map.map head fs))
118 binop region op e1 e2
120 Lambda p e@(A ann _) ->
121 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
122 body' <- expression body
124 case length args < 2 || length args > 9 of
125 True -> foldr (==>) body' (map (:[]) args)
126 False -> ref ("F" ++ show (length args)) <| (args ==> body')
128 depattern (args, body) pattern =
131 return (args ++ [ Var.varName x ], body)
133 do arg <- Case.newVar
134 return ( args ++ [arg]
135 , A ann (Case (A ann (localVar arg)) [(pattern, body)])
138 (patterns, innerBody) = collect [p] e
140 collect patterns lexpr@(A _ expr) =
142 Lambda p e -> collect (p:patterns) e
143 _ -> (patterns, lexpr)
146 do func' <- expression func
147 args' <- mapM expression args
150 [arg] -> func' <| arg
151 _ | length args' <= 9 -> ref aN `call` (func':args')
152 | otherwise -> foldl1 (<|) (func':args')
154 aN = "A" ++ show (length args)
155 (func, args) = getArgs e1 [e2]
158 (A _ (App f arg)) -> getArgs f (arg : args)
162 do let (defs',e') = flattenLets defs e
163 stmts <- concat <$> mapM definition defs'
165 return $ function [] (stmts ++ [ ret exp ]) `call` []
168 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
170 case last branches of
171 (A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
173 (A _ (Literal (Boolean True)), _) ->
176 ifs branches' (throw "badIf" region)
178 safeIfs branches = ifs (init branches) (snd (last branches))
179 ifs branches finally = foldr iff finally branches
180 iff (if', then') else' = CondExpr () if' then' else'
183 do (tempVar,initialMatch) <- Case.toMatch cases
184 (revisedMatch, stmt) <-
186 A _ (Var (Var.Canonical Var.Local x)) ->
187 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
189 do e' <- expression e
190 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
191 match' <- match region revisedMatch
192 return (function [] (stmt ++ match') `call` [])
195 do es' <- mapM expression es
196 return $ _List "fromArray" <| ArrayLit () es'
199 do es' <- mapM expression es
200 return $ ObjectLit () (ctor : fields es')
202 ctor = (prop "ctor", string name)
204 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
206 GLShader _uid src _tipe ->
207 return $ ObjectLit () [(PropString () "src", literal (Str src))]
212 return (Port.inbound name portType)
214 Out name expr portType ->
215 do expr' <- expression expr
216 return (Port.outbound name expr' portType)
218 Task name expr portType ->
219 do expr' <- expression expr
220 return (Port.task name expr' portType)
223definition :: Canonical.Def -> State Int [Statement ()]
224definition (Canonical.Definition pattern expr@(A region _) _) =
225 do expr' <- expression expr
226 let assign x = varDecl x expr'
230 let op = LBracket () (ref "_op") (string x) in
231 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
234 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
237 let setField f = varDecl f (obj ["$",f]) in
238 return [ VarDeclStmt () (assign "$" : map setField fields) ]
240 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
241 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
243 vars = getVars patterns
246 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
250 decl x n = varDecl x (obj ["$","_" ++ show n])
252 | Help.isTuple name = assign "$" : vars
253 | otherwise = assign "_raw" : safeAssign : vars
255 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
256 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
257 exception = Help.throw "badCase" region
260 do defs' <- concat <$> mapM toDef vars
261 return (VarDeclStmt () [assign "_"] : defs')
263 vars = P.boundVarList pattern
264 mkVar = A region . localVar
266 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
268 definition $ Canonical.Definition (P.Var y) expr Nothing
271match :: 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 :: 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 _ expr) =
334 Let ds body -> flattenLets (defs ++ ds) body
338generate :: 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 ()]
362 localVars = varDecl "_op" (ObjectLit () []) :
363 internalImports (Module.names modul) ++
366 explicitImports :: [VarDecl ()]
368 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
370 jsImport :: Module.Name -> VarDecl ()
372 varDecl (Var.moduleName name) $
373 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
375 body :: [Statement ()]
376 body = concat $ evalState defs 0
378 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
380 setup namespace path = map create paths
382 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
383 paths = drop 2 . init . List.inits $ namespace : path
385 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
387 exs = map entry $ "_op" : concatMap extract (exports modul)
388 entry x = (prop x, ref x)
395 | otherwise -> [Var.varName x]
397 Var.Union _ (Var.Listing ctors _) ->
398 map Var.varName ctors
402 [x] -> VarDeclStmt () [ varDecl x expr ]
405 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
413 -> State Int (Expression ())
414binop region func@(Var.Canonical home op) e1 e2 =
416 (Var.Module ["Basics"], ">>") ->
417 do es <- mapM expression (collectLeftAssoc [e2] e1)
418 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
420 (Var.Module ["Basics"], "<<") ->
421 do es <- mapM expression (e1 : collectRightAssoc [] e2)
422 return $ ["$"] ==> foldr (<|) (ref "$") es
424 (Var.Module ["Basics"], "<|") ->
425 do e2' <- expression e2
426 es <- mapM expression (collectRightAssoc [] e1)
427 return $ foldr (<|) e2' es
429 (Var.BuiltIn, "::") ->
430 expression (A region (Data "::" [e1,e2]))
432 (Var.Module ["Basics"], _) ->
433 do e1' <- expression e1
435 case Map.lookup op basicOps of
440 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
443 do e1' <- expression e1
445 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
448 collectRightAssoc es e =
450 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
451 collectRightAssoc (es ++ [e1]) e2
454 collectLeftAssoc es e =
456 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
457 collectLeftAssoc (e2 : es) e1
461 Map.fromList (infixOps ++ specialOps)
464 let infixOp str op = (str, InfixExpr () op) in
469 , infixOp "&&" OpLAnd
474 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
475 , (,) "|>" $ flip (<|)
476 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
477 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
478 , (,) "<" $ cmp OpLT 0
479 , (,) ">" $ cmp OpGT 0
480 , (,) "<=" $ cmp OpLT 1
481 , (,) ">=" $ cmp OpGT (-1)
482 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
486 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