on master help sign in
Make pattern matching significantly faster. Whoo! Bump to version 0.4.
d515420evancz
elm/Elm.cabal+6−2
@@ -1,6 +1,6 @@
1
1
2Name: Elm
2Name: Elm
3−Version: 0.3.6.2
3+Version: 0.4.0
4Synopsis: The Elm language module.
4Synopsis: The Elm language module.
5Description: Elm aims to make client-side web-development more pleasant.
5Description: Elm aims to make client-side web-development more pleasant.
6 It is a statically/strongly typed, functional reactive
6 It is a statically/strongly typed, functional reactive
@@ -22,7 +22,7 @@ Category: Compiler, Language
22Build-type: Simple
22Build-type: Simple
23
23
24Extra-source-files: changelog.txt
24Extra-source-files: changelog.txt
25−Data-files: elm-runtime-0.3.6.2.js
25+Data-files: elm-runtime-0.4.0.js
26Cabal-version: >=1.8
26Cabal-version: >=1.8
27
27
28source-repository head
28source-repository head
@@ -35,6 +35,8 @@ Library
35 Hs-Source-Dirs: src, src/Parse, src/Types, src/Gen
35 Hs-Source-Dirs: src, src/Parse, src/Types, src/Gen
36 other-modules: Ast,
36 other-modules: Ast,
37 CompileToJS,
37 CompileToJS,
38+ Cases,
39+ Substitute,
38 ExtractNoscript,
40 ExtractNoscript,
39 GenerateHtml,
41 GenerateHtml,
40 Guid,
42 Guid,
@@ -72,6 +74,8 @@ Executable elm
72 Hs-Source-Dirs: src, src/Parse, src/Types, src/Gen
74 Hs-Source-Dirs: src, src/Parse, src/Types, src/Gen
73 other-modules: Ast,
75 other-modules: Ast,
74 CompileToJS,
76 CompileToJS,
77+ Cases,
78+ Substitute,
75 ExtractNoscript,
79 ExtractNoscript,
76 GenerateHtml,
80 GenerateHtml,
77 Guid,
81 Guid,
elm/src/Gen/Cases.hsnew file+68
@@ -0,0 +1,68 @@
1+
2+module Cases (caseToMatch, Match (..), Clause (..)) where
3+
4+import Control.Arrow (first)
5+import Control.Monad (liftM,foldM)
6+import Data.List (groupBy,sortBy)
7+
8+import Ast
9+import Guid
10+import Substitute
11+
12+import System.IO.Unsafe
13+
14+caseToMatch patterns = do
15+ v <- newVar
16+ match [v] (map (first (:[])) patterns) Fail
17+
18+newVar = do n <- guid
19+ return $ "case" ++ show n
20+
21+data Match = Match String [Clause] Match
22+ | Break
23+ | Fail
24+ | Other Expr
25+ | Seq [Match]
26+ deriving Show
27+
28+data Clause = Clause String [String] Match
29+ deriving Show
30+
31+isCon (PData _ _ : _, _) = True
32+isCon _ = False
33+
34+isVar p = not (isCon p)
35+
36+match :: [String] -> [([Pattern],Expr)] -> Match -> GuidCounter Match
37+match [] [] def = return def
38+match [] [([],e)] Fail = return $ Other e
39+match [] [([],e)] Break = return $ Other e
40+match [] cs def = return $ Seq (map (Other . snd) cs ++ [def])
41+match vs cs def
42+ | all isVar cs = matchVar vs cs def
43+ | all isCon cs = matchCon vs cs def
44+ | otherwise = matchMix vs cs def
45+
46+matchVar :: [String] -> [([Pattern],Expr)] -> Match -> GuidCounter Match
47+matchVar (v:vs) cs def = match vs (map subVar cs) def
48+ where subVar (PVar x : ps, e) = (ps, subst v x e)
49+ subVar (PAnything : ps, e) = (ps, e)
50+
51+matchCon :: [String] -> [([Pattern],Expr)] -> Match -> GuidCounter Match
52+matchCon (v:vs) cs def = (flip (Match v) def) `liftM` mapM toClause css
53+ where css = groupBy (withName (==)) $ sortBy (withName compare) cs
54+ withName f (PData n1 _:_,_) (PData n2 _:_,_) = f n1 n2
55+ toClause cs = let (PData name _ : _ , _) = head cs in
56+ matchClause name (v:vs) cs Break
57+
58+matchClause :: String -> [String] -> [([Pattern],Expr)] -> Match -> GuidCounter Clause
59+matchClause c (v:vs) cs def =
60+ do vs' <- getVars
61+ Clause c vs' `liftM` match (vs' ++ vs) (map flatten cs) def
62+ where flatten (PData _ ps' : ps, e) = (ps' ++ ps, e)
63+ getVars = let (PData _ ps : _, _) = head cs in
64+ mapM (\_ -> newVar) ps
65+
66+matchMix :: [String] -> [([Pattern],Expr)] -> Match -> GuidCounter Match
67+matchMix vs cs def = foldM (flip $ match vs) def (reverse css)
68+ where css = groupBy (\p1 p2 -> isCon p1 == isCon p2) cs
elm/src/Gen/CompileToJS.hs+57−46
@@ -3,7 +3,7 @@ module CompileToJS (showErr, jsModule) where
3
3
4import Ast
4import Ast
5import Control.Arrow (first)
5import Control.Arrow (first)
6−import Control.Monad (liftM,(<=<),join)
6+import Control.Monad (liftM,(<=<),join,ap)
7import Data.Char (isAlpha,isDigit)
7import Data.Char (isAlpha,isDigit)
8import Data.List (intercalate,sortBy,inits)
8import Data.List (intercalate,sortBy,inits)
9import Data.Map (toList)
9import Data.Map (toList)
@@ -11,6 +11,8 @@ import Data.Maybe (mapMaybe)
11
11
12import Initialize
12import Initialize
13import Rename (derename)
13import Rename (derename)
14+import Cases
15+import Guid
14
16
15showErr :: String -> String
17showErr :: String -> String
16showErr err = mainEquals $ "text(monospace(" ++ msg ++ "))"
18showErr err = mainEquals $ "text(monospace(" ++ msg ++ "))"
@@ -116,58 +118,67 @@ stmtToJS (ExportEvent js elm _) =
116 ]
118 ]
117
119
118
120
121+toJS = run . toJS'
119
122
120−toJS :: Expr -> String
123+toJS' :: Expr -> GuidCounter String
121−toJS expr =
124+toJS' expr =
122 case expr of
125 case expr of
123− IntNum n -> show n
126+ IntNum n -> return $ show n
124− FloatNum n -> show n
127+ FloatNum n -> return $ show n
125− Var x -> x
128+ Var x -> return $ x
126− Chr c -> show c
129+ Chr c -> return $ show c
127− Str s -> "Value.str" ++ parens (show s)
130+ Str s -> return $ "Value.str" ++ parens (show s)
128− Boolean b -> if b then "true" else "false"
131+ Boolean b -> return $ if b then "true" else "false"
129− Range lo hi -> jsRange (toJS lo) (toJS hi)
132+ Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
130− Access e lbl -> toJS e ++ "." ++ lbl
133+ Access e lbl -> (\s -> s ++ "." ++ lbl) `liftM` toJS' e
131− Binop op e1 e2 -> binop op (toJS e1) (toJS e2)
134+ Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
132− If eb et ef -> parens $ iff (toJS eb) (toJS et) (toJS ef)
135+ If eb et ef -> parens `liftM` (iff `liftM` toJS' eb `ap` toJS' et `ap` toJS' ef)
133− Lambda v e -> jsFunc v $ ret (toJS e)
136+ Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
134− App (Var "toText") (Str s) -> "toText" ++ parens (show s)
137+ App (Var "toText") (Str s) -> return $ "toText" ++ parens (show s)
135− App (Var "link") (Str s) -> "link(" ++ show s ++ ")"
138+ App (Var "link") (Str s) -> return $ "link(" ++ show s ++ ")"
136− App (Var "plainText") (Str s) -> "plainText(" ++ show s ++ ")"
139+ App (Var "plainText") (Str s) -> return $ "plainText(" ++ show s ++ ")"
137− App e1 e2 -> toJS e1 ++ parens (toJS e2)
140+ App e1 e2 -> (++) `liftM` (toJS' e1) `ap` (parens `liftM` toJS' e2)
138 Let defs e -> jsLet defs e
141 Let defs e -> jsLet defs e
139− Case e cases -> jsCase e cases
142+ Case e cases -> caseToJS e cases
140− Data name es -> jsList $ show name : map toJS es
143+ Data name es -> (\ss -> jsList $ show name : ss) `liftM` mapM toJS' es
141
144
145+jsLet defs e' = do
142−jsLet defs e' = jsFunc "" (jsDefs defs ++ ret (toJS e')) ++ "()"
146+ body <- (++) `liftM` jsDefs defs `ap` (ret `liftM` toJS' e')
147+ return $ jsFunc "" body ++ "()"
143
148
144−jsDefs defs = concatMap toDef $ sortBy f defs
149+jsDefs defs = concat `liftM` mapM toDef (sortBy f defs)
145 where f a b = compare (valueOf a) (valueOf b)
150 where f a b = compare (valueOf a) (valueOf b)
146 valueOf (Definition _ args _) = min 1 (length args)
151 valueOf (Definition _ args _) = min 1 (length args)
147− toDef (Definition x [] e) = assign x (toJS e)
152+ toDef (Definition x [] e) = assign x `liftM` toJS' e
148− toDef (Definition f (a:as) e) =
153+ toDef (Definition f (a:as) e) = do
154+ out <- toJS' $ foldr Lambda e as
149− "\nfunction " ++ f ++ parens a ++ braces (ret . toJS $ foldr Lambda e as) ++ ";"
155+ return $ "\nfunction " ++ f ++ parens a ++ braces (ret out) ++ ";"
150−
156+
151−jsCase e [c] = jsMatch c ++ parens (toJS e)
157+caseToJS e ps = do
152−jsCase e cases = "(function(){" ++
158+ match <- caseToMatch ps
153− assign "v" (toJS e) ++
159+ var <- liftM (\n -> "vv" ++ show n) guid
154− assign "c" jsCases ++
160+ matches <- matchToJS var match
155− "for(var i=c.length;i--;){" ++
161+ e' <- toJS' e
156− assign "r" "c[i](v)" ++
162+ return $ concat [ "function(", var, "){"
157− "if(r!==undefined){return r;}}}())"
163+ , case match of { Match name _ _ -> assign name var ; _ -> "" }
158− where jsCases = jsList $ map jsMatch (reverse cases)
164+ , matches
159−
165+ , "}", parens e' ]
160−jsMatch (p,e) = jsFunc "v" . match p "v" . ret $ toJS e
166+
161−match p v hole =
167+matchToJS v (Match name clauses def) = do
162− case p of
168+ cases <- concat `liftM` mapM (clauseToJS name) clauses
163− PAnything -> hole
169+ finally <- matchToJS v def
164− PVar x -> assign x v ++ hole
170+ return $ concat [ "\nswitch(", name, "[0]){", cases, "\n}", finally ]
165− PData name ps ->
171+matchToJS _ Fail = return "\nthrow \"Non-exhaustive pattern match in case\";"
166− "if(" ++ show name ++ "!==" ++ v ++
172+matchToJS _ Break = return "break;"
167− "[0]){return undefined;}else{"++body++"}"
173+matchToJS _ (Other e) = ret `liftM` toJS' e
168− where matches = zipWith match ps vs
174+matchToJS v (Seq ms) = concat `liftM` mapM (matchToJS v) ms
169− vs = map (\i -> v++"["++show (i+1)++"]") [0..length ps-1]
175+
170− body = foldr ($) hole matches
176+clauseToJS var (Clause name vars e) = do
177+ s <- matchToJS var e
178+ return $ concat [ "\ncase ", show name, ":"
179+ , concat $ zipWith toDef vars [ 1 .. length vars ]
180+ , s ]
181+ where toDef v n = assign v (var ++ "[" ++ show n ++ "]")
171
182
172jsNil = "[\"Nil\"]"
183jsNil = "[\"Nil\"]"
173jsCons e1 e2 = jsList [ show "Cons", e1, e2 ]
184jsCons e1 e2 = jsList [ show "Cons", e1, e2 ]