~ubuntu-branches/debian/squeeze/erlang/squeeze

« back to all changes in this revision

Viewing changes to lib/stdlib/src/erl_expand_records.erl

  • Committer: Bazaar Package Importer
  • Author(s): Erlang Packagers, Sergei Golovan
  • Date: 2006-12-03 17:07:44 UTC
  • mfrom: (2.1.11 feisty)
  • Revision ID: james.westby@ubuntu.com-20061203170744-rghjwupacqlzs6kv
Tags: 1:11.b.2-4
[ Sergei Golovan ]
Fixed erlang-base and erlang-base-hipe prerm scripts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%% ``The contents of this file are subject to the Erlang Public License,
 
2
%% Version 1.1, (the "License"); you may not use this file except in
 
3
%% compliance with the License. You should have received a copy of the
 
4
%% Erlang Public License along with this software. If not, it can be
 
5
%% retrieved via the world wide web at http://www.erlang.org/.
 
6
%% 
 
7
%% Software distributed under the License is distributed on an "AS IS"
 
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 
9
%% the License for the specific language governing rights and limitations
 
10
%% under the License.
 
11
%% 
 
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 
14
%% AB. All Rights Reserved.''
 
15
%% 
 
16
%%     $Id $
 
17
%%
 
18
%% Purpose : Expand records into tuples.
 
19
 
 
20
%% N.B. Although structs (tagged tuples) are not yet allowed in the
 
21
%% language there is code included in pattern/2 and expr/3 (commented out)
 
22
%% that handles them.
 
23
 
 
24
-module(erl_expand_records).
 
25
 
 
26
-export([module/2]).
 
27
 
 
28
-import(lists,   [map/2,foldl/3,foldr/3,sort/1,reverse/1,duplicate/2]).
 
29
 
 
30
-record(exprec, {compile=[],          % Compile flags
 
31
                 vcount=0,            % Variable counter
 
32
                 imports=[],          % Imports
 
33
                 records=dict:new(),  % Record definitions
 
34
                 strict_ra=[],        % strict record accesses
 
35
                 checked_ra=[]        % succesfully accessed records
 
36
                }).
 
37
 
 
38
%% Is is assumed that Fs is a valid list of forms. It should pass
 
39
%% erl_lint without errors.
 
40
module(Fs0, Opts0) ->
 
41
    Opts = compiler_options(Fs0) ++ Opts0,
 
42
    St0 = #exprec{compile = Opts},
 
43
    {Fs,_St} = forms(Fs0, St0),
 
44
    Fs.
 
45
 
 
46
compiler_options(Forms) ->
 
47
    lists:flatten([C || {attribute,_,compile,C} <- Forms]).
 
48
    
 
49
forms([{attribute,_L,record,{Name,Defs}} | Fs], St0) ->
 
50
    NDefs = normalise_fields(Defs),
 
51
    St = St0#exprec{records=dict:store(Name, NDefs, St0#exprec.records)},
 
52
    forms(Fs, St);
 
53
forms([{attribute,L,import,Is} | Fs0], St0) ->
 
54
    St1 = import(Is, St0),
 
55
    {Fs,St2} = forms(Fs0, St1),
 
56
    {[{attribute,L,import,Is} | Fs], St2};
 
57
forms([{function,L,N,A,Cs0} | Fs0], St0) ->
 
58
    {Cs,St1} = clauses(Cs0, St0),
 
59
    {Fs,St2} = forms(Fs0, St1),
 
60
    {[{function,L,N,A,Cs} | Fs],St2};
 
61
forms([F | Fs0], St0) ->
 
62
    {Fs,St} = forms(Fs0, St0),
 
63
    {[F | Fs], St};
 
64
forms([], St) -> {[],St}.
 
65
 
 
66
clauses([{clause,Line,H0,G0,B0} | Cs0], St0) ->
 
67
    {H,St1} = head(H0, St0),
 
68
    {G,St2} = guard(G0, St1),
 
69
    {B,St3} = exprs(B0, St2),
 
70
    {Cs,St4} = clauses(Cs0, St3),
 
71
    {[{clause,Line,H,G,B} | Cs],St4};
 
72
clauses([], St) -> {[],St}.
 
73
 
 
74
head(As, St) -> pattern_list(As, St).
 
75
 
 
76
pattern({var,_,'_'}=Var, St) ->
 
77
    {Var,St};
 
78
pattern({var,_,_}=Var, St) ->
 
79
    {Var,St};
 
80
pattern({char,_,_}=Char, St) ->
 
81
    {Char,St};
 
82
pattern({integer,_,_}=Int, St) ->
 
83
    {Int,St};
 
84
pattern({float,_,_}=Float, St) ->
 
85
    {Float,St};
 
86
pattern({atom,_,_}=Atom, St) ->
 
87
    {Atom,St};
 
88
pattern({string,_,_}=String, St) ->
 
89
    {String,St};
 
90
pattern({nil,_}=Nil, St) ->
 
91
    {Nil,St};
 
