Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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 Warren authored
View
42 src/protobuffs_compile.erl
@@ -35,7 +35,8 @@ scan_file(ProtoFile) ->
%% @doc Generats a built .beam file and header file .hrl
%% Considerd option properties: output_include_dir,
-%% output_ebin_dir
+%% output_ebin_dir,
+%% imports_dir
%% @spec scan_file(ProtoFile,Options) -> Result
%% ProtoFile = string()
%% Options = proplists()
@@ -43,7 +44,9 @@ scan_file(ProtoFile) ->
%% Reason = ext_posix() | terminated | system_limit
scan_file(ProtoFile,Options) when is_list(ProtoFile) ->
Basename = filename:basename(ProtoFile, ".proto") ++ "_pb",
- {ok,Parsed} = parse(ProtoFile),
+ {ok,FirstParsed} = parse(ProtoFile),
+ ImportPaths = ["./" | proplists:get_value(imports_dir, Options, [])],
+ Parsed = parse_imports(FirstParsed, ImportPaths),
{{msg,UntypedMessages},{enum,Enums}} = collect_full_messages(Parsed),
Messages = resolve_types(UntypedMessages,Enums),
output(Basename, Messages, Enums, Options).
@@ -58,7 +61,8 @@ generate_source(ProtoFile) ->
%% @doc Generats a source .erl file and header file .hrl
%% Consider option properties: output_include_dir,
-%% output_src_dir
+%% output_src_dir,
+%% imports_dir
%% @spec generate_source(ProtoFile,Options) -> Result
%% ProtoFile = string()
%% Options = proplists()
@@ -66,12 +70,40 @@ generate_source(ProtoFile) ->
%% Reason = ext_posix() | terminated | system_limit
generate_source(ProtoFile,Options) when is_list (ProtoFile) ->
Basename = filename:basename(ProtoFile, ".proto") ++ "_pb",
- {ok,Parsed} = parse(ProtoFile),
+ {ok,FirstParsed} = parse(ProtoFile),
+ ImportPaths = ["./" | proplists:get_value(imports_dir, Options, [])],
+ Parsed = parse_imports(FirstParsed, ImportPaths),
{{msg,UntypedMessages},{enum,Enums}} = collect_full_messages(Parsed),
Messages = resolve_types(UntypedMessages,Enums),
output_source (Basename, Messages, Enums, Options).
%% @hidden
+parse_imports(Parsed, Path) ->
+ parse_imports(Parsed, Path, []).
+
+%% @hidden
+parse_imports([], _Path, Acc) ->
+ lists:reverse(Acc);
+parse_imports([{import, File} = Head | Tail], Path, Acc) ->
+ case file:path_open(Path, File, [read]) of
+ {ok, F, Fullname} ->
+ file:close(F),
+ {ok,FirstParsed} = parse(Fullname),
+ Parsed = lists:append(FirstParsed, Tail),
+ parse_imports(Parsed, Path, [Head | Acc]);
+ {error, Error} ->
+ error_logger:warning_report([
+ "Could not do import",
+ {import, File},
+ {error, Error},
+ {path, Path}
+ ]),
+ parse_imports(Tail, Path, [Head | Acc])
+ end;
+parse_imports([Head | Tail], Path, Acc) ->
+ parse_imports(Tail, Path, [Head | Acc]).
+
+%% @hidden
output(Basename, Messages, Enums, Options) ->
case proplists:get_value(output_include_dir,Options) of
undefined ->
@@ -333,6 +365,8 @@ collect_full_messages([{package, _PackageName} | Tail], AccEnum, AccMsg) ->
collect_full_messages(Tail, AccEnum, AccMsg);
collect_full_messages([{option,_,_} | Tail], AccEnum, AccMsg) ->
collect_full_messages(Tail, AccEnum, AccMsg);
+collect_full_messages([{import, _Filename} | Tail], AccEnum, AccMsg) ->
+ collect_full_messages(Tail, AccEnum, AccMsg);
collect_full_messages([], AccEnum, AccMsg) ->
{{msg,AccMsg},{enum,AccEnum}}.
View
7 src/protobuffs_parser.yrl
@@ -1,17 +1,19 @@
Nonterminals
g_protobuffs g_members g_members_noopts g_member g_options g_option
g_messages g_message g_service g_rpcs g_rpc g_enums g_enum g_elements
-g_element g_default g_pack g_var.
+g_element g_default g_pack g_var g_import.
Terminals ';' '=' '{' '}' '[' ']' '(' ')'
-package option message enum var integer float string type requirement default pack to extensions max service rpc returns.
+package option message enum var integer float string type requirement default pack to extensions max service rpc returns import.
Rootsymbol g_protobuffs.
Endsymbol '$end'.
+g_protobuffs -> g_import g_protobuffs : ['$1' | '$2'].
g_protobuffs -> package g_var ';' g_members : [{package, safe_string('$2')}] ++ '$4'.
g_protobuffs -> g_members : '$1'.
+g_import -> import g_var ';' : {import, '$2'}.
%g_members -> g_options g_messages : '$1' ++ '$2'.
%g_members -> g_messages : '$1'.
g_members -> g_options g_members_noopts : '$1' ++ '$2'.
@@ -101,4 +103,3 @@ pack_repeated(repeated,true) ->
repeated_packed;
pack_repeated(repeated,_) ->
repeated.
-
View
4 src/protobuffs_scanner.xrl
@@ -7,7 +7,7 @@ WS = ([\000-\s]|%.*)
S = [\(\)\]\[\{\};=]
TYPE = (double|float|int32|int64|uint32|uint64|sint32|sint64|fixed32|fixed64|sfixed32|sfixed64|bool|string|bytes)
-KEYWORD = (package|option|message|enum|default|pack|extensions|to|max|service|rpc|returns)
+KEYWORD = (package|option|message|enum|default|pack|extensions|to|max|service|rpc|returns|import)
REQUIREMENT = (required|optional|repeated)
Rules.
@@ -19,7 +19,7 @@ Rules.
{L}({L}|{D})+ : {token, {var, TokenLine,list_to_atom(TokenChars)}}.
'({L}|{D})+' : S = strip(TokenChars,TokenLen),
{token,{string,TokenLine,S}}.
-"({L}|{D})+" : S = strip(TokenChars,TokenLen),
+"({L}|{D}|/)+" : S = strip(TokenChars,TokenLen),
{token,{string,TokenLine,S}}.
{S} : {token, {list_to_atom(TokenChars),TokenLine}}.
{WS}+ : skip_token.
View
11 test/erlang_protobuffs_SUITE.erl
@@ -122,7 +122,8 @@ all() ->
parse_addressbook_test_case,
parse_repeater_test_case,
parse_packed_repeated_test_case,
- parse_special_words_test_case].
+ parse_special_words_test_case,
+ parse_import_test_case].
%%--------------------------------------------------------------------
%% @spec TestCase() -> Info
@@ -163,6 +164,8 @@ parse_packed_repeated_test_case() ->
[].
parse_special_words_test_case() ->
[].
+parse_import_test_case() ->
+ [].
%%--------------------------------------------------------------------
%% @spec TestCase(Config0) ->
@@ -295,6 +298,12 @@ parse_special_words_test_case(Config) ->
protobuffs_compile:scan_file(Path),
true = eqc:quickcheck(eqc:numtests(NumTests,protobuffs_eqc:prop_protobuffs_special_words())).
+parse_import_test_case(Config) ->
+ DataDir = ?config(data_dir, Config),
+ NumTests = ?config(num_tests, Config),
+ Path = filename:absname(filename:join([DataDir, "import.proto"])),
+ protobuffs_compile:scan_file(Path, [{imports_dir, [DataDir]}]),
+ true = eqc:quickcheck(eqc:numtests(NumTests,protobuffs_eqc:prop_protobuffs_import())).
%%---------------------------------------------------------------------
View
8 test/protobuffs_eqc.erl
@@ -348,3 +348,11 @@ prop_protobuffs_special_words() ->
Decoded = special_words_pb:decode_message(special_words_pb:encode_message(SpecialWords)),
compare_messages(SpecialWords,Decoded)
end).
+
+prop_protobuffs_import() ->
+ ?FORALL({Imported},
+ {default({foo, {imported, string()}},{foo, undefined})},
+ begin
+ Decoded = import_pb:decode_foo(import_pb:encode(Imported)),
+ compare_messages(Imported, Decoded)
+ end).
View
6 test/protobuffs_tests.erl
@@ -230,6 +230,12 @@ parse_packed_repeated_test_() ->
?_assertMatch({1,required,"string","region",number,none},lists:keyfind(1,1,Location)),
?_assertMatch({2,required,"string","country",number,none},lists:keyfind(2,1,Location))].
+parse_imported_test_() ->
+ Path = filename:absname("../test/erlang_protobuffs_SUITE_data/import.proto"),
+ Parsed = parse(Path),
+ [?_assertEqual(false, lists:keyfind("Imported", 2, Parsed)),
+ ?_assertMatch({message, "Foo", _Foo}, lists:keyfind("Foo", 2, Parsed))].
+
%%--------------------------------------------------------------------
%% Help functions
%%--------------------------------------------------------------------
Please sign in to comment.
Something went wrong with that request. Please try again.