Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added support for import directives.

An import sucks in the imported file (or tries to anyway).  the result
is as though the imported file was copy/pasted into the orignal parsed
file.
  • Loading branch information...
commit c21a77b88bf06e612e4a3bb0a4218cc204810b66 1 parent f489be8
Micah lordnull authored
42 src/protobuffs_compile.erl
@@ -35,7 +35,8 @@ scan_file(ProtoFile) ->
35 35
36 36 %% @doc Generats a built .beam file and header file .hrl
37 37 %% Considerd option properties: output_include_dir,
38   -%% output_ebin_dir
  38 +%% output_ebin_dir,
  39 +%% imports_dir
39 40 %% @spec scan_file(ProtoFile,Options) -> Result
40 41 %% ProtoFile = string()
41 42 %% Options = proplists()
@@ -43,7 +44,9 @@ scan_file(ProtoFile) ->
43 44 %% Reason = ext_posix() | terminated | system_limit
44 45 scan_file(ProtoFile,Options) when is_list(ProtoFile) ->
45 46 Basename = filename:basename(ProtoFile, ".proto") ++ "_pb",
46   - {ok,Parsed} = parse(ProtoFile),
  47 + {ok,FirstParsed} = parse(ProtoFile),
  48 + ImportPaths = ["./" | proplists:get_value(imports_dir, Options, [])],
  49 + Parsed = parse_imports(FirstParsed, ImportPaths),
47 50 {{msg,UntypedMessages},{enum,Enums}} = collect_full_messages(Parsed),
48 51 Messages = resolve_types(UntypedMessages,Enums),
49 52 output(Basename, Messages, Enums, Options).
@@ -58,7 +61,8 @@ generate_source(ProtoFile) ->
58 61
59 62 %% @doc Generats a source .erl file and header file .hrl
60 63 %% Consider option properties: output_include_dir,
61   -%% output_src_dir
  64 +%% output_src_dir,
  65 +%% imports_dir
62 66 %% @spec generate_source(ProtoFile,Options) -> Result
63 67 %% ProtoFile = string()
64 68 %% Options = proplists()
@@ -66,12 +70,40 @@ generate_source(ProtoFile) ->
66 70 %% Reason = ext_posix() | terminated | system_limit
67 71 generate_source(ProtoFile,Options) when is_list (ProtoFile) ->
68 72 Basename = filename:basename(ProtoFile, ".proto") ++ "_pb",
69   - {ok,Parsed} = parse(ProtoFile),
  73 + {ok,FirstParsed} = parse(ProtoFile),
  74 + ImportPaths = ["./" | proplists:get_value(imports_dir, Options, [])],
  75 + Parsed = parse_imports(FirstParsed, ImportPaths),
70 76 {{msg,UntypedMessages},{enum,Enums}} = collect_full_messages(Parsed),
71 77 Messages = resolve_types(UntypedMessages,Enums),
72 78 output_source (Basename, Messages, Enums, Options).
73 79
74 80 %% @hidden
  81 +parse_imports(Parsed, Path) ->
  82 + parse_imports(Parsed, Path, []).
  83 +
  84 +%% @hidden
  85 +parse_imports([], _Path, Acc) ->
  86 + lists:reverse(Acc);
  87 +parse_imports([{import, File} = Head | Tail], Path, Acc) ->
  88 + case file:path_open(Path, File, [read]) of
  89 + {ok, F, Fullname} ->
  90 + file:close(F),
  91 + {ok,FirstParsed} = parse(Fullname),
  92 + Parsed = lists:append(FirstParsed, Tail),
  93 + parse_imports(Parsed, Path, [Head | Acc]);
  94 + {error, Error} ->
  95 + error_logger:warning_report([
  96 + "Could not do import",
  97 + {import, File},
  98 + {error, Error},
  99 + {path, Path}
  100 + ]),
  101 + parse_imports(Tail, Path, [Head | Acc])
  102 + end;
  103 +parse_imports([Head | Tail], Path, Acc) ->
  104 + parse_imports(Tail, Path, [Head | Acc]).
  105 +
  106 +%% @hidden
75 107 output(Basename, Messages, Enums, Options) ->
76 108 case proplists:get_value(output_include_dir,Options) of
77 109 undefined ->
@@ -333,6 +365,8 @@ collect_full_messages([{package, _PackageName} | Tail], AccEnum, AccMsg) ->
333 365 collect_full_messages(Tail, AccEnum, AccMsg);
334 366 collect_full_messages([{option,_,_} | Tail], AccEnum, AccMsg) ->
335 367 collect_full_messages(Tail, AccEnum, AccMsg);
  368 +collect_full_messages([{import, _Filename} | Tail], AccEnum, AccMsg) ->
  369 + collect_full_messages(Tail, AccEnum, AccMsg);
336 370 collect_full_messages([], AccEnum, AccMsg) ->
337 371 {{msg,AccMsg},{enum,AccEnum}}.
338 372
7 src/protobuffs_parser.yrl
... ... @@ -1,17 +1,19 @@
1 1 Nonterminals
2 2 g_protobuffs g_members g_members_noopts g_member g_options g_option
3 3 g_messages g_message g_service g_rpcs g_rpc g_enums g_enum g_elements
4   -g_element g_default g_pack g_var.
  4 +g_element g_default g_pack g_var g_import.
5 5
6 6 Terminals ';' '=' '{' '}' '[' ']' '(' ')'
7   -package option message enum var integer float string type requirement default pack to extensions max service rpc returns.
  7 +package option message enum var integer float string type requirement default pack to extensions max service rpc returns import.