92
pattern({cons,Line,H,T}, St0) ->
 
93
    {TH,St1} = pattern(H, St0),
 
94
    {TT,St2} = pattern(T, St1),
 
95
    {{cons,Line,TH,TT},St2};
 
96
pattern({tuple,Line,Ps}, St0) ->
 
97
    {TPs,St1} = pattern_list(Ps, St0),
 
98
    {{tuple,Line,TPs},St1};
 
99
%%pattern({struct,Line,Tag,Ps}, St0) ->
 
100
%%    {TPs,TPsvs,St1} = pattern_list(Ps, St0),
 
101
%%    {{struct,Line,Tag,TPs},TPsvs,St1};
 
102
pattern({record_field,_,_,_}=M, St) ->
 
103
    {M,St};  % must be a package name
 
104
pattern({record_index,Line,Name,Field}, St) ->
 
105
    {index_expr(Line, Field, Name, record_fields(Name, St)),St};
 
106
pattern({record,Line,Name,Pfs}, St0) ->
 
107
    Fs = record_fields(Name, St0),
 
108
    {TMs,St1} = pattern_list(pattern_fields(Fs, Pfs), St0),
 
109
    {{tuple,Line,[{atom,Line,Name} | TMs]},St1};
 
110
pattern({bin,Line,Es0}, St0) ->
 
111
    {Es1,St1} = pattern_bin(Es0, St0),
 
112
    {{bin,Line,Es1},St1};
 
113
pattern({match,Line,Pat1, Pat2}, St0) ->
 
114
    {TH,St1} = pattern(Pat2, St0),
 
115
    {TT,St2} = pattern(Pat1, St1),
 
116
    {{match,Line,TT,TH},St2};
 
117
pattern({op,Line,Op,A0}, St0) ->
 
118
    {A,St1} = pattern(A0, St0),
 
119
    {{op,Line,Op,A},St1};
 
120
pattern({op,Line,Op,L0,R0}, St0) ->
 
121
    {L,St1} = pattern(L0, St0),
 
122
    {R,St2} = pattern(R0, St1),
 
123
    {{op,Line,Op,L,R},St2}.
 
124
 
 
125
pattern_list([P0 | Ps0], St0) ->
 
126
    {P,St1} = pattern(P0, St0),
 
127
    {Ps,St2} = pattern_list(Ps0, St1),
 
128
    {[P | Ps],St2};
 
129
pattern_list([], St) -> {[],St}.
 
130
 
 
131
guard([G0 | Gs0], St0) ->
 
132
    {G,St1} = guard_tests(G0, St0),
 
133
    {Gs,St2} = guard(Gs0, St1),
 
134
    {[G | Gs],St2};
 
135
guard([], St) -> {[],St}.
 
136
 
 
137
guard_tests(Gts0, St0) ->
 
138
    {Gts1,St1} = guard_tests1(Gts0, St0),
 
