1module CompileToJS (showErr, jsModule) where
3import Control.Arrow (first,second)
4import Control.Monad (liftM,(<=<),join,ap)
5import Data.Char (isAlpha,isDigit)
6import Data.List (intercalate,sortBy,inits,foldl')
7import qualified Data.Map as Map
8import Data.Either (partitionEithers)
9import qualified Text.Pandoc as Pan
13import Rename (derename)
17import Parse.Library (isOp)
18import Rename (deprime)
19import Types.Types ( Type(RecordT) )
21showErr :: String -> String
22showErr err = "Elm.Graphics.text(Elm.Text.monospace(" ++ msg ++ "))"
23 where msg = show . concatMap (++"<br>") . lines $ err
30 [ ("N" , "Elm.Native"),
31 ("_" , "N.Utils(elm)"),
32 ("_L", "N.List(elm)"),
33 ("_E", "N.Error(elm)"),
34 ("_str", "N.JavaScript(elm).toString")
37parens s = "(" ++ s ++ ")"
38brackets s = "{" ++ s ++ "}"
39jsList ss = "["++ intercalate "," ss ++"]"
40jsFunc args body = "function(" ++ args ++ "){" ++ indent body ++ "}"
41assign x e = "\nvar " ++ x ++ " = " ++ e ++ ";"
42ret e = "\nreturn "++ e ++";"
43iff a b c = a ++ "?" ++ b ++ ":" ++ c
44quoted s = "'" ++ concatMap f s ++ "'"
52globalAssign n e = "\n" ++ assign' n e ++ ";"
53assign' n e = n ++ " = " ++ e
55jsModule (Module names exports imports stmts) =
56 setup ++ globalAssign ("Elm." ++ modName) (jsFunc "elm" program)
58 modNames = if null names then ["Main"] else names
59 modName = intercalate "." modNames
60 includes = concatMap jsImport imports
61 body = stmtsToJS stmts
62 defs = assign "$op" "{}"
63 program = "\nvar " ++ usefulFuncs ++ ";" ++ defs ++ includes ++ body ++
64 "\n_ = " ++ "elm.Native." ++ modName ++ " || {};" ++
65 getExports exports stmts ++
66 ret (assign' ("elm." ++ modName) "_") ++ "\n"
67 setup = concatMap (\n -> globalAssign n $ n ++ " || {}") .
68 map (intercalate ".") . tail . inits $
69 take (length modNames - 1) modNames
70 usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
72getExports names stmts = "\n"++ intercalate ";\n" (op : map fnPair fns)
73 where exNames n = either derename id n `elem` names
74 exports | null names = concatMap get stmts
75 | otherwise = filter exNames (concatMap get stmts)
77 (fns,ops) = partitionEithers exports
79 opPair op = "'" ++ op ++ "' : $op['" ++ op ++ "']"
80 fnPair fn = let fn' = derename fn in "_." ++ fn' ++ " = " ++ fn
82 op = ("_.$op = "++) . brackets . intercalate ", " $ map opPair ops
84 get' (FnDef x _ _) = Left x
85 get' (OpDef op _ _ _) = Right op
86 get s = case s of Definition d -> [ get' d ]
87 Datatype _ _ tcs -> map (Left . fst) tcs
88 ImportEvent _ _ x _ -> [ Left x ]
89 ExportEvent _ _ _ -> []
91 TypeAnnotation _ _ -> []
94jsImport (modul, how) =
96 As name -> assign name ("Elm." ++ modul ++ parens "elm")
98 "\nvar _$ = Elm." ++ modul ++ parens "elm" ++ ";" ++
99 if null vs then "\nfor (var k in _$) {eval('var '+k+'=_$[\"'+k+'\"]')}"
100 else concatMap def vs
102 imprt asgn v = asgn v ("elm." ++ modul ++ "." ++ v)
104 if isOp o then imprt globalAssign ("$op['" ++ o:p ++ "']")
105 else imprt assign (deprime (o:p))
108stmtsToJS :: [Statement] -> String
109stmtsToJS stmts = run $ do program <- mapM toJS (sortBy cmpStmt stmts)
110 return (vars ++ concat program)
112 vars = "\nvar " ++ intercalate ", " ("e":"case0":letBoundVars stmts) ++ ";"
113 cmpStmt s1 s2 = compare (valueOf s1) (valueOf s2)
114 valueOf s = case s of
116 ImportEvent _ _ _ _ -> 2
117 Definition (FnDef f [] _) ->
118 if derename f == "main" then 5 else 4
120 ExportEvent _ _ _ -> 6
122 TypeAnnotation _ _ -> 0
125 toJS :: a -> GuidCounter String
127instance ToJS Def where
128 toJS (FnDef x [] e) = assign' x `liftM` toJS' e
129 toJS (FnDef f as e) = (assign' f . wrapper . func) `liftM` toJS' e
130 where func body = jsFunc (intercalate ", " as) (ret body)
131 wrapper e | length as == 1 = e
132 | otherwise = 'F' : show (length as) ++ parens e
133 toJS (OpDef op a1 a2 e) =
135 let func = "F2" ++ parens (jsFunc (a1 ++ ", " ++ a2) (ret body))
136 return (globalAssign ("$op['" ++ op ++ "']") func)
138instance ToJS Statement where
141 Definition d -> (\asgn -> "\n" ++ asgn ++ ";") `liftM` toJS d
142 Datatype _ _ tcs -> concat `liftM` mapM (toJS . toDef) tcs
143 where toDef (name,args) =
144 let vars = map (('a':) . show) [1..length args] in
145 Definition . FnDef name vars . noContext $
146 Data (derename name) (map (noContext . Var) vars)
147 ImportEvent js base elm _ ->
149 return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal.constant(" ++ v ++ ");"
150 , "\ndocument.addEventListener('", js
151 , "_' + elm.id, function(e) { elm.notify(", elm
152 , ".id, e.value); });" ]
153 ExportEvent js elm _ ->
154 return $ concat [ "\nlift(function(v) { "
155 , "var e = document.createEvent('Event');"
156 , "e.initEvent('", js, "_' + elm.id, true, true);"
158 , "document.dispatchEvent(e); return v; })(", elm, ");" ]
159 TypeAnnotation _ _ -> return ""
162 RecordT kvs _ -> return $ "\nfunction" ++ args ++ brackets body
163 where fs = Map.keys kvs
164 args = parens (intercalate "," fs)
165 body = ret . brackets . intercalate "," $
166 map (\k -> k ++ ":" ++ k) fs
169toJS' :: CExpr -> GuidCounter String
170toJS' (C txt span expr) =
172 MultiIf ps -> multiIfToJS span ps
173 Case e cases -> caseToJS span e cases
176remove x e = "_.remove('" ++ x ++ "', " ++ e ++ ")"
177addField x v e = "_.insert('" ++ x ++ "', " ++ v ++ ", " ++ e ++ ")"
178setField fs e = "_.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
179 where f (x,v) = "['" ++ x ++ "'," ++ v ++ "]"
180access x e = parens e ++ "." ++ x
181makeRecord kvs = record `liftM` collect kvs
183 combine r (k,v) = Map.insertWith (++) k v r
184 collect = liftM (foldl' combine Map.empty) . mapM prep
185 prep (k, as, e@(C t s _)) =
186 do v <- toJS' (foldr (\x e -> C t s $ Lambda x e) e as)
189 brackets ("\n "++intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
190 hidden = fields . map (second jsList) .
191 filter (not . null . snd) . Map.toList . Map.map tail
192 record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
195instance ToJS Expr where
198 IntNum n -> return $ show n
199 FloatNum n -> return $ show n
201 Chr c -> return $ quoted [c]
202 Str s -> return $ "_str" ++ parens (quoted s)
203 Boolean b -> return $ if b then "true" else "false"
204 Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
205 Access e x -> access x `liftM` toJS' e
206 Remove e x -> remove x `liftM` toJS' e
207 Insert e x v -> addField x `liftM` toJS' v `ap` toJS' e
208 Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
209 setField fs' `liftM` toJS' e
210 Record fs -> makeRecord fs
211 Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
214 parens `liftM` (iff `liftM` toJS' eb `ap` toJS' et `ap` toJS' ef)
216 Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
218 App e1 e2 -> jsApp e1 e2
219 Let defs e -> jsLet defs e
221 do fs <- mapM toJS' es
222 let fields = zipWith (\n e -> "_" ++ show n ++ ":" ++ e) [0..] fs
223 return (brackets ("ctor:" ++ show name ++ concatMap (", "++) fields))
225 Markdown doc -> return $ "text('" ++ pad ++ md ++ pad ++ "')"
226 where pad = "<div style=\"height:0;width:0;\"> </div>"
227 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
231 as <- mapM toJS' args
234 _ -> "A" ++ show (length as) ++ parens (intercalate ", " (f:as))
236 (func, args) = go [e2] e1
239 (C _ _ (App e1 e2)) -> go (e2 : args) e1
242formatMarkdown = concatMap f
250 (C _ _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
251 _ -> format ("_E.If" ++ parens (quoted (show span))) ps
254 foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
255 f (b,e) = do b' <- toJS' b
257 return (b' ++ " ? " ++ e')
259jsLet defs e' = do ds <- jsDefs defs
261 return $ parens (intercalate ", " ds ++ ", " ++ e)
263 jsDefs defs = mapM toJS (sortBy f defs)
264 f a b = compare (valueOf a) (valueOf b)
265 valueOf (FnDef _ args _) = min 1 (length args)
266 valueOf (OpDef _ _ _ _) = 1
268caseToJS span e ps = do
269 match <- caseToMatch ps
271 (match',stmt) <- case (match,e) of
272 (Match name _ _, C _ _ (Var x)) ->
273 return (matchSubst [(name,x)] match, Nothing)
274 (Match name _ _, _) ->
275 return (match, Just (name ++ " = " ++ e'))
276 _ -> return (match, Nothing)
277 matches <- matchToJS span match'
278 return $ case stmt of
280 Just exp -> parens (exp ++ ", " ++ matches)
282matchToJS span match =
283 let sqnc e1 e2 = parens ("e=" ++ e1 ++ ":null,e!==null?e:" ++ e2)
285 Match name clauses def ->
286 do cases <- intercalate ":" `liftM` mapM (clauseToJS span name) clauses
287 finally <- matchToJS span def
288 return (sqnc cases finally)
289 Fail -> return ("_E.Case" ++ parens (quoted (show span)))
290 Break -> return "null"
292 Seq ms -> foldr1 sqnc `liftM` mapM (matchToJS span) (dropEnd [] ms)
293 where dropEnd acc [] = acc
296 Other _ -> acc ++ [m]
297 _ -> dropEnd (acc ++ [m]) ms
300clauseToJS span var (Clause name vars e) = do
301 let vars' = map (\n -> var ++ "._" ++ show n) [0..]
302 s <- matchToJS span $ matchSubst (zip vars vars') e
303 return $ concat [ var, ".ctor===", quoted name, "?", s ]
306jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
307jsRange e1 e2 = "_L.range" ++ parens (e1 ++ "," ++ e2)
308jsCompare e1 e2 op = parens ("_.compare(" ++ e1 ++ "," ++ e2 ++ ").ctor" ++ op)
311 | isAlpha o || '_' == o = (o:p) ++ parens e1 ++ parens e2
313 let ops = ["+","-","*","/","&&","||"] in
316 "++" -> "_L.append" ++ parens (e1 ++ "," ++ e2)
317 "$" -> e1 ++ parens e2
318 "." -> jsFunc "x" . ret $ e1 ++ parens (e2 ++ parens "x")
319 "^" -> "Math.pow(" ++ e1 ++ "," ++ e2 ++ ")"
320 "==" -> "_.eq(" ++ e1 ++ "," ++ e2 ++ ")"
321 "/=" -> "!_.eq(" ++ e1 ++ "," ++ e2 ++ ")"
322 "<" -> jsCompare e1 e2 "==='LT'"
323 ">" -> jsCompare e1 e2 "==='GT'"
324 "<=" -> jsCompare e1 e2 "!=='GT'"
325 ">=" -> jsCompare e1 e2 "!=='LT'"
326 "<~" -> "A2(lift," ++ e1 ++ "," ++ e2 ++ ")"
327 "~" -> "A3(lift2,F2(function(f,x){return f(x)}),"++e1++","++e2++")"
328 _ | elem (o:p) ops -> parens (e1 ++ (o:p) ++ e2)
329 | otherwise -> concat [ "$op['", o:p, "']"
330 , parens e1, parens e2 ]
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