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'')
265 Case.Clause (Right _) _ _ -> True
269 | any isLiteral clauses = obj [name]
270 | otherwise = obj (Help.splitDots name ++ ["ctor"])
273 | or isChars = InfixExpr () OpAdd e (string "")
277 return [ ExprStmt () (Help.throw "badCase" region) ]
280 return [BreakStmt () Nothing]
283 do e' <- expression e
287 concat <$> mapM (match region) (dropEnd [] ms)
292 Case.Other _ -> acc ++ [m]
293 _ -> dropEnd (acc ++ [m]) ms
296clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
297clause region variable (Case.Clause value vars mtch) =
298 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
301 map (\n -> variable ++ "._" ++ show n) [0..]
305 Right (Chr c) -> (True, string [c])
309 Right (Boolean b) -> BoolLit () b
310 Right lit -> literal lit
311 Left (Var.Canonical _ name) ->
315flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
316flattenLets defs lexpr@(A _ expr) =
318 Let ds body -> flattenLets (defs ++ ds) body
322generate :: CanonicalModule -> String
324 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
325 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
328 names = Module.names modul
330 thisModule :: Expression ()
331 thisModule = obj (localRuntime : names ++ ["values"])
333 programStmts :: [Statement ()]
336 [ [ ExprStmt () (string "use strict") ]
337 , setup localRuntime (names ++ ["values"])
338 , [ IfSingleStmt () thisModule (ret thisModule) ]
339 , [ VarDeclStmt () localVars ]
345 localVars :: [VarDecl ()]
346 localVars = varDecl "_op" (ObjectLit () []) :
347 internalImports (Module.names modul) ++
350 explicitImports :: [VarDecl ()]
352 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
354 jsImport :: Module.Name -> VarDecl ()
356 varDecl (Var.moduleName name) $
357 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
359 body :: [Statement ()]
360 body = concat $ evalState defs 0
362 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
364 setup namespace path = map create paths
366 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
367 paths = drop 2 . init . List.inits $ namespace : path
369 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
371 exs = map entry $ "_op" : concatMap extract (exports modul)
372 entry x = (prop x, ref x)
379 | otherwise -> [Var.varName x]
381 Var.Union _ (Var.Listing ctors _) ->
382 map Var.varName ctors
386 [x] -> VarDeclStmt () [ varDecl x expr ]
389 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
397 -> State Int (Expression ())
398binop region func@(Var.Canonical home op) e1 e2 =
400 (Var.Module ["Basics"], ">>") ->
401 do es <- mapM expression (collectLeftAssoc [e2] e1)
402 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
404 (Var.Module ["Basics"], "<<") ->
405 do es <- mapM expression (e1 : collectRightAssoc [] e2)
406 return $ ["$"] ==> foldr (<|) (ref "$") es
408 (Var.Module ["Basics"], "<|") ->
409 do e2' <- expression e2
410 es <- mapM expression (collectRightAssoc [] e1)
411 return $ foldr (<|) e2' es
413 (Var.BuiltIn, "::") ->
414 expression (A region (Data "::" [e1,e2]))
416 (Var.Module ["Basics"], _) ->
417 do e1' <- expression e1
419 case Map.lookup op basicOps of
424 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
427 do e1' <- expression e1
429 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
432 collectRightAssoc es e =
434 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
435 collectRightAssoc (es ++ [e1]) e2
438 collectLeftAssoc es e =
440 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
441 collectLeftAssoc (e2 : es) e1
445 Map.fromList (infixOps ++ specialOps)
448 let infixOp str op = (str, InfixExpr () op) in
453 , infixOp "&&" OpLAnd
458 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
459 , (,) "|>" $ flip (<|)
460 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
461 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
462 , (,) "<" $ cmp OpLT 0
463 , (,) ">" $ cmp OpGT 0
464 , (,) "<=" $ cmp OpLT 1
465 , (,) ">=" $ cmp OpGT (-1)
466 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
470 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