139
    {Gts1,St1#exprec{checked_ra = []}}.
 
140
 
 
141
guard_tests1([Gt0 | Gts0], St0) ->
 
142
    {Gt1,St1} = guard_test(Gt0, St0),
 
143
    {Gts1,St2} = guard_tests1(Gts0, St1),
 
144
    {[Gt1 | Gts1],St2};
 
145
guard_tests1([], St) -> {[],St}.
 
146
 
 
147
guard_test(G0, St0) ->
 
148
    in_guard(fun() ->
 
149
                     {G1,St1} = guard_test1(G0, St0),
 
150
                     strict_record_access(G1, St1)
 
151
             end).
 
152
 
 
153
%% Normalising guard tests ensures that none of the Boolean operands
 
154
%% created by strict_record_access/2 calls any of the old guard tests.
 
155
guard_test1({call,Line,{atom,Lt,Tname},As}, St) ->
 
156
    Test = {atom,Lt,normalise_test(Tname, length(As))},
 
157
    expr({call,Line,Test,As}, St);
 
158
guard_test1(Test, St) ->
 
159
    expr(Test, St).
 
160
 
 
161
normalise_test(atom, 1)      -> is_atom;
 
162
normalise_test(binary, 1)    -> is_binary;
 
163
normalise_test(constant, 1)  -> is_constant;
 
164
normalise_test(float, 1)     -> is_float;
 
165
normalise_test(function, 1)  -> is_function;
 
166
normalise_test(integer, 1)   -> is_integer;
 
167
normalise_test(list, 1)      -> is_list;
 
168
normalise_test(number, 1)    -> is_number;
 
169
normalise_test(pid, 1)       -> is_pid; 
 
170
normalise_test(port, 1)      -> is_port; 
 
171
normalise_test(record, 2)    -> is_record;
 
172
normalise_test(reference, 1) -> is_reference;
 
173
normalise_test(tuple, 1)     -> is_tuple;
 
174
normalise_test(Name, _) -> Name.
 
175
 
 
176
is_in_guard() ->
 
177
    get(erl_expand_records_in_guard) =/= undefined.
 
178
 
 
179
in_guard(F) ->
 
180
    undefined = put(erl_expand_records_in_guard, true),
 
181
    Res = F(),
 
182
    true = erase(erl_expand_records_in_guard),
 
183
    Res.
 
184
 
 
185
%% record_test(Line, Term, Name, Vs, St) -> TransformedExpr
 
186
%%  Generate code for is_record/1.
 
187
 
 
188
record_test(Line, Term, Name, St) ->
 
189
    case is_in_guard() of
 
190
        false ->
 
191
            record_test_in_body(Line, Term, Name, St);
 
192
        true ->
 
193
            record_test_in_guard(Line, Term, Name, St)
 
194
    end.
 
195
 
 
196
record_test_in_guard(Line, Term, Name, St) ->
 
197
    case not_a_tuple(Term) of
 
198
        true ->
 
199
            %% In case that later optimization passes have been turned off.
 
200
            expr({atom,Line,false}, St);
 
201
        false ->
 
202
            Fs = record_fields(Name, St),
 
203
            expr({call,-Line,{remote,-Line,{atom,-Line,erlang},{atom,-Line,is_record}},
 
204
                  [Term,{atom,Line,Name},{integer,Line,length(Fs)+1}]},
 
205
                 St)
 
206
    end.
 
207
 
 
208
not_a_tuple({atom,_,_}) -> true;
 
209
not_a_tuple({integer,_,_}) -> true;
 
210
not_a_tuple({float,_,_}) -> true;
 
211
not_a_tuple({nil,_}) -> true;
 
212
not_a_tuple({cons,_,_,_}) -> true;
 
213
not_a_tuple({char,_,_}) -> true;
 
214
not_a_tuple({string,_,_}) -> true;
 
215
not_a_tuple({record_index,_,_,_}) -> true;
 
216
not_a_tuple({bin,_,_}) -> true;
 
217
not_a_tuple({op,_,_,_}) -> true;
 
218
not_a_tuple({op,_,_,_,_}) -> true;
 
219
not_a_tuple(_) -> false.
 
220
 
 
221
record_test_in_body(Line, Expr, Name, St0) ->
 
222
    %% As Expr may have side effects, we must evaluate it
 
223
    %% first and bind the value to a new variable.
 
224
    %% We must use also handle the case that Expr does not
 
225
    %% evaluate to a tuple properly.
 
226
    Fs = record_fields(Name, St0),
 
227
    {Var,St} = new_var(Line, St0),
 
228
    expr({block,Line,
 
229
          [{match,Line,Var,Expr},
 
230
           {call,-Line,{remote,-Line,{atom,-Line,erlang},
 
231
                        {atom,-Line,is_record}},
 
232
            [Var,{atom,Line,Name},{integer,Line,length(Fs)+1}]}]}, St).
 
233
 
 
234
exprs([E0 | Es0], St0) ->
 
235
    {E,St1} = expr(E0, St0),
 
236
    {Es,St2} = exprs(Es0, St1),
 
237
    {[E | Es],St2};
 
238
exprs([], St) -> {[],St}.
 
239
 
 
240
expr({var,_,_}=Var, St) ->
 
241
    {Var,St};
 
242
expr({char,_,_}=Char, St) ->
 
243
    {Char,St};
 
244
expr({integer,_,_}=Int, St) ->
 
245
    {Int,St};
 
246
expr({float,_,_}=Float, St) ->
 
247
    {Float,St};
 
248
expr({atom,_,_}=Atom, St) ->
 
249
    {Atom,St};
 
250
expr({string,_,_}=String, St) ->
 
251
    {String,St};
 
252
expr({nil,_}=Nil, St) ->
 
253
    {Nil,St};
 
254
expr({cons,Line,H0,T0}, St0) ->
 
255
    {H,St1} = expr(H0, St0),
 
256
    {T,St2} = expr(T0, St1),
 
257
    {{cons,Line,H,T},St2};
 
258
expr({lc,Line,E0,Qs0}, St0) ->
 
259
    {Qs1,St1} = lc_tq(Line, Qs0, St0),
 
260
    {E1,St2} = expr(E0, St1),
 
261
    {{lc,Line,E1,Qs1},St2};
 
262
expr({tuple,Line,Es0}, St0) ->
 
263
    {Es1,St1} = expr_list(Es0, St0),
 
264
    {{tuple,Line,Es1},St1};
 
265
%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
 
266
%%    {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
 
267
%%    {{struct,Line,Tag,Es1},Esvs,Esus,St1};
 
268
expr({record_field,_,_,_}=M, St) ->
 
269
    {M,St};  % must be a package name
 
270
expr({record_index,Line,Name,F}, St) ->
 
271
    I = index_expr(Line, F, Name, record_fields(Name, St)),
 
272
    expr(I, St);
 
273
expr({record,Line,Name,Is}, St) ->
 
274
    expr({tuple,Line,[{atom,Line,Name} | 
 
275
                      record_inits(record_fields(Name, St), Is)]},
 
276
         St);
 
277
expr({record_field,Line,R,Name,F}, St) ->
 
278
    get_record_field(Line, R, F, Name, St);
 
279
expr({record,_,R,Name,Us}, St0) ->
 
280
    {Ue,St1} = record_update(R, Name, record_fields(Name, St0), Us, St0),
 
281
    expr(Ue, St1);
 
282
expr({bin,Line,Es0}, St0) ->
 
283
    {Es1,St1} = expr_bin(Es0, St0),
 
284
    {{bin,Line,Es1},St1};
 
285
expr({block,Line,Es0}, St0) ->
 
286
    {Es,St1} = exprs(Es0, St0),
 
287
    {{block,Line,Es},St1};
 
288
expr({'if',Line,Cs0}, St0) ->
 
289
    {Cs,St1} = clauses(Cs0, St0),
 
290
    {{'if',Line,Cs},St1};
 
291
expr({'case',Line,E0,Cs0}, St0) ->
 
292
    {E,St1} = expr(E0, St0),
 
293
    {Cs,St2} = clauses(Cs0, St1),
 
294
    {{'case',Line,E,Cs},St2};
 
295
expr({'receive',Line,Cs0}, St0) ->
 
296
    {Cs,St1} = clauses(Cs0, St0),
 
297
    {{'receive',Line,Cs},St1};
 
298
expr({'receive',Line,Cs0,To0,ToEs0}, St0) ->
 
299
    {To,St1} = expr(To0, St0),
 
300
    {ToEs,St2} = exprs(ToEs0, St1),
 
301
    {Cs,St3} = clauses(Cs0, St2),
 
302
    {{'receive',Line,Cs,To,ToEs},St3};
 
303
expr({'fun',_,{function,_F,_A}}=Fun, St) ->
 
304
    {Fun,St};
 
305
expr({'fun',_,{function,_M,_F,_A}}=Fun, St) ->
 
306
    {Fun,St};
 
307
expr({'fun',Line,{clauses,Cs0}}, St0) ->
 
308
    {Cs,St1} = clauses(Cs0, St0),
 
309
    {{'fun',Line,{clauses,Cs}},St1};
 
310
expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, St) ->
 
311
    record_test(Line, A, Name, St);
 
312
expr({'cond',Line,Cs0}, St0) ->
 
313
    {Cs,St1} = clauses(Cs0, St0),
 
314
    {{'cond',Line,Cs},St1};
 
315
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
 
316
      [A,{atom,_,Name}]}, St) ->
 
