Skip to content

Commit e267065

Browse files
committed
Updated rec2json to use maps.
This means mutators also need to be changed. Also removed depricated features "scan_string" and "scan_file", as the cannonial way to use rec2json is now the parse_transform. This made many tests unable to run, and so needed to refactored.
1 parent ab35f49 commit e267065

10 files changed

+433
-637
lines changed

src/r2j_compile.erl

+50-178
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
-module(r2j_compile).
1616

17-
-export([scan_file/2, scan_string/2]).
1817
-export([simplify_fields/1]).
1918
-export([type_declaration/2, export_type_declaration/2, export_declaration/2, export_declaration/3, additional_funcs/3]).
2019

@@ -34,91 +33,6 @@
3433
-define(log(Msg), ?log(Msg, [])).
3534
-endif.
3635

37-
scan_file(Hrl, Opts) ->
38-
Imports = proplists:get_value(imports_dir, Opts, []),
39-
{ok, Handle} = epp:open(Hrl, Imports, []),
40-
{ok, Forms} = read_epp_forms(Handle),
41-
Modules = analyze_forms(Forms, Opts),
42-
output(Modules, proplists:get_value(output_dir, Opts, ".")).
43-
44-
read_epp_forms(Handle) ->
45-
read_epp_forms(Handle, []).
46-
47-
read_epp_forms(Handle, Acc) ->
48-
case epp:parse_erl_form(Handle) of
49-
{ok, Form} ->
50-
read_epp_forms(Handle, [Form | Acc]);
51-
{eof, _L} ->
52-
{ok, lists:reverse(Acc)}
53-
end.
54-
55-
scan_string(Str, Opts) ->
56-
%Imports = proplists:get_value(imports_dir, Opts, []),
57-
{ok, Tokens, _Lines} = erl_scan:string(Str),
58-
LineTokens = split_tokens_by_dots(Tokens),
59-
Forms = parse_forms(LineTokens),
60-
Modules = analyze_forms(Forms, Opts),
61-
output(Modules, proplists:get_value(output_dir, Opts, ".")).
62-
63-
split_tokens_by_dots(Tokens) ->
64-
split_tokens_by_dots(Tokens, []).
65-
66-
split_tokens_by_dots([], Acc) ->
67-
lists:reverse(Acc);
68-
69-
split_tokens_by_dots(Tokens, Acc) ->
70-
{SansDot, [Dot | Rest]} = lists:splitwith(fun is_not_dot/1, Tokens),
71-
HasDot = SansDot ++ [Dot],
72-
split_tokens_by_dots(Rest, [HasDot | Acc]).
73-
74-
is_not_dot({dot, _}) -> false;
75-
is_not_dot(_) -> true.
76-
77-
parse_forms(TokenList) ->
78-
parse_forms(TokenList, []).
79-
80-
parse_forms([], Acc) ->
81-
lists:reverse(Acc);
82-
83-
parse_forms([Tokens | Tail], Acc) ->
84-
{ok, Form} = erl_parse:parse_form(Tokens),
85-
parse_forms(Tail, [Form | Acc]).
86-
87-
output(Modules, OutputDir) ->
88-
lists:foreach(fun(Module) ->
89-
try_write_module(Module, OutputDir)
90-
end, Modules).
91-
92-
try_write_module(Module, OutputDir) ->
93-
case compile:forms(Module, [return]) of
94-
{ok, ModName, Bin, _Warings} ->
95-
File = filename:join(OutputDir, atom_to_list(ModName) ++ ".beam"),
96-
ok = file:write_file(File, Bin);
97-
Else ->
98-
error({compile_failed, Else, Module})
99-
end.
100-
101-
analyze_forms(Forms, Params) ->
102-
analyze_forms(Forms, [], Params).
103-
104-
analyze_forms([], Acc, _Params) ->
105-
lists:reverse(Acc);
106-
107-
analyze_forms([Form | Forms], Acc, Params) ->
108-
case erl_syntax:type(Form) of
109-
attribute ->
110-
case erl_syntax_lib:analyze_attribute(Form) of
111-
{record, {RecordName, _RecordFields}} ->
112-
SimpleFields = simplify_fields(Form),
113-
Mod = create_module(RecordName, SimpleFields, Form, Params),
114-
analyze_forms(Forms, [Mod | Acc], Params);
115-
_ ->
116-
analyze_forms(Forms, Acc, Params)
117-
end;
118-
_ ->
119-
analyze_forms(Forms, Acc, Params)
120-
end.
121-
12236
simplify_fields({attribute, _Line, record, {_RecName, Fields}}) ->
12337
simplify_fields(Fields, []).
12438

