@@ -3,28 +3,71 @@ module CompileToJS (compile, compileToJS) where
5import Control.Monad (liftM,(<=<),join)
5import Control.Monad (liftM,(<=<),join)
6−import Data.Char (isAlpha)
6+import Data.Char (isAlpha,isDigit)
7−import Data.List (intercalate,sortBy)
7+import Data.List (intercalate,sortBy,inits)
8import Data.Map (toList)
8import Data.Map (toList)
9+import Data.Maybe (mapMaybe)
10import Initialize
11import Initialize
12showErr :: String -> String
13showErr :: String -> String
13−showErr err = "text(monospace(" ++ msg ++ "))"
14+showErr err = mainEquals $ "text(monospace(" ++ msg ++ "))"
14 where msg = show . concatMap (++"<br>") . lines $ err
15 where msg = show . concatMap (++"<br>") . lines $ err
16−compile = (return . addMain . toJS) <=< initialize
17+compile str = jsModule `liftM` initialize str
17−compileToJS = addMain . either showErr toJS
18+compileToJS = either showErr jsModule
18−addMain body = "function main(){return " ++ body ++ ";}"
20parens = ("("++) . (++")")
20parens = ("("++) . (++")")
21braces = ("{"++) . (++"}")
21braces = ("{"++) . (++"}")
22jsList = ("["++) . (++"]") . intercalate ","
22jsList = ("["++) . (++"]") . intercalate ","
23jsFunc args body = "function(" ++ args ++ "){" ++ body ++ "}"
23jsFunc args body = "function(" ++ args ++ "){" ++ body ++ "}"
24−assign x e = "var " ++ x ++ "=" ++ e ++ ";"
24+assign x e = "\nvar " ++ x ++ "=" ++ e ++ ";"
25−ret e = "return "++ e ++";"
25+ret e = "\nreturn "++ e ++";"
26iff a b c = a ++ "?" ++ b ++ ":" ++ c
26iff a b c = a ++ "?" ++ b ++ ":" ++ c
28+mainEquals s = globalAssign "ElmCode.main" (jsFunc "" (ret s))
29+globalAssign m s = "\n" ++ m ++ "=" ++ s ++ ";"
32+ unlines [ "\ntry{\n" ++ e ++ "\n\n} catch (e) {"
33+ , "ElmCode.main=function() {"
34+ , "var msg = ('<br><h2>Your browser may not be supported. Are you using a modern browser?</h2>' +" ++
35+ " '<br><span style=\"grey\">Runtime Error:<br>' + e + '</span>')"
36+ , "document.body.innerHTML = Text.monospace(msg);"
42+jsModule (Module names exports imports defs) =
44+ [ concatMap (\n -> globalAssign n $ n ++ " || {}") . map (intercalate ".") . drop 2 . inits $ take (length modNames - 1) modNames
45+ , "\nif (" ++ modName ++ ") throw 'Module name collision, " ++ intercalate "." names ++ " is already defined.'; "
46+ , globalAssign modName $ jsFunc "" (includes ++ body ++ export) ++ "()"
47+ , mainEquals $ modName ++ ".main" ]
48+ where modNames = if null names then ["ElmCode", "Main"] else "ElmCode" : names
49+ modName = intercalate "." modNames
50+ includes = concatMap jsImport $
51+ map (\(x,y) -> ("ElmCode." ++ x,y)) imports
53+ export = ret . braces . intercalate "," $ mapMaybe getNames defs
54+ exps = if null exports then ["main"] else exports
56+ let y = reverse . tail . dropWhile isDigit $ reverse x in
57+ if y `elem` exps then Just $ y ++ ":" ++ x else Nothing
59+jsImport (modul, As name) = assign name modul
60+jsImport (modul, Importing []) = jsImport (modul, Hiding [])
61+jsImport (modul, Importing vs) =
62+ concatMap (\v -> assign v $ modul ++ "." ++ v) vs
63+jsImport (modul, Hiding vs) =
64+ unwords [ "\nfor(var i in " ++ modul ++ "){"
65+ , assign "hiddenVars" . jsList $ map (\v -> "'" ++ v ++ "'") vs
66+ , "\nif (hiddenVars.indexOf(i) >= 0) continue;"
67+ , globalAssign "this[i]" $ modul ++ "[i]"
28toJS expr =
71toJS expr =
29 case expr of
72 case expr of
30 Number n -> show n
73 Number n -> show n
@@ -45,13 +88,14 @@ toJS expr =
45 Case e cases -> jsCase e cases
88 Case e cases -> jsCase e cases
46 Data name es -> jsList $ show name : map toJS es
89 Data name es -> jsList $ show name : map toJS es
48−jsLet defs e' = jsFunc "" (defs' ++ ret (toJS e')) ++ "()"
91+jsLet defs e' = jsFunc "" (jsDefs defs ++ ret (toJS e')) ++ "()"
49− where defs' = concatMap toDef $ sortBy f defs
93+jsDefs defs = concatMap toDef $ sortBy f defs
50− f a b = compare (isLambda a) (isLambda b)
94+ where f a b = compare (isLambda a) (isLambda b)
51 isLambda (_, Lambda _ _) = 1
95 isLambda (_, Lambda _ _) = 1
52 isLambda _ = 0
96 isLambda _ = 0
53 toDef (f, Lambda x e) =
97 toDef (f, Lambda x e) =
54− "function " ++ f ++ parens x ++ braces (ret $ toJS e) ++ ";"
98+ "\nfunction " ++ f ++ parens x ++ braces (ret $ toJS e) ++ ";"
55 toDef (x, e) = assign x (toJS e)
99 toDef (x, e) = assign x (toJS e)
57jsCase e [c] = jsMatch c ++ parens (toJS e)
101jsCase e [c] = jsMatch c ++ parens (toJS e)