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)
204 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
206 GLShader _uid src _tipe ->
207 return $ ObjectLit () [(PropString () "src", literal (Str src))]
210 return (Foreign.input name tipe)
212 Output name tipe value ->
213 do value' <- expression value
214 return (Foreign.output name tipe value')
216 Loopback name tipe maybeExpr ->
219 error "create writable stream"
222 error "run the promises"
225definition :: Canonical.Def -> State Int [Statement ()]
226definition (Canonical.Definition pattern expr@(A region _) _) =
227 do expr' <- expression expr
228 let assign x = varDecl x expr'
232 let op = LBracket () (ref "_op") (string x) in
233 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
236 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
239 let setField f = varDecl f (obj ["$",f]) in
240 return [ VarDeclStmt () (assign "$" : map setField fields) ]
242 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
243 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
245 vars = getVars patterns
248 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
252 decl x n = varDecl x (obj ["$","_" ++ show n])
254 | Help.isTuple name = assign "$" : vars
255 | otherwise = assign "_raw" : safeAssign : vars
257 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
258 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
259 exception = Help.throw "badCase" region
262 do defs' <- concat <$> mapM toDef vars
263 return (VarDeclStmt () [assign "_"] : defs')
265 vars = P.boundVarList pattern
266 mkVar = A region . localVar
268 let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
270 definition $ Canonical.Definition (P.Var y) expr Nothing
273match :: Region -> Case.Match -> State Int [Statement ()]
276 Case.Match name clauses mtch' ->
277 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
278 mtch'' <- match region mtch'
279 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
283 Case.Clause (Right _) _ _ -> True
287 | any isLiteral clauses = obj [name]
288 | otherwise = obj (Help.splitDots name ++ ["ctor"])
291 | or isChars = InfixExpr () OpAdd e (string "")
295 return [ ExprStmt () (Help.throw "badCase" region) ]
298 return [BreakStmt () Nothing]
301 do e' <- expression e
305 concat <$> mapM (match region) (dropEnd [] ms)
310 Case.Other _ -> acc ++ [m]
311 _ -> dropEnd (acc ++ [m]) ms
314clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
315clause region variable (Case.Clause value vars mtch) =
316 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
319 map (\n -> variable ++ "._" ++ show n) [0..]
323 Right (Chr c) -> (True, string [c])
327 Right (Boolean b) -> BoolLit () b
328 Right lit -> literal lit
329 Left (Var.Canonical _ name) ->
333flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
334flattenLets defs lexpr@(A _ expr) =
336 Let ds body -> flattenLets (defs ++ ds) body
340generate :: CanonicalModule -> String
342 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
343 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
346 names = Module.names modul
348 thisModule :: Expression ()
349 thisModule = obj (localRuntime : names ++ ["values"])
351 programStmts :: [Statement ()]
354 [ [ ExprStmt () (string "use strict") ]
355 , setup localRuntime (names ++ ["values"])
356 , [ IfSingleStmt () thisModule (ret thisModule) ]
357 , [ VarDeclStmt () localVars ]
363 localVars :: [VarDecl ()]
364 localVars = varDecl "_op" (ObjectLit () []) :
365 internalImports (Module.names modul) ++
368 explicitImports :: [VarDecl ()]
370 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
372 jsImport :: Module.Name -> VarDecl ()
374 varDecl (Var.moduleName name) $
375 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
377 body :: [Statement ()]
378 body = concat $ evalState defs 0
380 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
382 setup namespace path = map create paths
384 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
385 paths = drop 2 . init . List.inits $ namespace : path
387 jsExports = assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
389 exs = map entry $ "_op" : concatMap extract (exports modul)
390 entry x = (prop x, ref x)
397 | otherwise -> [Var.varName x]
399 Var.Union _ (Var.Listing ctors _) ->
400 map Var.varName ctors
404 [x] -> VarDeclStmt () [ varDecl x expr ]
407 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
415 -> State Int (Expression ())
416binop region func@(Var.Canonical home op) e1 e2 =
418 (Var.Module ["Basics"], ">>") ->
419 do es <- mapM expression (collectLeftAssoc [e2] e1)
420 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
422 (Var.Module ["Basics"], "<<") ->
423 do es <- mapM expression (e1 : collectRightAssoc [] e2)
424 return $ ["$"] ==> foldr (<|) (ref "$") es
426 (Var.Module ["Basics"], "<|") ->
427 do e2' <- expression e2
428 es <- mapM expression (collectRightAssoc [] e1)
429 return $ foldr (<|) e2' es
431 (Var.BuiltIn, "::") ->
432 expression (A region (Data "::" [e1,e2]))
434 (Var.Module ["Basics"], _) ->
435 do e1' <- expression e1
437 case Map.lookup op basicOps of
442 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
445 do e1' <- expression e1
447 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
450 collectRightAssoc es e =
452 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
453 collectRightAssoc (es ++ [e1]) e2
456 collectLeftAssoc es e =
458 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
459 collectLeftAssoc (e2 : es) e1
463 Map.fromList (infixOps ++ specialOps)
466 let infixOp str op = (str, InfixExpr () op) in
471 , infixOp "&&" OpLAnd
476 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
477 , (,) "|>" $ flip (<|)
478 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
479 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
480 , (,) "<" $ cmp OpLT 0
481 , (,) ">" $ cmp OpGT 0
482 , (,) "<=" $ cmp OpLT 1
483 , (,) ">=" $ cmp OpGT (-1)
484 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
488 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