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.Foreign as Foreign
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)), _) ->
177 ifs branches' (throw "badIf" region)
179 safeIfs branches = ifs (init branches) (snd (last branches))
180 ifs branches finally = foldr iff finally branches
181 iff (if', then') else' = CondExpr () if' then' else'
184 do (tempVar,initialMatch) <- Case.toMatch cases
185 (revisedMatch, stmt) <-
187 A _ (Var (Var.Canonical Var.Local x)) ->
188 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
190 do e' <- expression e
191 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
192 match' <- match region revisedMatch
193 return (function [] (stmt ++ match') `call` [])
196 do es' <- mapM expression es
197 return $ _List "fromArray" <| ArrayLit () es'
200 do es' <- mapM expression es
201 return $ ObjectLit () (ctor : fields es')
203 ctor = (prop "ctor", string name)
205 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
207 GLShader _uid src _tipe ->
208 return $ ObjectLit () [(PropString () "src", literal (Str src))]
211 return (Foreign.input name tipe)
213 Output name tipe value ->
214 do value' <- expression value
215 return (Foreign.output name tipe value')
217 LoopbackIn name source ->
220 return $ obj ["_P","writableStream"] `call` [ string name ]
223 return $ obj ["_P","loopbackIn"] `call` [ string name ]
225 LoopbackOut name expr ->
226 do expr' <- expression expr
227 return $ obj ["_P","loopbackOut"] `call` [ string name, expr' ]
230definition :: Canonical.Def -> State Int [Statement ()]
231definition (Canonical.Definition pattern expr@(A region _) _) =
232 do expr' <- expression expr
233 let assign x = varDecl x expr'
237 let op = LBracket () (ref "_op") (string x) in
238 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
241 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
244 let setField f = varDecl f (obj ["$",f]) in
245 return [ VarDeclStmt () (assign "$" : map setField fields) ]
247 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
248 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
250 vars = getVars patterns
253 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
257 decl x n = varDecl x (obj ["$","_" ++ show n])
259 | Help.isTuple name = assign "$" : vars
260 | otherwise = assign "_raw" : safeAssign : vars
262 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
263 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
264 exception = Help.throw "badCase" region
267 do defs' <- concat <$> mapM toDef vars
268 return (VarDeclStmt () [assign "_"] : defs')
270 vars = P.boundVarList pattern
271 mkVar = A region . localVar
273 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
275 definition $ Canonical.Definition (P.Var y) expr Nothing
278match :: Region -> Case.Match -> State Int [Statement ()]
281 Case.Match name clauses mtch' ->
282 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
283 mtch'' <- match region mtch'
284 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
288 Case.Clause (Right _) _ _ -> True
292 | any isLiteral clauses = obj [name]
293 | otherwise = obj (Help.splitDots name ++ ["ctor"])
296 | or isChars = InfixExpr () OpAdd e (string "")
300 return [ ExprStmt () (Help.throw "badCase" region) ]
303 return [BreakStmt () Nothing]
306 do e' <- expression e
310 concat <$> mapM (match region) (dropEnd [] ms)
315 Case.Other _ -> acc ++ [m]
316 _ -> dropEnd (acc ++ [m]) ms
319clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
320clause region variable (Case.Clause value vars mtch) =
321 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
324 map (\n -> variable ++ "._" ++ show n) [0..]
328 Right (Chr c) -> (True, string [c])
332 Right (Boolean b) -> BoolLit () b
333 Right lit -> literal lit
334 Left (Var.Canonical _ name) ->
338flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
339flattenLets defs lexpr@(A _ expr) =
341 Let ds body -> flattenLets (defs ++ ds) body
345generate :: CanonicalModule -> String
347 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
348 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
351 names = Module.names modul
353 thisModule :: Expression ()
354 thisModule = obj (localRuntime : names ++ ["values"])
356 programStmts :: [Statement ()]
359 [ [ ExprStmt () (string "use strict") ]
360 , setup localRuntime (names ++ ["values"])
361 , [ IfSingleStmt () thisModule (ret thisModule) ]
362 , [ VarDeclStmt () localVars ]
368 localVars :: [VarDecl ()]
369 localVars = varDecl "_op" (ObjectLit () []) :
370 internalImports (Module.names modul) ++
373 explicitImports :: [VarDecl ()]
375 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
377 jsImport :: Module.Name -> VarDecl ()
379 varDecl (Var.moduleName name) $
380 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
382 body :: [Statement ()]
383 body = concat $ evalState defs 0
385 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
387 setup namespace path = map create paths
389 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
390 paths = drop 2 . init . List.inits $ namespace : path
392 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
394 exs = map entry $ "_op" : concatMap extract (exports modul)
395 entry x = (prop x, ref x)
402 | otherwise -> [Var.varName x]
404 Var.Union _ (Var.Listing ctors _) ->
405 map Var.varName ctors
409 [x] -> VarDeclStmt () [ varDecl x expr ]
412 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
420 -> State Int (Expression ())
421binop region func@(Var.Canonical home op) e1 e2 =
423 (Var.Module ["Basics"], ">>") ->
424 do es <- mapM expression (collectLeftAssoc [e2] e1)
425 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
427 (Var.Module ["Basics"], "<<") ->
428 do es <- mapM expression (e1 : collectRightAssoc [] e2)
429 return $ ["$"] ==> foldr (<|) (ref "$") es
431 (Var.Module ["Basics"], "<|") ->
432 do e2' <- expression e2
433 es <- mapM expression (collectRightAssoc [] e1)
434 return $ foldr (<|) e2' es
436 (Var.BuiltIn, "::") ->
437 expression (A region (Data "::" [e1,e2]))
439 (Var.Module ["Basics"], _) ->
440 do e1' <- expression e1
442 case Map.lookup op basicOps of
447 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
450 do e1' <- expression e1
452 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
455 collectRightAssoc es e =
457 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
458 collectRightAssoc (es ++ [e1]) e2
461 collectLeftAssoc es e =
463 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
464 collectLeftAssoc (e2 : es) e1
468 Map.fromList (infixOps ++ specialOps)
471 let infixOp str op = (str, InfixExpr () op) in
476 , infixOp "&&" OpLAnd
481 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
482 , (,) "|>" $ flip (<|)
483 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
484 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
485 , (,) "<" $ cmp OpLT 0
486 , (,) ">" $ cmp OpGT 0
487 , (,) "<=" $ cmp OpLT 1
488 , (,) ">=" $ cmp OpGT (-1)
489 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
493 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