317
    record_test(Line, A, Name, St);
 
318
expr({call,Line,{tuple,_,[{atom,_,erlang},{atom,_,is_record}]},
 
319
      [A,{atom,_,Name}]}, St) ->
 
320
    record_test(Line, A, Name, St);
 
321
expr({call,Line,{atom,La,N},As0}, St0) ->
 
322
    {As,St1} = expr_list(As0, St0),
 
323
    Ar = length(As),
 
324
    case erl_internal:bif(N, Ar) of
 
325
        true ->
 
326
            {{call,Line,{atom,La,N},As},St1};
 
327
        false ->
 
328
            case imported(N, Ar, St1) of
 
329
                {yes,_Mod} ->
 
330
                    {{call,Line,{atom,La,N},As},St1};
 
331
                no ->
 
332
                    case {N,Ar} of
 
333
                        {record_info,2} ->
 
334
                            record_info_call(Line, As, St1);
 
335
                        _ ->
 
336
                            {{call,Line,{atom,La,N},As},St1}
 
337
                    end
 
338
            end
 
339
    end;
 
340
expr({call,Line,{record_field,_,_,_}=M,As0}, St0) ->
 
341
    {As,St1} = expr_list(As0, St0),
 
342
    {{call,Line,M,As},St1};
 
343
expr({call,Line,{remote,Lr,M,F},As0}, St0) ->
 
344
    {[M1,F1 | As1],St1} = expr_list([M,F | As0], St0),
 
345
    {{call,Line,{remote,Lr,M1,F1},As1},St1};
 
346
expr({call,Line,{tuple,Lt,[{atom,_,_}=M,{atom,_,_}=F]},As0}, St0) ->
 
347
    {As,St1} = expr_list(As0, St0),
 
348
    {{call,Line,{tuple,Lt,[M,F]},As},St1};
 
349
expr({call,Line,F,As0}, St0) ->
 
350
    {[Fun1 | As1],St1} = expr_list([F | As0], St0),
 
351
    {{call,Line,Fun1,As1},St1};
 
352
expr({'try',Line,Es0,Scs0,Ccs0,As0}, St0) ->
 
353
    {Es1,St1} = exprs(Es0, St0),
 
354
    {Scs1,St2} = clauses(Scs0, St1),
 
355
    {Ccs1,St3} = clauses(Ccs0, St2),
 
356
    {As1,St4} = exprs(As0, St3),
 
357
    {{'try',Line,Es1,Scs1,Ccs1,As1},St4};
 
358
expr({'catch',Line,E0}, St0) ->
 
