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) =
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 p e@(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)
134 do arg <- Case.newVar
135 return ( args ++ [arg]
136 , A ann (Case (A ann (localVar arg)) [(pattern, body)])
139 (patterns, innerBody) = collect [p] e
141 collect patterns lexpr@(A _ expr) =
143 Lambda p e -> collect (p:patterns) e
144 _ -> (patterns, lexpr)
147 do func' <- expression func
148 args' <- mapM expression args
151 [arg] -> func' <| arg
152 _ | length args' <= 9 -> ref aN `call` (func':args')
153 | otherwise -> foldl1 (<|) (func':args')
155 aN = "A" ++ show (length args)
156 (func, args) = getArgs e1 [e2]
159 (A _ (App f arg)) -> getArgs f (arg : args)
163 do let (defs',e') = flattenLets defs e
164 stmts <- concat <$> mapM definition defs'
166 return $ function [] (stmts ++ [ ret exp ]) `call` []
169 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
171 case last branches of
172 (A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
174 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
175 _ -> ifs branches' (throw "badIf" region)
177 safeIfs branches = ifs (init branches) (snd (last branches))
178 ifs branches finally = foldr iff finally branches
179 iff (if', then') else' = CondExpr () if' then' else'
182 do (tempVar,initialMatch) <- Case.toMatch cases
183 (revisedMatch, stmt) <-
185 A _ (Var (Var.Canonical Var.Local x)) ->
186 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
188 do e' <- expression e
189 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
190 match' <- match region revisedMatch
191 return (function [] (stmt ++ match') `call` [])
194 do es' <- mapM expression es
195 return $ _List "fromArray" <| ArrayLit () es'
198 do es' <- mapM expression es
199 return $ ObjectLit () (ctor : fields es')
201 ctor = (prop "ctor", string name)
202 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
204 GLShader _uid src _tipe ->
205 return $ ObjectLit () [(PropString () "src", literal (Str src))]
208 return $ obj ["_P","portIn"] `call`
209 [ string name, Port.incoming tipe ]
211 PortOut name tipe value ->
212 do value' <- expression value
213 return $ obj ["_P","portOut"] `call`
214 [ string name, Port.outgoing tipe, value' ]
217definition :: Canonical.Def -> State Int [Statement ()]
218definition (Canonical.Definition pattern expr@(A region _) _) = do
219 expr' <- expression expr
220 let assign x = varDecl x expr'
224 let op = LBracket () (ref "_op") (string x) in
225 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
227 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
230 let setField f = varDecl f (obj ["$",f]) in
231 return [ VarDeclStmt () (assign "$" : map setField fields) ]
233 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
234 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
236 vars = getVars patterns
239 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
243 decl x n = varDecl x (obj ["$","_" ++ show n])
245 | Help.isTuple name = assign "$" : vars
246 | otherwise = assign "_raw" : safeAssign : vars
248 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
249 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
250 exception = Help.throw "badCase" region
253 do defs' <- concat <$> mapM toDef vars
254 return (VarDeclStmt () [assign "_"] : defs')
256 vars = P.boundVarList pattern
257 mkVar = A region . localVar
259 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
261 definition $ Canonical.Definition (P.Var y) expr Nothing
264match :: Region -> Case.Match -> State Int [Statement ()]
267 Case.Match name clauses mtch' ->
268 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
269 mtch'' <- match region mtch'
270 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
274 Case.Clause (Right _) _ _ -> True
278 | any isLiteral clauses = obj [name]
279 | otherwise = obj (Help.splitDots name ++ ["ctor"])
282 | or isChars = InfixExpr () OpAdd e (string "")
286 return [ ExprStmt () (Help.throw "badCase" region) ]
289 return [BreakStmt () Nothing]
292 do e' <- expression e
296 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)
310 map (\n -> variable ++ "._" ++ show n) [0..]
314 Right (Chr c) -> (True, string [c])
318 Right (Boolean b) -> BoolLit () b
319 Right lit -> literal lit
320 Left (Var.Canonical _ name) ->
324flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
325flattenLets defs lexpr@(A _ expr) =
327 Let ds body -> flattenLets (defs ++ ds) body
331generate :: CanonicalModule -> String
333 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
334 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
337 names = Module.names modul
339 thisModule :: Expression ()
340 thisModule = obj (localRuntime : names ++ ["values"])
342 programStmts :: [Statement ()]
345 [ [ ExprStmt () (string "use strict") ]
346 , setup localRuntime (names ++ ["values"])
347 , [ IfSingleStmt () thisModule (ret thisModule) ]
348 , [ VarDeclStmt () localVars ]
354 localVars :: [VarDecl ()]
355 localVars = varDecl "_op" (ObjectLit () []) :
356 internalImports (Module.names modul) ++
359 explicitImports :: [VarDecl ()]
361 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
363 jsImport :: Module.Name -> VarDecl ()
365 varDecl (Var.moduleName name) $
366 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
368 body :: [Statement ()]
369 body = concat $ evalState defs 0
371 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
373 setup namespace path = map create paths
375 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
376 paths = drop 2 . init . List.inits $ namespace : path
378 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
380 exs = map entry $ "_op" : concatMap extract (exports modul)
381 entry x = (prop x, ref x)
388 | otherwise -> [Var.varName x]
390 Var.Union _ (Var.Listing ctors _) ->
391 map Var.varName ctors
395 [x] -> VarDeclStmt () [ varDecl x expr ]
398 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
406 -> State Int (Expression ())
407binop region func@(Var.Canonical home op) e1 e2 =
409 (Var.Module ["Basics"], ">>") ->
410 do es <- mapM expression (collectLeftAssoc [e2] e1)
411 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
413 (Var.Module ["Basics"], "<<") ->
414 do es <- mapM expression (e1 : collectRightAssoc [] e2)
415 return $ ["$"] ==> foldr (<|) (ref "$") es
417 (Var.Module ["Basics"], "<|") ->
418 do e2' <- expression e2
419 es <- mapM expression (collectRightAssoc [] e1)
420 return $ foldr (<|) e2' es
422 (Var.BuiltIn, "::") ->
423 expression (A region (Data "::" [e1,e2]))
425 (Var.Module ["Basics"], _) ->
426 do e1' <- expression e1
428 case Map.lookup op basicOps of
433 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
436 do e1' <- expression e1
438 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
441 collectRightAssoc es e =
443 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
444 collectRightAssoc (es ++ [e1]) e2
447 collectLeftAssoc es e =
449 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
450 collectLeftAssoc (e2 : es) e1
454 Map.fromList (infixOps ++ specialOps)
457 let infixOp str op = (str, InfixExpr () op) in
462 , infixOp "&&" OpLAnd
467 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
468 , (,) "|>" $ flip (<|)
469 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
470 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
471 , (,) "<" $ cmp OpLT 0
472 , (,) ">" $ cmp OpGT 0
473 , (,) "<=" $ cmp OpLT 1
474 , (,) ">=" $ cmp OpGT (-1)
475 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
479 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