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