359
    {E,St1} = expr(E0, St0),
 
360
    {{'catch',Line,E},St1};
 
361
expr({match,Line,P0,E0}, St0) ->
 
362
    {E,St1} = expr(E0, St0),
 
363
    {P,St2} = pattern(P0, St1),
 
364
    {{match,Line,P,E},St2};
 
365
expr({op,Line,'not',A0}, St0) ->
 
366
    {A,St1} = bool_operand(A0, St0),
 
367
    {{op,Line,'not',A},St1};
 
368
expr({op,Line,Op,A0}, St0) ->
 
369
    {A,St1} = expr(A0, St0),
 
370
    {{op,Line,Op,A},St1};
 
371
expr({op,Line,Op,L0,R0}, St0) when Op =:= 'and'; 
 
372
                                   Op =:= 'or' ->
 
373
    {L,St1} = bool_operand(L0, St0),
 
374
    {R,St2} = bool_operand(R0, St1),
 
375
    {{op,Line,Op,L,R},St2};
 
376
expr({op,Line,Op,L0,R0}, St0) when Op =:= 'andalso';
 
377
                                   Op =:= 'orelse' ->
 
378
    {L,St1} = bool_operand(L0, St0),
 
379
    {R,St2} = bool_operand(R0, St1),
 
380
    {{op,Line,Op,L,R},St2#exprec{checked_ra = St1#exprec.checked_ra}};
 
381
expr({op,Line,Op,L0,R0}, St0) ->
 
382
    {L,St1} = expr(L0, St0),
 
383
    {R,St2} = expr(R0, St1),
 
384
    {{op,Line,Op,L,R},St2}.
 
385
 
 
386
expr_list([E0 | Es0], St0) ->
 
387
    {E,St1} = expr(E0, St0),
 
388
    {Es,St2} = expr_list(Es0, St1),
 
389
    {[E | Es],St2};
 
390
expr_list([], St) -> {[],St}.
 
391
 
 
392
bool_operand(E0, St0) ->
 
393
    {E1,St1} = expr(E0, St0),
 
394
    strict_record_access(E1, St1).
 
395
 
 
396
strict_record_access(E, #exprec{strict_ra = []} = St) ->
 
397
    {E, St};
 
398
strict_record_access(E0, St0) ->
 
399
    #exprec{strict_ra = StrictRA, checked_ra = CheckedRA} = St0,
 
400
    {New,NC} = lists:foldl(fun ({Key,_L,_R,_Sz}=A, {L,C}) ->
 
401
                                   case lists:keymember(Key, 1, C) of
 
402
                                       true -> {L,C};
 
403
                                       false -> {[A|L],[A|C]}
 
404
                                   end
 
405
                           end, {[],CheckedRA}, StrictRA),
 
406
    E1 = if New =:= [] -> E0; true -> conj(New, E0) end,
 
407
    St1 = St0#exprec{strict_ra = [], checked_ra = NC},
 
408
    expr(E1, St1).
 
409
 
 
410
%% Make it look nice (?) when compiled with the 'E' flag 
 
411
%% ('and'/2 is left recursive).
 
412
conj([], _E) ->
 
413
    empty;
 
414
conj([{{Name,_Rp},L,R,Sz} | AL], E) ->
 
415
    T1 = {op,L,'orelse',
 
416
          {call,L,{atom,L,is_record},[R,{atom,L,Name},{integer,L,Sz}]},
 
417
          {atom,L,fail}},
 
418
    T2 = case conj(AL, none) of
 
419
        empty -> T1;
 
420
        C -> {op,L,'and',C,T1}
 
421
    end,
 
422
    if E =:= none -> T2; true -> {op,L,'and',T2,E} end.
 
423
 
 
424
%% lc_tq(Line, Qualifiers, State) ->
 
425
%%      {[TransQual],State'}
 
426
 
 
427
lc_tq(Line, [{generate,Lg,P0,G0} | Qs0], St0) ->
 
428
    {G1,St1} = expr(G0, St0),
 
429
    {P1,St2} = pattern(P0, St1),
 
430
    {Qs1,St3} = lc_tq(Line, Qs0, St2),
 
431
    {[{generate,Lg,P1,G1} | Qs1],St3};
 
432
lc_tq(Line, [F0 | Qs0], St0) ->
 
433
    %% Allow record/2 and expand out as guard test.
 
434
    case erl_lint:is_guard_test(F0) of
 
435
        true ->
 
436
            {F1,St1} = guard_test(F0, St0),
 
437
            {Qs1,St2} = lc_tq(Line, Qs0, St1),
 
438
            {[F1|Qs1],St2};
 
439
        false ->
 
440
            {F1,St1} = expr(F0, St0),
 
441
            {Qs1,St2} = lc_tq(Line, Qs0, St1),
 
442
            {[F1 | Qs1],St2}
 
443
    end;
 
444
lc_tq(_Line, [], St0) ->
 
445
    {[],St0#exprec{checked_ra = []}}.
 
446
 
 
447
%% normalise_fields([RecDef]) -> [Field].
 
448
%%  Normalise the field definitions to always have a default value. If
 
449
%%  none has been given then use 'undefined'.
 
450
 
 
451
normalise_fields(Fs) ->
 
452
    map(fun ({record_field,Lf,Field}) ->
 
453
                {record_field,Lf,Field,{atom,Lf,undefined}};
 
454
            (F) -> F end, Fs).
 
455
 
 
456
%% record_fields(RecordName, State)
 
457
%% find_field(FieldName, Fields)
 
458
 
 
459
record_fields(R, St) -> dict:fetch(R, St#exprec.records).
 
460
 
 
461
find_field(F, [{record_field,_,{atom,_,F},Val} | _]) -> {ok,Val};
 
462
find_field(F, [_ | Fs]) -> find_field(F, Fs);
 
463
find_field(_, []) -> error.
 
464
 
 
465
%% field_names(RecFields) -> [Name].
 
466
%%  Return a list of the field names structures.
 
467
 
 
468
field_names(Fs) ->
 
469
    map(fun ({record_field,_,Field,_Val}) -> Field end, Fs).
 
470
 
 
471
%% index_expr(Line, FieldExpr, Name, Fields) -> IndexExpr.
 
472
%%  Return an expression which evaluates to the index of a
 
473
%%  field. Currently only handle the case where the field is an
 
474
%%  atom. This expansion must be passed through expr again.
 
475
 
 
476
index_expr(Line, {atom,_,F}, _Name, Fs) ->
 
477
    {integer,Line,index_expr(F, Fs, 2)}.
 
478
 
 
479
index_expr(F, [{record_field,_,{atom,_,F},_} | _], I) -> I;
 
480
index_expr(F, [_ | Fs], I) -> index_expr(F, Fs, I+1).
 
481
 
 
482
%% get_record_field(Line, RecExpr, FieldExpr, Name, St) -> {Expr,St'}.
 
483
%%  Return an expression which verifies that the type of record
 
484
%%  is correct and then returns the value of the field.
 
485
%%  This expansion must be passed through expr again.
 
486
 
 
487
get_record_field(Line, R, Index, Name, St) ->
 
488
    case strict_record_tests(St#exprec.compile) of
 
489
        false ->
 
490
            sloppy_get_record_field(Line, R, Index, Name, St);
 
491
        true ->
 
492
            strict_get_record_field(Line, R, Index, Name, St)
 
493
    end.
 
494
 
 
495
strict_get_record_field(Line, R, {atom,_,F}=Index, Name, St0) ->
 
496
    case is_in_guard() of
 
497
        false ->                                %Body context.
 
498
            {Var,St} = new_var(Line, St0),
 
499
            Fs = record_fields(Name, St),
 
500
            I = index_expr(F, Fs, 2),
 
501
            P = record_pattern(2, I, Var, length(Fs)+1, Line, [{atom,Line,Name}]),
 
502
            E = {'case',Line,R,
 
503
                     [{clause,Line,[{tuple,Line,P}],[],[Var]},
 
504
                      {clause,Line,[{var,Line,'_'}],[],
 
505
                       [{call,Line,{remote,Line,
 
506
                                    {atom,Line,erlang},
 
507
                                    {atom,Line,error}},
 
508
                         [{tuple,Line,[{atom,Line,badrecord},{atom,Line,Name}]}]}]}]},
 
509
            expr(E, St);
 
510
        true ->                                 %In a guard.
 
511
            Fs = record_fields(Name, St0),
 
512
            I = index_expr(Line, Index, Name, Fs),
 
513
            {ExpR,St1}  = expr(R, St0),
 
514
            %% Just to make comparison simple:
 
515
            ExpRp = erl_lint:modify_line(ExpR, fun(_L) -> 0 end),
 
516
            RA = {{Name,ExpRp},Line,ExpR,length(Fs)+1},
 
517
            St2 = St1#exprec{strict_ra = [RA | St1#exprec.strict_ra]},
 
518
            {{call,Line,{atom,Line,element},[I,ExpR]},St2}
 
519
    end.
 
520
 
 
521
record_pattern(I, I, Var, Sz, Line, Acc) ->
 
522
    record_pattern(I+1, I, Var, Sz, Line, [Var | Acc]);
 
523
record_pattern(Cur, I, Var, Sz, Line, Acc) when Cur =< Sz ->
 
524
    record_pattern(Cur+1, I, Var, Sz, Line, [{var,Line,'_'} | Acc]);
 
525
record_pattern(_, _, _, _, _, Acc) -> reverse(Acc).
 
526
 
 
527
sloppy_get_record_field(Line, R, Index, Name, St) ->
 
528
    Fs = record_fields(Name, St),
 
529
    I = index_expr(Line, Index, Name, Fs),
 
530
    expr({call,Line,{atom,Line,element},[I,R]}, St).
 
531
 
 
532
strict_record_tests([strict_record_tests | _]) -> true;
 
533
strict_record_tests([no_strict_record_tests | _]) -> false;
 
534
strict_record_tests([_ | Os]) -> strict_record_tests(Os);
 
535
strict_record_tests([]) -> true.                %Default.
 
536
 
 
537
%% pattern_fields([RecDefField], [Match]) -> [Pattern].
 
538
%%  Build a list of match patterns for the record tuple elements.
 
539
%%  This expansion must be passed through pattern again. N.B. We are
 
540
%%  scanning the record definition field list!
 
541
 
 
542
pattern_fields(Fs, Ms) ->
 
543
    Wildcard = record_wildcard_init(Ms),
 
544
    map(fun ({record_field,L,{atom,_,F},_}) ->
 
545
                case find_field(F, Ms) of
 
546
                    {ok,Match} -> Match;
 
547
                    error when Wildcard =:= none -> {var,L,'_'};
 
548
                    error -> Wildcard
 
549
                end end,
 
550
        Fs).
 
551
 
 
552
%% record_inits([RecDefField], [Init]) -> [InitExpr].
 
553
%%  Build a list of initialisation expressions for the record tuple
 
554
%%  elements. This expansion must be passed through expr
 
555
%%  again. N.B. We are scanning the record definition field list!
 
556
 
 
557
record_inits(Fs, Is) ->
 
558
    WildcardInit = record_wildcard_init(Is),
 
559
    map(fun ({record_field,_,{atom,_,F},D}) ->
 
560
                case find_field(F, Is) of
 
561
                    {ok,Init} -> Init;
 
562
                    error when WildcardInit =:= none -> D;
 
563
                    error -> WildcardInit
 
564
                end end,
 
565
        Fs).
 
566
 
 
567
record_wildcard_init([{record_field,_,{var,_,'_'},D} | _]) -> D;
 
568
record_wildcard_init([_ | Is]) -> record_wildcard_init(Is);
 
569
record_wildcard_init([]) -> none.
 
570
 
 
571
%% record_update(Record, RecordName, [RecDefField], [Update], State) ->
 
572
%%      {Expr,State'}
 
573
%%  Build an expression to update fields in a record returning a new
 
574
%%  record.  Try to be smart and optimise this. This expansion must be
 
575
%%  passed through expr again.
 
576
 
 
577
record_update(R, Name, Fs, Us0, St0) ->
 
578
    Line = element(2, R),
 
579
    {Pre,Us,St1} = record_exprs(Us0, St0),
 
580
    Nf = length(Fs),                            %# of record fields
 
581
    Nu = length(Us),                            %# of update fields
 
582
    Nc = Nf - Nu,                               %# of copy fields
 
583
 
 
584
    %% We need a new variable for the record expression
 
585
    %% to guarantee that it is only evaluated once.
 
586
    {Var,St2} = new_var(Line, St1),
 
587
 
 
588
    %% Try to be intelligent about which method of updating record to use.
 
589
    {Update,St} =
 
590
        if
 
591
            Nu == 0 -> {R,St2};                 %No fields updated
 
592
            Nu =< Nc ->                         %Few fields updated
 
593
                {record_setel(Var, Name, Fs, Us), St2};
 
594
            true ->                           %The wide area inbetween
 
595
                record_match(Var, Name, Fs, Us, St2)
 
596
        end,
 
597
    {{block,element(2, R),Pre ++ [{match,Line,Var,R},Update]},St}.
 
598
 
 
599
%% record_match(Record, RecordName, [RecDefField], [Update], State)
 
600
%%  Build a 'case' expression to modify record fields.
 
601
 
 
602
record_match(R, Name, Fs, Us, St0) ->
 
603
    {Ps,News,St1} = record_upd_fs(Fs, Us, St0),
 
604
    Lr = element(2, hd(Us)),
 
605
    {{'case',Lr,R,
 
606
      [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name} | Ps]}],[],
 
607
        [{tuple,Lr,[{atom,Lr,Name} | News]}]},
 
608
       {clause,Lr,[{var,Lr,'_'}],[],
 
609
        [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}
 
610
      ]},
 
611
     St1}.
 
612
 
 
613
record_upd_fs([{record_field,Lf,{atom,_La,F},_Val} | Fs], Us, St0) ->
 
614
    {P,St1} = new_var(Lf, St0),
 
615
    {Ps,News,St2} = record_upd_fs(Fs, Us, St1),
 
616
    case find_field(F, Us) of
 
617
        {ok,New} -> {[P | Ps],[New | News],St2};
 
618
        error -> {[P | Ps],[P | News],St2}
 
619
    end;
 
620
record_upd_fs([], _, St) -> {[],[],St}.
 
621
 
 
622
%% record_setel(Record, RecordName, [RecDefField], [Update])
 
623
%%  Build a nested chain of setelement calls to build the 
 
624
%%  updated record tuple.
 
625
 
 
626
record_setel(R, Name, Fs, Us0) ->
 
627
    Us1 = foldl(fun ({record_field,Lf,Field,Val}, Acc) ->
 
628
                        I = index_expr(Lf, Field, Name, Fs),
 
629
                        [{I,Lf,Val} | Acc]
 
630
                end, [], Us0),
 
631
    Us = sort(Us1),
 
632
    Lr = element(2, hd(Us)),
 
633
    Wildcards = duplicate(length(Fs), {var,Lr,'_'}),
 
634
    {'case',Lr,R,
 
635
     [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name} | Wildcards]}],[],
 
636
       [foldr(fun ({I,Lf,Val}, Acc) ->
 
637
                      {call,Lf,{atom,Lf,setelement},[I,Acc,Val]} end,
 
638
              R, Us)]},
 
639
      {clause,Lr,[{var,Lr,'_'}],[],
 
640
       [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}]}.
 
