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)
39internalImports :: Module.Name -> [VarDecl ()]
41 [ varDecl "_N" (obj ["Elm","Native"])
42 , include "_U" "Utils"
44 , include "_P" "Ports"
45 , varDecl Help.localModuleName (string (Module.nameToString name))
48 include :: String -> String -> VarDecl ()
50 varDecl alias (obj ["_N", modul, "make"] <| ref localRuntime)
53_Utils :: String -> Expression ()
58_List :: String -> Expression ()
63literal :: Literal -> Expression ()
66 Chr c -> _Utils "chr" <| string [c]
68 IntNum n -> IntLit () n
69 FloatNum n -> NumLit () n
70 Boolean b -> BoolLit () b
73expression :: Canonical.Expr -> State Int (Expression ())
74expression (A region expr) =
76 Var var -> return $ Var.canonical var
78 Literal lit -> return $ literal lit
81 do lo' <- expression lo
83 return $ _List "range" `call` [lo',hi']
87 return $ DotRef () e' (var x)
91 return $ _Utils "remove" `call` [string x, e']
96 return $ _Utils "insert" `call` [string x, v', e']
100 fs' <- forM fs $ \(f,v) -> do
102 return $ ArrayLit () [string f, v']
103 return $ _Utils "replace" `call` [ArrayLit () fs', e']
106 do fields' <- forM fields $ \(f,e) -> do
107 (,) f <$> expression e
108 let fieldMap = List.foldl' combine Map.empty fields'
109 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
111 combine r (k,v) = Map.insertWith (++) k [v] r
112 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
113 Map.toList . Map.filter (not . null) $ Map.map tail fs
114 visible fs = 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..]
197 obj ["Elm","Text","make"] <| ref localRuntime
200 "<div style=\"height:0;width:0;\"> </div>"
203 pad ++ MD.toHtml doc ++ pad
205 return (DotRef () text (var "markdown") `call` [ string md, string uid ])
207 GLShader _uid src _tipe ->
208 return $ ObjectLit () [(PropString () "src", literal (Str src))]
211 return $ obj ["_P","portIn"] `call`
212 [ string name, Port.incoming tipe ]
214 PortOut name tipe value ->
215 do value' <- expression value
216 return $ obj ["_P","portOut"] `call`
217 [ string name, Port.outgoing tipe, value' ]
220definition :: Canonical.Def -> State Int [Statement ()]
221definition (Canonical.Definition pattern expr@(A region _) _) = do
222 expr' <- expression expr
223 let assign x = varDecl x expr'
227 let op = LBracket () (ref "_op") (string x) in
228 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 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
246 decl x n = varDecl x (obj ["$","_" ++ show n])
248 | Help.isTuple name = assign "$" : vars
249 | otherwise = assign "_raw" : safeAssign : vars
251 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
252 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
253 exception = Help.throw "badCase" region
256 do defs' <- concat <$> mapM toDef vars
257 return (VarDeclStmt () [assign "_"] : defs')
259 vars = P.boundVarList pattern
260 mkVar = A region . localVar
262 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
264 definition $ Canonical.Definition (P.Var y) expr Nothing
267match :: Region -> Case.Match -> State Int [Statement ()]
270 Case.Match name clauses mtch' ->
271 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
272 mtch'' <- match region mtch'
273 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
275 isLiteral p = case p of
276 Case.Clause (Right _) _ _ -> True
280 | any isLiteral clauses = obj [name]
281 | otherwise = obj (Help.splitDots name ++ ["ctor"])
284 | or isChars = InfixExpr () OpAdd e (string "")
288 return [ ExprStmt () (Help.throw "badCase" region) ]
290 Case.Break -> return [BreakStmt () Nothing]
293 do e' <- expression e
296 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
301 Case.Other _ -> acc ++ [m]
302 _ -> dropEnd (acc ++ [m]) ms
305clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
306clause region variable (Case.Clause value vars mtch) =
307 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
309 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
312 Right (Chr c) -> (True, string [c])
316 Right (Boolean b) -> BoolLit () b
317 Right lit -> literal lit
318 Left (Var.Canonical _ name) ->
322flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
323flattenLets defs lexpr@(A _ expr) =
325 Let ds body -> flattenLets (defs ++ ds) body
329generate :: CanonicalModule -> String
331 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
332 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
335 names = Module.names modul
337 thisModule :: Expression ()
338 thisModule = obj (localRuntime : names ++ ["values"])
340 programStmts :: [Statement ()]
343 [ [ ExprStmt () (string "use strict") ]
344 , setup localRuntime (names ++ ["values"])
345 , [ IfSingleStmt () thisModule (ret thisModule) ]
346 , [ VarDeclStmt () localVars ]
352 localVars :: [VarDecl ()]
353 localVars = varDecl "_op" (ObjectLit () []) :
354 internalImports (Module.names modul) ++
357 explicitImports :: [VarDecl ()]
359 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
361 jsImport :: Module.Name -> VarDecl ()
363 varDecl (Var.moduleName name) $
364 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
366 body :: [Statement ()]
367 body = concat $ evalState defs 0
369 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
371 setup namespace path = map create paths
373 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
374 paths = drop 2 . init . List.inits $ namespace : path
376 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
378 exs = map entry $ "_op" : concatMap extract (exports modul)
379 entry x = (prop x, ref x)
386 | otherwise -> [Var.varName x]
388 Var.Union _ (Var.Listing ctors _) ->
389 map Var.varName ctors
393 [x] -> VarDeclStmt () [ varDecl x expr ]
396 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
404 -> State Int (Expression ())
405binop region func@(Var.Canonical home op) e1 e2 =
407 (Var.Module ["Basics"], ">>") ->
408 do es <- mapM expression (collectLeftAssoc [e2] e1)
409 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
411 (Var.Module ["Basics"], "<<") ->
412 do es <- mapM expression (e1 : collectRightAssoc [] e2)
413 return $ ["$"] ==> foldr (<|) (ref "$") es
415 (Var.Module ["Basics"], "<|") ->
416 do e2' <- expression e2
417 es <- mapM expression (collectRightAssoc [] e1)
418 return $ foldr (<|) e2' es
420 (Var.BuiltIn, "::") ->
421 expression (A region (Data "::" [e1,e2]))
423 (Var.Module ["Basics"], _) ->
424 do e1' <- expression e1
426 case Map.lookup op basicOps of
431 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
434 do e1' <- expression e1
436 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
439 collectRightAssoc es e =
441 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
442 collectRightAssoc (es ++ [e1]) e2
445 collectLeftAssoc es e =
447 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
448 collectLeftAssoc (e2 : es) e1
452 Map.fromList (infixOps ++ specialOps)
455 let infixOp str op = (str, InfixExpr () op) in
460 , infixOp "&&" OpLAnd
465 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
466 , (,) "|>" $ flip (<|)
467 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
468 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
469 , (,) "<" $ cmp OpLT 0
470 , (,) ">" $ cmp OpGT 0
471 , (,) "<=" $ cmp OpLT 1
472 , (,) ">=" $ cmp OpGT (-1)
473 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
477 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