@@ -128,7 +42,7 @@ simplify_fields([], Acc) ->
12842
simplify_fields([{record_field, Line, NameForm} | Tail], Acc) ->
12943
Name = erl_parse:normalise(NameForm),
13044
FieldRec = #record_field{name = Name, name_form = NameForm,
131-
default_form = undefined_default(Line) },
45+
default_form = undefined_default(Line), typelist = [undefined] },
13246
simplify_fields(Tail, [FieldRec | Acc]);
13347

13448
simplify_fields([{record_field, _Line, NameForm, Default} | Tail], Acc) ->
@@ -145,7 +59,7 @@ simplify_fields([{typed_record_field, {record_field, Line, NameForm}, Type} | Ta
14559
end,
14660
FieldRec = #record_field{name = Name, name_form = NameForm,
14761
default_form = undefined_default(Line), allow_any = AllowAny,
148-
typelist = Types},
62+
typelist = [undefined | Types]},
14963
simplify_fields(Tail, [FieldRec | Acc]);
15064

15165
simplify_fields([{typed_record_field, {record_field, _L1, NameForm, Default}, Type} | Tail], Acc) ->
@@ -197,7 +111,7 @@ extract_types([{type, _L1, list, ListTypes} | Tail], Acc) ->
197111
% usual {type, L, record, RecordName}.
198112
extract_types([{type, _L1, record, [{atom, _L2, RecordName}]} | Tail], Acc) ->
199113
% no way to know if it's a rec2json compiled record, so we'll just go with
200-
% it. If it's not (no to/from json), there's a catch for undef in the
114+
% it. If it's not (no to/from json), there's a catch for undef in the
201115
% rec2json module.
202116
extract_types(Tail, [{record, RecordName} | Acc]);
203117
extract_types([{type, _L1, Type, TypeArgs} | Tail], Acc) ->
@@ -245,14 +159,6 @@ supported_type(record, _) ->
245159
supported_type(_,_) ->
246160
false.
247161

248-
create_module(RecordName, Fields, RecordDecl, Params) ->
249-
{ok, ModuleDeclaration} = module_declaration(RecordName),
250-
{ok, Type} = type_declaration(RecordName, Params),
251-
{ok, ExportType} = export_type_declaration(RecordName, Params),
252-
{ok, ExportDeclaration} = export_declaration(RecordName, Fields, Params),
253-
{ok, NewFunctions} = additional_funcs(RecordName, Fields, Params),
254-
[ModuleDeclaration, RecordDecl, ExportDeclaration] ++ Type ++ ExportType ++ NewFunctions.
255-
256162
type_declaration(RecordName, Params) ->
257163
case proplists:get_value(generate_type, Params, true) of
258164
false ->
@@ -298,7 +204,6 @@ additional_funcs(RecordName, Fields, Params) ->
298204
{ok, FromJsonA1} = from_json_arity1_func(RecordName, Fields),
299205
{ok, FromJsonA2} = from_json_arity2_func(RecordName, Fields),
300206
{ok, FromJsonA3} = from_json_arity3_func(),
301-
ScrubKeys = scrub_keys_func(Fields),
302207
GrandFuncList = []
303208
++ AccessorFuncs
304209
++ SetterFuncs
@@ -310,12 +215,8 @@ additional_funcs(RecordName, Fields, Params) ->
310215
++ [FromJsonA1]
311216
++ [FromJsonA2]
312217
++ [FromJsonA3]
313-
++ ScrubKeys
314218
, {ok, GrandFuncList}.
315219