641
 
 
642
%% Expand a call to record_info/2. We have checked that it is not
 
643
%% shadowed by an import.
 
644
 
 
645
record_info_call(Line, [{atom,_Li,Info},{atom,_Ln,Name}], St) ->
 
646
    case Info of
 
647
        size ->
 
648
            {{integer,Line,1+length(record_fields(Name, St))},St};
 
649
        fields ->
 
650
            {make_list(field_names(record_fields(Name, St)), Line),St}
 
651
    end.
 
652
 
 
653
%% Break out expressions from an record update list and bind to new
 
654
%% variables. The idea is that we will evaluate all update expressions
 
655
%% before starting to update the record.
 
656
 
 
657
record_exprs(Us, St) ->
 
658
    record_exprs(Us, St, [], []).
 
659
 
 
660
record_exprs([{record_field,Lf,{atom,_La,_F}=Name,Val}=Field0 | Us], St0, Pre, Fs) ->
 
661
    case is_simple_val(Val) of
 
662
        true ->
 
663
            record_exprs(Us, St0, Pre, [Field0 | Fs]);
 
664
        false ->
 
665
            {Var,St} = new_var(Lf, St0),
 
666
            Bind = {match,Lf,Var,Val},
 
667
            Field = {record_field,Lf,Name,Var},
 
