on master help sign in
Make pattern matching significantly faster. Whoo! Bump to version 0.4.
commitd51542067ac946247252885ddaf5ff6c49c6f2aa authorevancz <info@elm-lang.org> dateSat 25 Aug 2012, 16:02 (14 years ago) parentcf011a0
3 files changed+131−48
elm/Elm.cabal+6−2 elm/src/Gen/Cases.hs+68 elm/src/Gen/CompileToJS.hs+57−46
elm/Elm.cabal+6−2
@@ -1,6 +1,6 @@
11
22Name: Elm
3−Version: 0.3.6.2
3+Version: 0.4.0
44Synopsis: The Elm language module.
55Description: Elm aims to make client-side web-development more pleasant.
66 It is a statically/strongly typed, functional reactive
@@ -22,7 +22,7 @@ Category: Compiler, Language
2222Build-type: Simple
2323
2424Extra-source-files: changelog.txt
25−Data-files: elm-runtime-0.3.6.2.js
25+Data-files: elm-runtime-0.4.0.js
2626Cabal-version: >=1.8
2727
2828source-repository head
@@ -35,6 +35,8 @@ Library
3535 Hs-Source-Dirs: src, src/Parse, src/Types, src/Gen
3636 other-modules: Ast,
3737 CompileToJS,
38+ Cases,
39+ Substitute,
3840 ExtractNoscript,
3941 GenerateHtml,
4042 Guid,
@@ -72,6 +74,8 @@ Executable elm
7274 Hs-Source-Dirs: src, src/Parse, src/Types, src/Gen
7375 other-modules: Ast,
7476 CompileToJS,
77+ Cases,
78+ Substitute,
7579 ExtractNoscript,
7680 GenerateHtml,
7781 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
33
44import Ast
55import Control.Arrow (first)
6−import Control.Monad (liftM,(<=<),join)
6+import Control.Monad (liftM,(<=<),join,ap)
77import Data.Char (isAlpha,isDigit)
88import Data.List (intercalate,sortBy,inits)
99import Data.Map (toList)
@@ -11,6 +11,8 @@ import Data.Maybe (mapMaybe)
1111
1212import Initialize
1313import Rename (derename)
14+import Cases
15+import Guid
1416
1517showErr :: String -> String
1618showErr err = mainEquals $ "text(monospace(" ++ msg ++ "))"
@@ -116,58 +118,67 @@ stmtToJS (ExportEvent js elm _) =
116118 ]
117119
118120
121+toJS = run . toJS'
119122
120−toJS :: Expr -> String
121−toJS expr =
123+toJS' :: Expr -> GuidCounter String
124+toJS' expr =
122125 case expr of
123− IntNum n -> show n
124− FloatNum n -> show n
125− Var x -> x
126− Chr c -> show c
127− Str s -> "Value.str" ++ parens (show s)
128− Boolean b -> if b then "true" else "false"
129− Range lo hi -> jsRange (toJS lo) (toJS hi)
130− Access e lbl -> toJS e ++ "." ++ lbl
131− Binop op e1 e2 -> binop op (toJS e1) (toJS e2)
132− If eb et ef -> parens $ iff (toJS eb) (toJS et) (toJS ef)
133− Lambda v e -> jsFunc v $ ret (toJS e)
134− App (Var "toText") (Str s) -> "toText" ++ parens (show s)
135− App (Var "link") (Str s) -> "link(" ++ show s ++ ")"
136− App (Var "plainText") (Str s) -> "plainText(" ++ show s ++ ")"
137− App e1 e2 -> toJS e1 ++ parens (toJS e2)
126+ IntNum n -> return $ show n
127+ FloatNum n -> return $ show n
128+ Var x -> return $ x
129+ Chr c -> return $ show c
130+ Str s -> return $ "Value.str" ++ parens (show s)
131+ Boolean b -> return $ if b then "true" else "false"
132+ Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
133+ Access e lbl -> (\s -> s ++ "." ++ lbl) `liftM` toJS' e
134+ Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
135+ If eb et ef -> parens `liftM` (iff `liftM` toJS' eb `ap` toJS' et `ap` toJS' ef)
136+ Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
137+ App (Var "toText") (Str s) -> return $ "toText" ++ parens (show s)
138+ App (Var "link") (Str s) -> return $ "link(" ++ show s ++ ")"
139+ App (Var "plainText") (Str s) -> return $ "plainText(" ++ show s ++ ")"
140+ App e1 e2 -> (++) `liftM` (toJS' e1) `ap` (parens `liftM` toJS' e2)
138141 Let defs e -> jsLet defs e
139− Case e cases -> jsCase e cases
140− Data name es -> jsList $ show name : map toJS es
142+ Case e cases -> caseToJS e cases
143+ Data name es -> (\ss -> jsList $ show name : ss) `liftM` mapM toJS' es
141144
142−jsLet defs e' = jsFunc "" (jsDefs defs ++ ret (toJS e')) ++ "()"
145+jsLet defs e' = do
146+ body <- (++) `liftM` jsDefs defs `ap` (ret `liftM` toJS' e')
147+ return $ jsFunc "" body ++ "()"
143148
144−jsDefs defs = concatMap toDef $ sortBy f defs
149+jsDefs defs = concat `liftM` mapM toDef (sortBy f defs)
145150 where f a b = compare (valueOf a) (valueOf b)
146151 valueOf (Definition _ args _) = min 1 (length args)
147− toDef (Definition x [] e) = assign x (toJS e)
148− toDef (Definition f (a:as) e) =
149− "\nfunction " ++ f ++ parens a ++ braces (ret . toJS $ foldr Lambda e as) ++ ";"
150−
151−jsCase e [c] = jsMatch c ++ parens (toJS e)
152−jsCase e cases = "(function(){" ++
153− assign "v" (toJS e) ++
154− assign "c" jsCases ++
155− "for(var i=c.length;i--;){" ++
156− assign "r" "c[i](v)" ++
157− "if(r!==undefined){return r;}}}())"
158− where jsCases = jsList $ map jsMatch (reverse cases)
159−
160−jsMatch (p,e) = jsFunc "v" . match p "v" . ret $ toJS e
161−match p v hole =
162− case p of
163− PAnything -> hole
164− PVar x -> assign x v ++ hole
165− PData name ps ->
166− "if(" ++ show name ++ "!==" ++ v ++
167− "[0]){return undefined;}else{"++body++"}"
168− where matches = zipWith match ps vs
169− vs = map (\i -> v++"["++show (i+1)++"]") [0..length ps-1]
170− body = foldr ($) hole matches
152+ toDef (Definition x [] e) = assign x `liftM` toJS' e
153+ toDef (Definition f (a:as) e) = do
154+ out <- toJS' $ foldr Lambda e as
155+ return $ "\nfunction " ++ f ++ parens a ++ braces (ret out) ++ ";"
156+
157+caseToJS e ps = do
158+ match <- caseToMatch ps
159+ var <- liftM (\n -> "vv" ++ show n) guid
160+ matches <- matchToJS var match
161+ e' <- toJS' e
162+ return $ concat [ "function(", var, "){"
163+ , case match of { Match name _ _ -> assign name var ; _ -> "" }
164+ , matches
165+ , "}", parens e' ]
166+
167+matchToJS v (Match name clauses def) = do
168+ cases <- concat `liftM` mapM (clauseToJS name) clauses
169+ finally <- matchToJS v def
170+ return $ concat [ "\nswitch(", name, "[0]){", cases, "\n}", finally ]
171+matchToJS _ Fail = return "\nthrow \"Non-exhaustive pattern match in case\";"
172+matchToJS _ Break = return "break;"
173+matchToJS _ (Other e) = ret `liftM` toJS' e
174+matchToJS v (Seq ms) = concat `liftM` mapM (matchToJS v) ms
175+
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 ++ "]")
171182
172183jsNil = "[\"Nil\"]"
173184jsCons e1 e2 = jsList [ show "Cons", e1, e2 ]