1+ # This is an optimization Elixir runs on function clauses.
2+ # Whenever a variables matches against a record (a tagged
3+ # tuple), this information is stored in order to optimize
4+ # record calls.
5+ defmodule Kernel.RecordRewriter do
6+ @ moduledoc false
7+
8+ def optimize_clause ( clause ) do
9+ optimize_clause ( clause , :orddict . new )
10+ end
11+
12+ ## Clause
13+
14+ defp optimize_clause ( { :clause , line , args , guards , body } , dict ) do
15+ { args , dict } = optimize_args ( args , dict )
16+ { body , dict , res } = optimize_body ( body , dict , [ ] )
17+ { { :clause , line , args , guards , body } , dict , res }
18+ end
19+
20+ defp optimize_args ( args , dict ) do
21+ Enum . map_reduce args , dict , fn ( arg , acc ) ->
22+ { new_arg , new_acc , _res } = optimize_expr ( arg , acc )
23+ { new_arg , new_acc }
24+ end
25+ end
26+
27+ defp optimize_body ( [ ] , dict , _acc ) do
28+ { [ ] , dict , nil }
29+ end
30+
31+ defp optimize_body ( [ h ] , dict , acc ) do
32+ { new_expr , new_dict , new_res } = optimize_expr ( h , dict )
33+ { Enum . reverse ( [ new_expr | acc ] ) , new_dict , new_res }
34+ end
35+
36+ defp optimize_body ( [ h | t ] , dict , acc ) do
37+ { new_expr , new_dict , _ } = optimize_expr ( h , dict )
38+ optimize_body ( t , new_dict , [ new_expr | acc ] )
39+ end
40+
41+ ## Expr
42+
43+ defp optimize_expr ( { :match , line , left , right } , dict ) do
44+ { left , dict , left_res } = optimize_expr ( left , dict )
45+ { right , dict , right_res } = optimize_expr ( right , dict )
46+
47+ match = { :match , line , left , right }
48+
49+ if right_res do
50+ dict = assign_vars ( extract_vars ( left , [ ] ) , dict , right_res )
51+ end
52+
53+ if left_res do
54+ dict = assign_vars ( extract_vars ( right , [ ] ) , dict , left_res )
55+ end
56+
57+ { match , dict , right_res || left_res }
58+ end
59+
60+ defp optimize_expr ( { :tuple , line , args } , dict ) do
61+ { args , dict , args_res } = optimize_tuple_args ( args , dict )
62+
63+ res =
64+ case args do
65+ [ { :atom , _ , atom } | t ] -> if is_record? ( atom ) , do: atom
66+ _ -> nil
67+ end
68+
69+ { { :tuple , line , args } , dict , { res , args_res } }
70+ end
71+
72+ defp optimize_expr ( { :var , _ , name } = var , dict ) do
73+ case :orddict . find ( name , dict ) do
74+ { :ok , res } -> { var , dict , { res , nil } }
75+ :error -> { var , dict , nil }
76+ end
77+ end
78+
79+ defp optimize_expr ( other , dict ) do
80+ { other , dict , nil }
81+ end
82+
83+ ## Match related
84+
85+ defp optimize_tuple_args ( args , dict ) do
86+ { final_args , { final_dict , final_acc } } =
87+ Enum . map_reduce args , { dict , [ ] } , fn ( arg , { acc_dict , acc_res } ) ->
88+ { new_arg , new_acc , res } = optimize_expr ( arg , acc_dict )
89+ { new_arg , { new_acc , [ res | acc_res ] } }
90+ end
91+
92+ { final_args , final_dict , Enum . reverse ( final_acc ) }
93+ end
94+
95+ defp assign_vars ( [ key | t ] , dict , { _ , value } = res ) when is_list ( key ) and is_list ( value ) and length ( key ) == length ( value ) do
96+ assign_vars t , assign_nested_vars ( key , dict , value ) , res
97+ end
98+
99+ defp assign_vars ( [ key | t ] , dict , { value , _ } = res ) when is_atom ( key ) and value != nil do
100+ assign_vars t , :orddict . store ( key , value , dict ) , res
101+ end
102+
103+ defp assign_vars ( [ _ | t ] , dict , res ) do
104+ assign_vars t , dict , res
105+ end
106+
107+ defp assign_vars ( [ ] , dict , _res ) do
108+ dict
109+ end
110+
111+ defp assign_nested_vars ( [ vars | vt ] , dict , [ res | rt ] ) do
112+ assign_nested_vars ( vt , assign_vars ( vars , dict , res ) , rt )
113+ end
114+
115+ defp assign_nested_vars ( [ ] , dict , [ ] ) do
116+ dict
117+ end
118+
119+ defp extract_vars ( { :match , _ , left , right } , vars ) do
120+ vars = extract_vars ( right , vars )
121+ extract_vars ( left , vars )
122+ end
123+
124+ defp extract_vars ( { :var , _ , name } , vars ) do
125+ [ name | vars ]
126+ end
127+
128+ defp extract_vars ( { :tuple , _ , args } , vars ) do
129+ [ Enum . map ( args , extract_vars ( & 1 , [ ] ) ) | vars ]
130+ end
131+
132+ defp extract_vars ( _ , vars ) do
133+ vars
134+ end
135+
136+ ## Record helpers
137+
138+ # TODO: Implement proper record check
139+ defp is_record? ( _h ) do
140+ true
141+ end
142+ end
0 commit comments