@@ -1,7 +1,5 @@
1module CompileToJS (showErr, jsModule) where
1module CompileToJS (showErr, jsModule) where
5import Control.Arrow (first,second)
3import Control.Arrow (first,second)
6import Control.Monad (liftM,(<=<),join,ap)
4import Control.Monad (liftM,(<=<),join,ap)
7import Data.Char (isAlpha,isDigit)
5import Data.Char (isAlpha,isDigit)
@@ -10,12 +8,15 @@ import qualified Data.Map as Map
10import Data.Either (partitionEithers)
8import Data.Either (partitionEithers)
11import qualified Text.Pandoc as Pan
9import qualified Text.Pandoc as Pan
13−import Initialize
11+import Ast
14import Rename (derename)
13import Rename (derename)
15import Cases
14import Cases
16import Guid
15import Guid
17import Parse.Library (isOp)
17import Parse.Library (isOp)
18import Rename (deprime)
18import Rename (deprime)
19+import Types.Types ( Type(RecordT) )
20showErr :: String -> String
21showErr :: String -> String
21showErr err = mainEquals $ "Elm.Graphics.text(Elm.Text.monospace(" ++ msg ++ "))"
22showErr err = mainEquals $ "Elm.Graphics.text(Elm.Text.monospace(" ++ msg ++ "))"
@@ -41,47 +42,26 @@ quoted s = "'" ++ concatMap f s ++ "'"
43mainEquals s = globalAssign "Elm.main" (jsFunc "" (ret s))
44mainEquals s = globalAssign "Elm.main" (jsFunc "" (ret s))
44−globalAssign m s = "\n" ++ m ++ "=" ++ s ++ ";"
45+globalAssign n e = "\n" ++ assign' n e ++ ";"
45−
46+assign' n e = n ++ " = " ++ e
46−tryBlock escapees names e =
47+
47− concat [ "\ntry{\n" ++ e ++ "\n} catch (e) {"
48− , "\nElm.main=function() {"
49− , "\nvar msg = ('<br/><h2>Your browser may not be supported. " ++
50− "Are you using a modern browser?</h2>' +" ++
51− " '<br/><span style=\"color:grey\">Runtime Error in " ++
52− intercalate "." names ++ " module:<br/>' + e + '" ++ msg ++ "</span>');"
53− , "\ndocument.body.innerHTML = Elm.Text.monospace(msg);"
57− where msg | escapees /= [] = concat [ "<br/><br/>The problem may stem from an improper usage of:<br/>"
58− , intercalate ", " $ map (concatMap escape) escapees ]
65−jsModule (escapees, Module names exports imports stmts) =
48+jsModule (Module names exports imports stmts) =
66− tryBlock escapees (tail modNames) $ concat
67− [ concatMap (\n -> globalAssign n $ n ++ " || {}") .
49+ concat [ concatMap (\n -> globalAssign n $ n ++ " || {}") .
68− map (intercalate ".") . drop 2 . inits $
50+ map (intercalate ".") . drop 2 . inits $
69− take (length modNames - 1) modNames
51+ take (length modNames - 1) modNames
70− , "\nif (" ++ modName ++ ") throw new Error(\"Module name collision, '" ++
71− intercalate "." (tail modNames) ++ "' is already defined.\"); "
72− , globalAssign modName $ jsFunc "" (defs ++ includes ++ body ++ export) ++ "()"
52+ , parens (jsFunc "" (defs ++ includes ++ body ++ globalAssign modName export) ++ "()")
73− , mainEquals $ modName ++ ".main" ]
53+ , mainEquals $ modName ++ ".main" ]
74− where modNames = if null names then ["Elm", "Main"]
54+ where modNames = if null names then ["Elm", "Main"]
75− else "Elm" : names
55+ else "Elm" : names
76− modName = intercalate "." modNames
56+ modName = intercalate "." modNames
77− includes = concatMap jsImport $ map (first ("Elm."++)) imports
57+ includes = concatMap jsImport imports
78− body = stmtsToJS stmts
58+ body = stmtsToJS stmts
79− export = getExports exports stmts
59+ export = getExports exports stmts
80− exps = if null exports then ["main"] else exports
60+ exps = if null exports then ["main"] else exports
81− defs = concat [ assign "$op" "{}"
61+ defs = assign "$op" "{}"
82− , "\nfor(Elm['i'] in Elm){eval('var '+Elm['i']+'=Elm[Elm.i];');}" ]
62+
84−getExports names stmts = ret . brackets $ intercalate ",\n" (op : map fnPair fns)
63+getExports names stmts = brackets . ("\n "++) $
64+ intercalate ",\n " (op : map fnPair fns)
85 where exNames n = either derename id n `elem` names
65 where exNames n = either derename id n `elem` names
86 exports | null names = concatMap get stmts
66 exports | null names = concatMap get stmts
87 | otherwise = filter exNames (concatMap get stmts)
67 | otherwise = filter exNames (concatMap get stmts)
@@ -104,75 +84,75 @@ getExports names stmts = ret . brackets $ intercalate ",\n" (op : map fnPair fns
106jsImport (modul, how) =
86jsImport (modul, how) =
107− concat [ "\ntry{\n if (!(" ++ modul ++ " instanceof Object)) throw new Error('module not found');\n} catch(e) {\n throw new Error(\"Module '"
87+ case how of
108− , drop 1 (dropWhile (/='.') modul)
109− , "' is missing. Compile with --make flag or load missing "
110− , "module in a separate JavaScript file.\");\n}" ] ++
111− jsImport' (modul, how)
113−jsImport' (modul, As name) = assign name modul
88+ As name -> assign name ("Elm." ++ modul)
114−jsImport' (modul, Importing vs) = concatMap def vs
89+ Importing vs ->
90+ assign modul ("Elm." ++ modul) ++ concatMap def vs
115− where def [] = []
91+ where
92+ imprt asgn v = asgn v ("Elm." ++ modul ++ "." ++ v)
116− def (o:p) | isOp o = let v = "$op['" ++ o:p ++ "']" in
93+ def (o:p) =
117− "\n" ++ v ++ " = " ++ modul ++ "." ++ v ++ ";"
94+ if isOp o then imprt globalAssign ("$op['" ++ o:p ++ "']")
118− | otherwise = let v = deprime (o:p) in
95+ else imprt assign (deprime (o:p))
119− assign v $ modul ++ "." ++ v
96+
121−jsImport' (modul, Hiding vs) =
122− concat [ assign "hiddenVars" . ("{"++) . (++"}") . intercalate "," $
123− map (\v -> v ++ ":true") (map deprime vs)
124− , "\nfor (Elm['i'] in " ++ modul ++ ") "
125− , brackets . indent . concat $
126− [ "\nif (hiddenVars[Elm['i']]) continue;"
127− , "\neval('var ' + Elm['i'] + ' = "
128− , modul, "[Elm.i];');" ]
131stmtsToJS :: [Statement] -> String
98stmtsToJS :: [Statement] -> String
132−stmtsToJS stmts = run (concat `liftM` mapM toJS (sortBy cmpStmt stmts))
99+stmtsToJS stmts = run $ do program <- mapM toJS (sortBy cmpStmt stmts)
100+ return (vars ++ concat program)
102+ vars = "\nvar " ++ intercalate "," (letBoundVars stmts) ++ ";"
133− where cmpStmt s1 s2 = compare (valueOf s1) (valueOf s2)
103+ cmpStmt s1 s2 = compare (valueOf s1) (valueOf s2)
134− valueOf s = case s of
104+ valueOf s = case s of
135− Datatype _ _ _ -> 1
105+ Datatype _ _ _ -> 1
136− ImportEvent _ _ _ _ -> 2
106+ ImportEvent _ _ _ _ -> 2
137− Definition (FnDef f [] _) ->
107+ Definition (FnDef f [] _) ->
138− if derename f == "main" then 5 else 4
108+ if derename f == "main" then 5 else 4
139− Definition _ -> 3
109+ Definition _ -> 3
140− ExportEvent _ _ _ -> 6
110+ ExportEvent _ _ _ -> 6
141− TypeAlias _ _ _ -> 0
111+ TypeAlias _ _ _ -> 0
142− TypeAnnotation _ _ -> 0
112+ TypeAnnotation _ _ -> 0
144class ToJS a where
114class ToJS a where
145 toJS :: a -> GuidCounter String
115 toJS :: a -> GuidCounter String
147instance ToJS Def where
117instance ToJS Def where
148− toJS (FnDef x [] e) = assign x `liftM` toJS' e
118+ toJS (FnDef x [] e) = assign' x `liftM` toJS' e
149 toJS (FnDef f (a:as) e) =
119 toJS (FnDef f (a:as) e) =
150 do body <- toJS' (foldr (\x e -> noContext (Lambda x e)) e as)
120 do body <- toJS' (foldr (\x e -> noContext (Lambda x e)) e as)
151− return $ concat ["\nfunction ",f,parens a, brackets . indent $ ret body]
121+ return $ assign' f (jsFunc a (ret body))
152 toJS (OpDef op a1 a2 e) =
122 toJS (OpDef op a1 a2 e) =
153 do body <- toJS' (foldr (\x e -> noContext (Lambda x e)) e [a1,a2])
123 do body <- toJS' (foldr (\x e -> noContext (Lambda x e)) e [a1,a2])
154 return $ concat [ "\n$op['", op, "'] = ", body, ";" ]
124 return $ concat [ "\n$op['", op, "'] = ", body, ";" ]
156instance ToJS Statement where
126instance ToJS Statement where
157− toJS (Definition d) = toJS d
129+ Definition d -> (\asgn -> "\n" ++ asgn ++ ";") `liftM` toJS d
158− toJS (Datatype _ _ tcs) = concat `liftM` mapM (toJS . toDef) tcs
130+ Datatype _ _ tcs -> concat `liftM` mapM (toJS . toDef) tcs
159− where toDef (name,args) = Definition . FnDef name vars . noContext $
131+ where toDef (name,args) =
160− Data (derename name) (map (noContext . Var) vars)
161− where vars = map (('a':) . show) [1..length args]
132+ let vars = map (('a':) . show) [1..length args] in
133+ Definition . FnDef name vars . noContext $
134+ Data (derename name) (map (noContext . Var) vars)
162− toJS (ImportEvent js base elm _) =
135+ ImportEvent js base elm _ ->
163 do v <- toJS' base
136 do v <- toJS' base
164 return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal.constant(" ++ v ++ ");"
137 return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal.constant(" ++ v ++ ");"
165 , "\nValue.addListener(document, '" ++ js
138 , "\nValue.addListener(document, '" ++ js
166 , "', function(e) { Dispatcher.notify(" ++ elm
139 , "', function(e) { Dispatcher.notify(" ++ elm
167 , ".id, e.value); });" ]
140 , ".id, e.value); });" ]
168− toJS (ExportEvent js elm _) =
141+ ExportEvent js elm _ ->
169 return $ concat [ "\nlift(function(v) { "
142 return $ concat [ "\nlift(function(v) { "
170 , "var e = document.createEvent('Event');"
143 , "var e = document.createEvent('Event');"
171 , "e.initEvent('", js, "', true, true);"
144 , "e.initEvent('", js, "', true, true);"
172 , "e.value = v;"
145 , "e.value = v;"
173 , "document.dispatchEvent(e); return v; })(", elm, ");" ]
146 , "document.dispatchEvent(e); return v; })(", elm, ");" ]
174− toJS (TypeAnnotation _ _) = return ""
147+ TypeAnnotation _ _ -> return ""
175− toJS (TypeAlias _ _ _) = return ""
148+ TypeAlias n _ t ->
150+ RecordT kvs _ -> return $ "\nfunction" ++ args ++ brackets body
151+ where fs = Map.keys kvs
152+ args = parens (intercalate "," fs)
153+ body = ret . brackets . intercalate "," $
154+ map (\k -> k ++ ":" ++ k) fs
177toJS' :: CExpr -> GuidCounter String
157toJS' :: CExpr -> GuidCounter String
178toJS' (C txt span expr) =
158toJS' (C txt span expr) =
@@ -203,7 +183,7 @@ makeRecord kvs = record `liftM` collect kvs
203instance ToJS Expr where
183instance ToJS Expr where
204 toJS expr =
184 toJS expr =
205 case expr of
185 case expr of
206− IntNum n -> return $ show n
186+ IntNum n -> return $ show n ++ "|0"
207 FloatNum n -> return $ show n
187 FloatNum n -> return $ show n
208 Var x -> return $ x
188 Var x -> return $ x
209 Chr c -> return $ quoted [c]
189 Chr c -> return $ quoted [c]
@@ -249,22 +229,27 @@ formatMarkdown = concatMap f
249 f '"' = "\""
229 f '"' = "\""
250 f c = [c]
230 f c = [c]
252−multiIfToJS span ps = format `liftM` mapM f ps
232+multiIfToJS span ps =
234+ (C _ _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
253− where format cs = foldr (\c e -> parens $ c ++ " : " ++ e) err cs
238+ foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
254− err = concat [ "(function(){throw new Error(\"Non-exhaustive "
239+ err = "throw new Error('Non-exhaustive multi-way-if expression (" ++
255− , "multi-way-if expression (", show span, ")\");}())" ]
240+ show span ++ ")')"
256− f (b,e) = do b' <- toJS' b
241+ f (b,e) = do b' <- toJS' b
257− e' <- toJS' e
242+ e' <- toJS' e
258− return (b' ++ " ? " ++ e')
243+ return (b' ++ " ? " ++ e')
260−jsLet defs e' = do
245+jsLet defs e' = do ds <- jsDefs defs
261− body <- (++) `liftM` jsDefs defs `ap` (ret `liftM` toJS' e')
246+ e <- toJS' e'
262− return $ jsFunc "" body ++ "()"
247+ return $ parens (intercalate ", " ds ++ ", " ++ e)
264−jsDefs defs = concat `liftM` mapM toJS (sortBy f defs)
249+ jsDefs defs = mapM toJS (sortBy f defs)
265− where f a b = compare (valueOf a) (valueOf b)
250+ f a b = compare (valueOf a) (valueOf b)
266− valueOf (FnDef _ args _) = min 1 (length args)
251+ valueOf (FnDef _ args _) = min 1 (length args)
267− valueOf (OpDef _ _ _ _) = 1
252+ valueOf (OpDef _ _ _ _) = 1
269caseToJS span e ps = do
254caseToJS span e ps = do
270 match <- caseToMatch ps
255 match <- caseToMatch ps
@@ -280,8 +265,9 @@ matchToJS span (Match name clauses def) = do
280 cases <- concat `liftM` mapM (clauseToJS span name) clauses
265 cases <- concat `liftM` mapM (clauseToJS span name) clauses
281 finally <- matchToJS span def
266 finally <- matchToJS span def
282 return $ concat [ "\nswitch(", name, ".ctor){", indent cases, "\n}", finally ]
267 return $ concat [ "\nswitch(", name, ".ctor){", indent cases, "\n}", finally ]
268+matchToJS span Fail =
283−matchToJS span Fail = return ("\nthrow new Error(\"Non-exhaustive pattern match " ++
269+ return ("\nthrow new Error(\"Non-exhaustive pattern match " ++
284− "in case expression (" ++ show span ++ ")\");")
270+ "in case expression (" ++ show span ++ ")\");")
285matchToJS span Break = return "break;"
271matchToJS span Break = return "break;"
286matchToJS span (Other e) = ret `liftM` toJS' e
272matchToJS span (Other e) = ret `liftM` toJS' e
287matchToJS span (Seq ms) = concat `liftM` mapM (matchToJS span) ms
273matchToJS span (Seq ms) = concat `liftM` mapM (matchToJS span) ms
@@ -291,12 +277,9 @@ clauseToJS span var (Clause name vars e) = do
291 s <- matchToJS span $ matchSubst (zip vars vars') e
277 s <- matchToJS span $ matchSubst (zip vars vars') e
292 return $ concat [ "\ncase ", quoted name, ":", s ]
278 return $ concat [ "\ncase ", quoted name, ":", s ]
294−jsNil = "[\"Nil\"]"
280+jsNil = "{ctor:'Nil'}"
295−jsCons e1 e2 = jsList [ quoted "Cons", e1, e2 ]
281+jsCons e1 e2 = "{ctor:'Cons',_0:" ++ e1 ++ ",_1:" ++ e2 ++ "}"
296−jsRange e1 e2 = (++"()") . jsFunc "" $
282+jsRange e1 e2 = "Elm.Native.List.range" ++ parens (e1 ++ "," ++ e2)
297− assign "lo" e1 ++ assign "hi" e2 ++ assign "lst" jsNil ++
298− "if(lo<=hi){do{lst=" ++ (jsCons "hi" "lst") ++ "}while(hi-->lo)}" ++
301binop (o:p) e1 e2
284binop (o:p) e1 e2
302 | isAlpha o || '_' == o = (o:p) ++ parens e1 ++ parens e2
285 | isAlpha o || '_' == o = (o:p) ++ parens e1 ++ parens e2