316-
module_declaration(Name) ->
317-
{ok, {attribute, 1, module, Name}}.
318-
319220
export_declaration(RecordName, Fields) ->
320221
export_declaration(RecordName, Fields, []).
321222

@@ -476,113 +377,84 @@ blank_record(RecName, Fields) ->
476377
ElementList = [{atom, ?LINE, RecName} | ValueListSansName],
477378
{tuple, ?LINE, ElementList}.
478379

479-
scrub_keys_func(Fields) ->
480-
TopScrub = {function, ?LINE, scrub_keys, 1, [
481-
{clause, ?LINE, [{var, ?LINE, 'Json'}], [], [
482-
{call, ?LINE, {atom, ?LINE, scrub_keys}, [{var, ?LINE, 'Json'}, {nil, ?LINE}]}
483-
]}
484-
]},
485-
EndScrubClause = scrub_key_end(),
486-
CatchBinClauses = lists:map(fun scrub_key_clause/1, Fields),
487-
CatchAtomClause = scrub_key_catch_atom(),
488-
CatchAllClause = scrub_key_catch_all(),
489-
AllClauses = [EndScrubClause] ++ CatchBinClauses ++ [CatchAtomClause, CatchAllClause],
490-
491-
ScrubKeys = {function, ?LINE, scrub_keys, 2, AllClauses},
492-
493-
[TopScrub, ScrubKeys].
494-
495-
scrub_key_catch_atom() ->
496-
KeyValueMatch = {match, ?LINE, {tuple, ?LINE, [{var, ?LINE, 'Key'}, {var, ?LINE, '_'}]}, {var, ?LINE, 'Head'}},
497-
Arg1 = {cons, ?LINE, KeyValueMatch, {var, ?LINE, 'Tail'}},
498-
Arg2 = {var, ?LINE, 'Acc'},
499-
ArgsList = [Arg1, Arg2],
500-
GuardList = [[{call, ?LINE, {atom, ?LINE, is_atom}, [{var, ?LINE, 'Key'}]}]],
501-
CallList = [
502-
{call, ?LINE, {atom, ?LINE, scrub_keys}, [
503-
{var, ?LINE, 'Tail'}, {cons, ?LINE, {var, ?LINE, 'Head'}, {var, ?LINE, 'Acc'}}
504-
]}
505-
],
506-
{clause, ?LINE, ArgsList, GuardList, CallList}.
507-
508-
scrub_key_catch_all() ->
509-
{clause, ?LINE, [{cons, ?LINE, {var, ?LINE, '_'}, {var, ?LINE, 'Tail'}}, {var, ?LINE, 'Acc'}], [], [
510-
{call, ?LINE, {atom, ?LINE, scrub_keys}, [{var, ?LINE, 'Tail'}, {var, ?LINE, 'Acc'}]}
511-
]}.
512-
513-
scrub_key_end() ->
514-
{clause, ?LINE, [{nil, ?LINE}, {var, ?LINE, 'Acc'}], [], [
515-
{call, ?LINE, {remote, ?LINE, {atom, ?LINE, lists}, {atom, ?LINE, reverse}}, [{var, ?LINE, 'Acc'}]}
516-
]}.
517-
518-
scrub_key_clause(FieldRec) ->
519-
NameAsBin = {bin, ?LINE, [
520-
{bin_element, ?LINE, {string, ?LINE, atom_to_list(FieldRec#record_field.name) }, default, default}
521-
]},
522-
ArgOneHead = {tuple, ?LINE, [NameAsBin, {var, ?LINE, 'Value'}]},
523-
ArgOneTail = {var, ?LINE, 'Tail'},
524-
ArgOne = {cons, ?LINE, ArgOneHead, ArgOneTail},
525-
ArgsList = [ArgOne, {var, ?LINE, 'Acc'}],
526-
527-
AccOnTop = {cons, ?LINE, {tuple, ?LINE, [{atom, ?LINE, FieldRec#record_field.name}, {var, ?LINE, 'Value'}]}, {var, ?LINE, 'Acc'}},
528-
529-
RecursiveCall = {call, ?LINE, {atom, ?LINE, scrub_keys}, [{var, ?LINE, 'Tail'}, AccOnTop]},
530-
{clause, ?LINE, ArgsList, [], [RecursiveCall]}.
531-
532380
from_json_arity1_func(RecName, Fields) ->
533381
BlankTuple = blank_record(RecName, Fields),
534382
{ok, {function, ?LINE, from_json, 1, [
535383
{clause, ?LINE, [{var, ?LINE, 'Json'}], [], [
536-
{match, ?LINE, {var, ?LINE, 'Json2'}, {call, ?LINE, {atom, ?LINE, scrub_keys}, [{var, ?LINE, 'Json'}]}},
537-
{call, ?LINE, {remote, ?LINE, {atom, ?LINE, rec2json}, {atom, ?LINE, from_json}}, [BlankTuple, {var, ?LINE, 'Json2'}, {nil, ?LINE}]}
384+
{call, ?LINE, {remote, ?LINE, {atom, ?LINE, rec2json}, {atom, ?LINE, from_json}}, [BlankTuple, {var, ?LINE, 'Json'}, {nil, ?LINE}]}
538385
]}
539386
]}}.
540387

541388
from_json_arity2_func(RecName, Fields) ->
542389
BlankRec = blank_record(RecName, Fields),
543390
{ok,{function, ?LINE,from_json,2, [
544-
from_json_arity2_clause1(BlankRec),
545-
from_json_arity2_clause2(),
546-
from_json_arity2_clause3()
391+
from_json_arity2_no_seed_json_first(BlankRec),
392+
from_json_arity2_no_seed_json_second(BlankRec),
393+
from_json_arity2_seed_first(),
394+
from_json_arity2_seed_second()
547395
]}}.
548-
549-
from_json_arity2_clause1(BlankRec) ->
396+
397+
from_json_arity2_no_seed_json_first(BlankRec) ->
550398
Args = [{var, ?LINE, 'Json'}, {var, ?LINE, 'Opts'}],
551399
Guards = [[
552-
{call, ?LINE, {atom, ?LINE, is_list}, [{var, ?LINE, 'Json'}]},
400+
{call, ?LINE, {atom, ?LINE, is_map}, [{var, ?LINE, 'Json'}]},
553401
{call, ?LINE, {atom, ?LINE, is_list}, [{var, ?LINE, 'Opts'}]}
554402
]],
555403
Expressions = [
556404
{call, ?LINE, {atom, ?LINE, from_json}, [BlankRec, {var, ?LINE, 'Json'}, {var, ?LINE, 'Opts'}]}
557405
],
558406
{clause, ?LINE, Args, Guards, Expressions}.
559407

560-
from_json_arity2_clause2() ->
408+
from_json_arity2_no_seed_json_second(BlankRec) ->
409+
Args = [{var, ?LINE, 'Opts'}, {var, ?LINE, 'Json'}],
410+
Guards = [[
411+
{call, ?LINE, {atom, ?LINE, is_map}, [{var, ?LINE, 'Json'}]},
412+
{call, ?LINE, {atom, ?LINE, is_list}, [{var, ?LINE, 'Opts'}]}
413+
]],
414+
Expressions = [
415+
{call, ?LINE, {atom, ?LINE, from_json}, [BlankRec, {var, ?LINE, 'Json'}, {var, ?LINE, 'Opts'}]}
416+
],
417+
{clause, ?LINE, Args, Guards, Expressions}.
418+
419+
from_json_arity2_seed_first() ->
561420
Args = [{var, ?LINE, 'Struct'}, {var, ?LINE, 'Json'}],
562421
Guards = [[
563-
{call, ?LINE, {atom, ?LINE, is_tuple}, [{var, ?LINE, 'Struct'}]}
422+
{call, ?LINE, {atom, ?LINE, is_tuple}, [{var, ?LINE, 'Struct'}]},
423+
{call, ?LINE, {atom, ?LINE, is_map}, [{var, ?LINE, 'Json'}]}
564424
]],
565425
Expressions = [
566426
{call, ?LINE, {atom, ?LINE, from_json}, [{var, ?LINE, 'Struct'}, {var, ?LINE, 'Json'}, {nil, ?LINE}]}
567427
],
568428
{clause, ?LINE, Args, Guards, Expressions}.
569-
570-
from_json_arity2_clause3() ->
429+
430+
from_json_arity2_seed_second() ->
571431
Args = [{var, ?LINE, 'Json'}, {var, ?LINE, 'Struct'}],
572-
Guards = [],
432+
Guards = [[
433+
{call, ?LINE, {atom, ?LINE, is_tuple}, [{var, ?LINE, 'Struct'}]},
434+
{call, ?LINE, {atom, ?LINE, is_map}, [{var, ?LINE, 'Json'}]}
435+
]],
573436
Expressions = [
574437
{call, ?LINE, {atom, ?LINE, from_json}, [{var, ?LINE, 'Struct'}, {var, ?LINE, 'Json'}, {nil, ?LINE}]}
575438
],
576439
{clause, ?LINE, Args, Guards, Expressions}.
577440

578441
from_json_arity3_func() ->
579-
{ok,{function, ?LINE, from_json,3, [
580-
{clause, ?LINE, [{var, ?LINE, 'Struct'},{var, ?LINE, 'Json'},{var, ?LINE, 'Opts'}], [[{call, ?LINE, {atom, ?LINE, is_list},[{var, ?LINE, 'Opts'}]}]], [
581-
{call, ?LINE, {atom, ?LINE, from_json}, [{var, ?LINE, 'Json'}, {var, ?LINE, 'Opts'}, {var, ?LINE, 'Struct'}]}
582-
]},
583-
{clause, ?LINE, [{var, ?LINE, 'Json'},{var, ?LINE, 'Opts'},{var, ?LINE, 'Struct'}], [], [
584-
{match, ?LINE, {var, ?LINE, 'Json2'}, {call, ?LINE, {atom, ?LINE, scrub_keys},[{var, ?LINE, 'Json'}]}},
585-
{call, ?LINE, {remote, ?LINE, {atom, ?LINE, rec2json},{atom, ?LINE, from_json}}, [{var, ?LINE, 'Struct'}, {var, ?LINE, 'Json2'}, {var, ?LINE, 'Opts'}]}
586-
]}
587-
]}}.
588-
442+
Gaurds = [[
443+
{call, ?LINE, {atom, ?LINE, is_tuple}, [{var, ?LINE, 'Struct'}]},
444+
{call, ?LINE, {atom, ?LINE, is_map}, [{var, ?LINE, 'Json'}]},
445+
{call, ?LINE, {atom, ?LINE, is_list}, [{var, ?LINE, 'Opts'}]}
446+
]],
447+
RemoteCall = {call, ?LINE, {remote, ?LINE, {atom, ?LINE, rec2json},{atom,?LINE,from_json}}, [{var, ?LINE, 'Struct'}, {var, ?LINE, 'Json'}, {var, ?LINE, 'Opts'}]},
448+
ArgOrders = [
449+
['Struct', 'Json', 'Opts'],
450+
['Struct', 'Opts', 'Json'],
451+
['Json', 'Struct', 'Opts'],
452+
['Json', 'Opts', 'Struct'],
453+
['Opts', 'Json', 'Struct'],
454+
['Opts', 'Struct', 'Json']
455+
],
456+
Clauses = lists:map(fun(Args) ->
457+
Vars = [{var, ?LINE, A} || A <- Args],
458+
{clause, ?LINE, Vars, Gaurds, [RemoteCall]}
459+
end, ArgOrders),
460+
{ok, {function, ?LINE, from_json, 3, Clauses}}.

0 commit comments

Comments
 (0)