8 8
9 9 Rootsymbol g_protobuffs.
10 10 Endsymbol '$end'.
11 11
  12 +g_protobuffs -> g_import g_protobuffs : ['$1' | '$2'].
12 13 g_protobuffs -> package g_var ';' g_members : [{package, safe_string('$2')}] ++ '$4'.
13 14 g_protobuffs -> g_members : '$1'.
14 15
  16 +g_import -> import g_var ';' : {import, '$2'}.
15 17 %g_members -> g_options g_messages : '$1' ++ '$2'.
16 18 %g_members -> g_messages : '$1'.
17 19 g_members -> g_options g_members_noopts : '$1' ++ '$2'.
@@ -101,4 +103,3 @@ pack_repeated(repeated,true) ->
101 103 repeated_packed;
102 104 pack_repeated(repeated,_) ->
103 105 repeated.
104   -
4 src/protobuffs_scanner.xrl
@@ -7,7 +7,7 @@ WS = ([\000-\s]|%.*)
7 7 S = [\(\)\]\[\{\};=]
8 8
9 9 TYPE = (double|float|int32|int64|uint32|uint64|sint32|sint64|fixed32|fixed64|sfixed32|sfixed64|bool|string|bytes)
10   -KEYWORD = (package|option|message|enum|default|pack|extensions|to|max|service|rpc|returns)
  10 +KEYWORD = (package|option|message|enum|default|pack|extensions|to|max|service|rpc|returns|import)
11 11 REQUIREMENT = (required|optional|repeated)
12 12
13 13 Rules.
@@ -19,7 +19,7 @@ Rules.
19 19 {L}({L}|{D})+ : {token, {var, TokenLine,list_to_atom(TokenChars)}}.
20 20 '({L}|{D})+' : S = strip(TokenChars,TokenLen),
21 21 {token,{string,TokenLine,S}}.
22   -"({L}|{D})+" : S = strip(TokenChars,TokenLen),
  22 +"({L}|{D}|/)+" : S = strip(TokenChars,TokenLen),
23 23 {token,{string,TokenLine,S}}.
24 24 {S} : {token, {list_to_atom(TokenChars),TokenLine}}.
25 25 {WS}+ : skip_token.
11 test/erlang_protobuffs_SUITE.erl
@@ -122,7 +122,8 @@ all() ->
122 122 parse_addressbook_test_case,
123 123 parse_repeater_test_case,
124 124 parse_packed_repeated_test_case,
125   - parse_special_words_test_case].
  125 + parse_special_words_test_case,
  126 + parse_import_test_case].
126 127
127 128 %%--------------------------------------------------------------------
128 129 %% @spec TestCase() -> Info
@@ -163,6 +164,8 @@ parse_packed_repeated_test_case() ->
163 164 [].
164 165 parse_special_words_test_case() ->
165 166 [].
  167 +parse_import_test_case() ->
  168 + [].
166 169
167 170 %%--------------------------------------------------------------------
168 171 %% @spec TestCase(Config0) ->
@@ -295,6 +298,12 @@ parse_special_words_test_case(Config) ->
295 298 protobuffs_compile:scan_file(Path),
296 299 true = eqc:quickcheck(eqc:numtests(NumTests,protobuffs_eqc:prop_protobuffs_special_words())).
297 300
  301 +parse_import_test_case(Config) ->
  302 + DataDir = ?config(data_dir, Config),
  303 + NumTests = ?config(num_tests, Config),
  304 + Path = filename:absname(filename:join([DataDir, "import.proto"])),
  305 + protobuffs_compile:scan_file(Path, [{imports_dir, [DataDir]}]),
  306 + true = eqc:quickcheck(eqc:numtests(NumTests,protobuffs_eqc:prop_protobuffs_import())).
298 307
299 308
300 309 %%---------------------------------------------------------------------
8 test/protobuffs_eqc.erl
@@ -348,3 +348,11 @@ prop_protobuffs_special_words() ->
348 348 Decoded = special_words_pb:decode_message(special_words_pb:encode_message(SpecialWords)),
349 349 compare_messages(SpecialWords,Decoded)
350 350 end).
  351 +
  352 +prop_protobuffs_import() ->
  353 + ?FORALL({Imported},
  354 + {default({foo, {imported, string()}},{foo, undefined})},
  355 + begin
  356 + Decoded = import_pb:decode_foo(import_pb:encode(Imported)),
  357 + compare_messages(Imported, Decoded)
  358 + end).
6 test/protobuffs_tests.erl
@@ -230,6 +230,12 @@ parse_packed_repeated_test_() ->
230 230 ?_assertMatch({1,required,"string","region",number,none},lists:keyfind(1,1,Location)),
231 231 ?_assertMatch({2,required,"string","country",number,none},lists:keyfind(2,1,Location))].
232 232
  233 +parse_imported_test_() ->
  234 + Path = filename:absname("../test/erlang_protobuffs_SUITE_data/import.proto"),
  235 + Parsed = parse(Path),
  236 + [?_assertEqual(false, lists:keyfind("Imported", 2, Parsed)),
  237 + ?_assertMatch({message, "Foo", _Foo}, lists:keyfind("Foo", 2, Parsed))].
  238 +
233 239 %%--------------------------------------------------------------------
234 240 %% Help functions
235 241 %%--------------------------------------------------------------------

0 comments on commit c21a77b

Please sign in to comment.
Something went wrong with that request. Please try again.