1{-# OPTIONS_GHC -Wall #-}
8import Control.Arrow (second)
9import qualified Data.Map as Map
11import qualified Data.Maybe as Maybe
13import qualified AST.Canonical as Can
14import qualified AST.Optimized as Opt
15import qualified Elm.Name as N
16import qualified Optimize.DecisionTree as DT
20-- OPTIMIZE A CASE EXPRESSION
23optimize :: N.Name -> [(Can.Pattern, Opt.Expr)] -> Opt.Expr
24optimize exprName optBranches =
26 (patterns, indexedBranches) =
27 unzip (zipWith indexify [0..] optBranches)
29 treeToExpr exprName (DT.compile patterns) indexedBranches
32indexify :: Int -> (a,b) -> ((a,Int), (Int,b))
33indexify index (pattern, branch) =
40-- CONVERT A TREE TO AN EXPRESSION
43treeToExpr :: N.Name -> DT.DecisionTree -> [(Int, Opt.Expr)] -> Opt.Expr
44treeToExpr name decisionTree allJumps =
47 treeToDecider decisionTree
52 (choices, maybeJumps) =
53 unzip (map (createChoices targetCounts) allJumps)
57 (insertChoices (Map.fromList choices) decider)
58 (Maybe.catMaybes maybeJumps)
64-- Decision trees may have some redundancies, so we convert them to a Decider
65-- which has special constructs to avoid code duplication when possible.
68treeToDecider :: DT.DecisionTree -> Opt.Decider Int
75 DT.Decision _ [] Nothing ->
76 error "compiler bug, somehow created an empty decision tree"
79 DT.Decision _ [(_, subTree)] Nothing ->
82 DT.Decision _ [] (Just subTree) ->
86 DT.Decision path [(test, successTree)] (Just failureTree) ->
87 toChain path test successTree failureTree
89 DT.Decision path [(test, successTree), (_, failureTree)] Nothing ->
90 toChain path test successTree failureTree
93 DT.Decision path edges Nothing ->
95 (necessaryTests, fallback) =
96 (init edges, snd (last edges))
100 (map (second treeToDecider) necessaryTests)
101 (treeToDecider fallback)
103 DT.Decision path edges (Just fallback) ->
104 Opt.FanOut path (map (second treeToDecider) edges) (treeToDecider fallback)
107toChain :: DT.Path -> DT.Test -> DT.DecisionTree -> DT.DecisionTree -> Opt.Decider Int
108toChain path test successTree failureTree =
111 treeToDecider failureTree
113 case treeToDecider successTree of
114 Opt.Chain testChain success subFailure | failure == subFailure ->
115 Opt.Chain ((path, test) : testChain) success failure
118 Opt.Chain [(path, test)] success failure
124-- If a target appears exactly once in a Decider, the corresponding expression
125-- can be inlined. Whether things are inlined or jumps is called a "choice".
128countTargets :: Opt.Decider Int -> Map.Map Int Int
129countTargets decisionTree =
132 Map.singleton target 1
134 Opt.Chain _ success failure ->
135 Map.unionWith (+) (countTargets success) (countTargets failure)
137 Opt.FanOut _ tests fallback ->
138 Map.unionsWith (+) (map countTargets (fallback : map snd tests))
144 -> ( (Int, Opt.Choice), Maybe (Int, Opt.Expr) )
145createChoices targetCounts (target, branch) =
146 if targetCounts ! target == 1 then
147 ( (target, Opt.Inline branch)
152 ( (target, Opt.Jump target)
153 , Just (target, branch)
158 :: Map.Map Int Opt.Choice
160 -> Opt.Decider Opt.Choice
161insertChoices choiceDict decider =
164 insertChoices choiceDict
168 Opt.Leaf (choiceDict ! target)
170 Opt.Chain testChain success failure ->
171 Opt.Chain testChain (go success) (go failure)
173 Opt.FanOut path tests fallback ->
174 Opt.FanOut path (map (second go) tests) (go fallback)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list