668
            record_exprs(Us, St, [Bind | Pre], [Field | Fs])
 
669
    end;
 
670
record_exprs([], St, Pre, Fs) ->
 
671
    {reverse(Pre),Fs,St}.
 
672
 
 
673
is_simple_val({var,_,_}) -> true;
 
674
is_simple_val(Val) ->
 
675
    try
 
676
        erl_parse:normalise(Val),
 
677
        true
 
678
    catch error:_ ->
 
679
        false
 
680
    end.
 
681
 
 
682
%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}.
 
683
 
 
684
pattern_bin(Es0, St) ->
 
685
    foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],St}, Es0).
 
686
 
 
687
pattern_element({bin_element,Line,Expr0,Size,Type}, {Es,St0}) ->
 
688
    {Expr,St1} = pattern(Expr0, St0),
 
689
    {[{bin_element,Line,Expr,Size,Type} | Es],St1}.
 
690
 
 
691
%% expr_bin([Element], State) -> {[Element],State}.
 
692
 
 
693
expr_bin(Es0, St) ->
 
694
    foldr(fun (E, Acc) -> bin_element(E, Acc) end, {[],St}, Es0).
 
695
 
 
696
bin_element({bin_element,Line,Expr,Size,Type}, {Es,St0}) ->
 
697
    {Expr1,St1} = expr(Expr, St0),
 
