@@ -3,28 +3,71 @@ module CompileToJS (compile, compileToJS) where
33
44import Ast
55import Control.Monad (liftM,(<=<),join)
6−import Data.Char (isAlpha)
7−import Data.List (intercalate,sortBy)
6+import Data.Char (isAlpha,isDigit)
7+import Data.List (intercalate,sortBy,inits)
88import Data.Map (toList)
9+import Data.Maybe (mapMaybe)
910
1011import Initialize
1112
1213showErr :: String -> String
13−showErr err = "text(monospace(" ++ msg ++ "))"
14+showErr err = mainEquals $ "text(monospace(" ++ msg ++ "))"
1415 where msg = show . concatMap (++"<br>") . lines $ err
1516
16−compile = (return . addMain . toJS) <=< initialize
17−compileToJS = addMain . either showErr toJS
18−addMain body = "function main(){return " ++ body ++ ";}"
17+compile str = jsModule `liftM` initialize str
18+compileToJS = either showErr jsModule
1919
2020parens = ("("++) . (++")")
2121braces = ("{"++) . (++"}")
2222jsList = ("["++) . (++"]") . intercalate ","
2323jsFunc args body = "function(" ++ args ++ "){" ++ body ++ "}"
24−assign x e = "var " ++ x ++ "=" ++ e ++ ";"
25−ret e = "return "++ e ++";"
24+assign x e = "\nvar " ++ x ++ "=" ++ e ++ ";"
25+ret e = "\nreturn "++ e ++";"
2626iff a b c = a ++ "?" ++ b ++ ":" ++ c
2727
28+mainEquals s = globalAssign "ElmCode.main" (jsFunc "" (ret s))
29+globalAssign m s = "\n" ++ m ++ "=" ++ s ++ ";"
30+
31+tryBlock e =
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);"
37+ , "throw e;"
38+ , "};}"
39+ ]
40+
41+
42+jsModule (Module names exports imports defs) =
43+ tryBlock $ unwords
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
52+ body = jsDefs defs
53+ export = ret . braces . intercalate "," $ mapMaybe getNames defs
54+ exps = if null exports then ["main"] else exports
55+ getNames (x,_) =
56+ let y = reverse . tail . dropWhile isDigit $ reverse x in
57+ if y `elem` exps then Just $ y ++ ":" ++ x else Nothing
58+
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]"
68+ , "}" ]
69+
70+
2871toJS expr =
2972 case expr of
3073 Number n -> show n
@@ -45,13 +88,14 @@ toJS expr =
4588 Case e cases -> jsCase e cases
4689 Data name es -> jsList $ show name : map toJS es
4790
48−jsLet defs e' = jsFunc "" (defs' ++ ret (toJS e')) ++ "()"
49− where defs' = concatMap toDef $ sortBy f defs
50− f a b = compare (isLambda a) (isLambda b)
91+jsLet defs e' = jsFunc "" (jsDefs defs ++ ret (toJS e')) ++ "()"
92+
93+jsDefs defs = concatMap toDef $ sortBy f defs
94+ where f a b = compare (isLambda a) (isLambda b)
5195 isLambda (_, Lambda _ _) = 1
5296 isLambda _ = 0
5397 toDef (f, Lambda x e) =
54− "function " ++ f ++ parens x ++ braces (ret $ toJS e) ++ ";"
98+ "\nfunction " ++ f ++ parens x ++ braces (ret $ toJS e) ++ ";"
5599 toDef (x, e) = assign x (toJS e)
56100
57101jsCase e [c] = jsMatch c ++ parens (toJS e)