698
    {Size1,St2} = if Size == default -> {default,St1};
 
699
                             true -> expr(Size, St1)
 
700
                          end,
 
701
    {[{bin_element,Line,Expr1,Size1,Type} | Es],St2}.
 
702
 
 
703
new_var(L, St0) ->
 
704
    {New,St1} = new_var_name(St0),
 
705
    {{var,L,New},St1}.
 
706
 
 
707
new_var_name(St) ->
 
708
    C = St#exprec.vcount,
 
709
    {list_to_atom("rec" ++ integer_to_list(C)),St#exprec{vcount=C+1}}.
 
710
 
 
711
make_list(Ts, Line) ->
 
712
    foldr(fun (H, T) -> {cons,Line,H,T} end, {nil,Line}, Ts).
 
713
 
 
714
call_error(L, R) ->
 
715
    {call,L,{remote,L,{atom,L,erlang},{atom,L,error}},[R]}.
 
716
 
 
717
import({Mod,Fs}, St) ->
 
718
    St#exprec{imports=add_imports(Mod, Fs, St#exprec.imports)};
 
719
import(_Mod0, St) ->
 
720
    St.
 
721
 
 
722
add_imports(Mod, [F | Fs], Is) ->
 
723
    add_imports(Mod, Fs, orddict:store(F, Mod, Is));
 
724
add_imports(_, [], Is) -> Is.
 
725
 
 
726
imported(F, A, St) ->
 
727
    case orddict:find({F,A}, St#exprec.imports) of
 
728
        {ok,Mod} -> {yes,Mod};
 
729
        error -> no
 
730
    end.