Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

converted protobuffs_compile to use erltl

  • Loading branch information...
commit 62d3b963698d6d6fe86c370dfab8caf8858f4dd1 1 parent 6a668ae
Jacob Vorreuter authored
View
6 generate.escript
@@ -0,0 +1,6 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%! -pa ebin -sasl errlog_type error -boot start_sasl -noshell
+
+main([Input]) ->
+ ok = protobuffs:generate(Input).
View
7 src/protobuffs.erl
@@ -1,6 +1,6 @@
%% @doc A protcol buffers encoding and decoding module.
-module(protobuffs).
--export([encode/3, decode/2, decode_many/1]).
+-export([encode/3, decode/2, decode_many/1, generate/1]).
-define(TYPE_VARINT, 0).
-define(TYPE_64BIT, 1).
@@ -9,6 +9,11 @@
-define(TYPE_END_GROUP, 4).
-define(TYPE_32BIT, 5).
+generate(Input_Path) ->
+ IOList = protobuffs_compile:render(Input_Path),
+ ok = file:write_file(filename:basename(Input_Path, ".proto") ++ "_pb.erl", iolist_to_binary(IOList)),
+ ok.
+
%% @spec encode(FieldID, Value, Type) -> Result
%% FieldID = integer()
%% Value = any()
View
445 src/protobuffs_compile.erl
@@ -1,445 +0,0 @@
-%% @doc Create modules for decoding and encoding protocolo buffers messages out of .proto files.
--module(protobuffs_compile).
--export([scan_file/1]).
-
-%% @spec scan_file(string()) -> ok
-%% @doc Scan a .proto file and try to create a module for it. This process
-%% creates a number of encoding, decoding and validation functions for each
-%% message contained.
-scan_file(Filename) ->
- {ok, Data} = file:read_file(Filename),
- Raw = scan(binary_to_list(Data)),
- Parsed = parse(Raw),
- true = write_header(Parsed, filename:basename(Filename, ".proto") ++ "_pb.hrl"),
- true = write_module(Parsed, filename:basename(Filename, ".proto") ++ "_pb.erl"),
- ok.
-
-%% @hidden
-parse(Data) -> parse(Data, []).
-
-%% @hidden
-parse([], Acc) -> lists:reverse(Acc);
-parse([{'}', _Line} | Tail], Acc) -> {Acc, Tail};
-parse([{enum, _Line}, {bareword, _Line, MessageName}, {'{', _Line} | Tail], Acc) ->
- {Res, Tail2} = parse(Tail, []),
- parse(Tail2, [{enum, MessageName, lists:reverse(Res)} | Acc]);
-parse([{message, _Line}, {bareword, _Line, MessageName}, {'{', _Line} | Tail], Acc) ->
- {Res, Tail2} = parse(Tail, []),
- parse(Tail2, [{message, MessageName, lists:reverse(Res)} | Acc]);
-parse([{bareword, _Line, FieldName}, {'=', _Line}, {number, _Line, Value}, {';', _Line} | Tail], Acc) ->
- parse(Tail, [{enum, Value, FieldName} | Acc]);
-parse([{Type, _Line}, {bareword, _Line, Field}, {bareword, _Line, FieldName}, {'=', _Line}, {FieldType, _Line, Position}, {'[', _Line}, {bareword, _Line,"default"}, {'=', _Line}, {_DefaultType, _Line, Default}, {']', _Line}, {';', _Line} | Tail], Acc) ->
- parse(Tail, [{Position, Type, Field, FieldName, FieldType, Default} | Acc]);
-parse([{Type, _Line}, {bareword, _Line, Field}, {bareword, _Line, FieldName}, {'=', _Line}, {FieldType, _Line, Position}, {';', _Line} | Tail], Acc) ->
- parse(Tail, [{Position, Type, Field, FieldName, FieldType, none} | Acc]);
-parse([{'$end', _} | Tail], Acc) ->
- parse(Tail, Acc);
-parse([Head | Tail], Acc) ->
- parse(Tail, [Head | Acc]).
-
-%% @hidden
-write_header(Data, Filename) ->
- Messages = collect_messages(Data, []),
- {ok, FileRef} = file:open(Filename, [write]),
- lists:foreach(
- fun({Name, Fields}) ->
- OutFields = [string:to_lower(B) || {_A, B} <- lists:keysort(1, Fields)],
- JoinedFields = string:join(OutFields, ", "),
- io:format(FileRef, "-record(~s, {~s}).~n", [string:to_lower(Name), JoinedFields])
- end,
- Messages
- ),
- ok == file:close(FileRef).
-
-%% @hidden
-write_module(Data, Filename) ->
- Messages = collect_full_messages(Data, []),
- {ok, FileRef} = file:open(Filename, [write]),
- io:format(FileRef, "-module(~s).~n", [filename:basename(Filename, ".erl")]),
- DecodeString = string:join(
- ["decode_" ++ string:to_lower(Name) ++ "/1" || {Name, _} <- Messages] ++
- ["encode_" ++ string:to_lower(Name) ++ "/1" || {Name, _} <- Messages]
- ,", "),
- io:format(FileRef, "-export([~s]).~n", [DecodeString]),
- io:format(FileRef, "-include(\"~s\").~n~n", [filename:basename(Filename, ".erl") ++ ".hrl"]),
- write_decode_message(FileRef, Messages),
- write_encode_message(FileRef, Messages),
- ok == file:close(FileRef).
-
-%% @hidden
-write_encode_message(_, []) -> ok;
-write_encode_message(FileRef, [{Name, Fields} |Tail]) ->
- EncodeElements = lists:foldl(
- fun(Field, Acc) ->
- {Position, Rule, FieldType, FieldName, _, Default} = Field,
- [io_lib:format("{~p, ~p, Rec#~s.~s, ~p, ~p}", [Position, Rule, string:to_lower(Name), FieldName, list_to_atom(string:to_lower(FieldType)), Default]) | Acc]
- end,
- [],
- lists:keysort(1, Fields)
- ),
- EncodeString = string:join(lists:reverse(EncodeElements), ", "),
- io:format(
- FileRef,
- "encode_~s(Rec) -> ~n"
- " EncodeData = [~s], ~n"
- " erlang:iolist_to_binary(lists:reverse(lists:foldl(fun({Pos, Rule, Data, Type, Default}, Acc) ->
- case [Rule, Data, Type] of
- [_, undefined, _] ->
- case Default of
- none -> Acc;
- _ ->
- [protobuffs:encode(Pos, Data, Type) | Acc]
- end;
- [_, Data, Type] when is_binary(Data), Type =/= bytes ->
- [protobuffs:encode(Pos, Data, bytes) | Acc];
- [_, Data, Type] when is_tuple(Data) ->
- [RecName | _] = erlang:tuple_to_list(Data),
- ToEncode = apply(?MODULE, list_to_atom(\"encode_\" ++ atom_to_list(RecName)), [Data]),
- [protobuffs:encode(Pos, ToEncode, bytes) | Acc];
- [repeated, [Head|_]=List, Type] when is_tuple(Head) ->
- [RecName | _] = erlang:tuple_to_list(Head),
- Encoded =
- list_to_binary([begin
- Method = list_to_atom(\"encode_\" ++ atom_to_list(RecName)),
- ToEncode = apply(?MODULE, Method, [Record]),
- protobuffs:encode(Pos, ToEncode, bytes)
- end || Record <- List]),
- [Encoded | Acc];
- [repeated, List, Type] ->
- Encoded = [protobuffs:encode(Pos, Item, Type) || Item <- List],
- [Encoded | Acc];
- [_, Data, Type] ->
- case atom_to_list(Type) of
- \"int\" ++ _ when is_list(Data) ->
- [protobuffs:encode(Pos, list_to_integer(Data), Type) | Acc];
- _ ->
- [protobuffs:encode(Pos, Data, Type) | Acc]
- end
- end
- end,[], EncodeData))). ~n~n",
- [string:to_lower(Name), EncodeString]
- ),
- write_encode_message(FileRef, Tail).
-
-%% @hidden
-write_decode_message(_, []) -> ok;
-write_decode_message(FileRef, [{Name, Fields} | Tail]) ->
- AllElements = lists:foldl(
- fun(Field, Acc) ->
- case Field of
- {_, _, _, _, _, none} -> Acc;
- {Position, _, _, _, _, Default} ->
- [io_lib:format("{~p, ~p}", [Position, Default]) | Acc]
- end
- end,
- [],
- lists:keysort(1, Fields)
- ),
- AllElementsString = string:join(lists:reverse(AllElements), ", "),
- io:format(
- FileRef,
- "decode_~s(Data) when is_binary(Data) -> ~n"
- " DecodedData = protobuffs:decode_many(Data), ~n"
- " ~s_to_record(DecodedData ++ [~s]).~n~n",
- [string:to_lower(Name), string:to_lower(Name), AllElementsString]
- ),
- CasePosString = lists:foldl(
- fun(Field, Acc) ->
- case Field of
- {FPos, repeated, [C|_]=RecName, FName, _, _}
- when C >= $A, C =< $Z ->
- io_lib:format(" {~p, Data} -> ~n"
- " DecodedData = apply(?MODULE, decode_~s, [Data]), ~n"
- " case Rec#~s.~s of~n"
- " undefined -> ~n"
- " Rec#~s{ ~s = [DecodedData]};~n"
- " List -> ~n"
- " Rec#~s{ ~s = [DecodedData | List] }~n"
- " end;~n",
- [FPos, string:to_lower(RecName), string:to_lower(Name), FName, string:to_lower(Name), FName, string:to_lower(Name), FName]) ++ Acc;
- {FPos, repeated, FType, FName, _, _} ->
- DecodedData =
- case FType of
- "string" -> "binary_to_list(Data)";
- _ -> "Data"
- end,
- io_lib:format(" {~p, Data} -> ~n"
- " DecodedData = ~s,~n"
- " case Rec#~s.~s of~n"
- " undefined -> ~n"
- " Rec#~s{ ~s = [DecodedData]};~n"
- " List -> ~n"
- " Rec#~s{ ~s = [DecodedData | List] }~n"
- " end;~n",
- [FPos, DecodedData, string:to_lower(Name), FName, string:to_lower(Name), FName, string:to_lower(Name), FName]) ++ Acc;
- {FPos, _, [C|_]=RecName, FName, _, _}
- when C >= $A, C =< $Z->
- io_lib:format(" {~p, Data} -> ~n"
- " Data1 = apply(?MODULE, decode_~s, [Data]),~n"
- " Rec#~s{ ~s = Data1};~n", [FPos, string:to_lower(RecName), string:to_lower(Name), FName]) ++ Acc;
- {FPos, _, FType, FName, _, Default} ->
- DecodedData =
- case FType of
- "string" -> "binary_to_list(Data)";
- _ -> "Data"
- end,
- io_lib:format(
- " {~p, Data} -> ~n"
- " DecodedData = ~s,~n"
- " Rec#~s{ ~s = DecodedData};~n",
- [FPos, DecodedData, string:to_lower(Name), FName]
- ) ++ Acc
- end
- end, "", Fields),
- io:format(
- FileRef,
- "~s_to_record(DecodedData) -> ~n"
- " ~s_to_record(DecodedData, #~s{}). ~n"
- "~s_to_record([], Acc) -> Acc; ~n"
- "~s_to_record([Head | Tail], Rec) -> ~n"
- " NewRec = case Head of ~n~s"
- " _ -> Rec %% Ruh-roh ~n"
- " end, ~n"
- " ~s_to_record(Tail, NewRec).~n~n",
- [
- string:to_lower(Name), string:to_lower(Name),
- string:to_lower(Name), string:to_lower(Name),
- string:to_lower(Name),
- CasePosString, string:to_lower(Name)
- ]
- ),
- write_decode_message(FileRef, Tail).
-
-%% @hidden
-collect_messages([], Acc) -> Acc;
-collect_messages([{message, Name, Fields} | Tail], Acc) ->
- FieldsOut = lists:foldl(
- fun ({A, _, _, B, _, _}, TmpAcc) ->
- [{A, B} | TmpAcc];
- (_, TmpAcc) -> TmpAcc
- end,
- [],
- Fields
- ),
- SubMessages = lists:foldl(
- fun ({message, C, D}, TmpAcc) -> [{message, C, D} | TmpAcc];
- (_, TmpAcc) -> TmpAcc
- end,
- [],
- Fields
- ),
- collect_messages(Tail ++ SubMessages, [{Name, FieldsOut} | Acc]).
-
-%% @hidden
-collect_full_messages([], Acc) -> Acc;
-collect_full_messages([{message, Name, Fields} | Tail], Acc) ->
- FieldsOut = lists:foldl(
- fun (Input, TmpAcc) ->
- case Input of
- {_, _, _, _, _, _} -> [Input | TmpAcc];
- _ -> TmpAcc
- end
- end,
- [],
- Fields
- ),
- SubMessages = lists:foldl(
- fun ({message, C, D}, TmpAcc) -> [{message, C, D} | TmpAcc];
- (_, TmpAcc) -> TmpAcc
- end,
- [],
- Fields
- ),
- collect_full_messages(Tail ++ SubMessages, [{Name, FieldsOut} | Acc]).
-
-scan(String) ->
- scan(String, [], 1).
-
-%% @hidden
-scan([${|Rest], Accum, Line) ->
- scan(Rest, [{'{', Line}|Accum], Line);
-scan([$}|Rest], Accum, Line) ->
- scan(Rest, [{'}', Line}|Accum], Line);
-scan([$[|Rest], Accum, Line) ->
- scan(Rest, [{'[', Line}|Accum], Line);
-scan([$]|Rest], Accum, Line) ->
- scan(Rest, [{']', Line}|Accum], Line);
-scan([$(|Rest], Accum, Line) ->
- scan(Rest, [{'(', Line}|Accum], Line);
-scan([$)|Rest], Accum, Line) ->
- scan(Rest, [{')', Line}|Accum], Line);
-scan([$=|Rest], Accum, Line) ->
- scan(Rest, [{'=', Line}|Accum], Line);
-scan([$;|Rest], Accum, Line) ->
- scan(Rest, [{';', Line}|Accum], Line);
-scan([$,|Rest], Accum, Line) ->
- scan(Rest, [{',', Line}|Accum], Line);
-scan([Digit|_] = String, Accum, Line)
- when Digit >= $0, Digit =< $9 ->
- {Number, Rest} = scan_number(String),
- scan(Rest, [{number, Line, Number}|Accum], Line);
-scan([$-, Digit|_] = String, Accum, Line)
- when Digit >= $0, Digit =< $9 ->
- {Number, Rest} = scan_number(tl(String)),
- scan(Rest, [{number, Line, -Number}|Accum], Line);
-scan([$\n|Rest], Accum, Line) ->
- scan(Rest, Accum, Line + 1);
-scan([WS|Rest], Accum, Line)
- when WS =:= 32; WS =:= $\t ->
- scan(Rest, Accum, Line);
-scan([$/, $/|Rest], Accum, Line) ->
- scan(skip_to_newline(Rest), Accum, Line);
-scan([$/, $*|Rest], Accum, Line) ->
- {Rest1, Line1} = skip_comment(Rest, Line),
- scan(Rest1, Accum, Line1);
-scan([$"|_] = String, Accum, Line) ->
- {Strval, Rest, Line1} = scan_string(String, Line),
- scan(Rest, [{string, Line, Strval}|Accum], Line1);
-scan([C|_] = String, Accum, Line)
- when C >= $A, C =< $Z;
- C >= $a, C =< $z;
- C =:= $_ ->
- {Identifier, Rest} = scan_identifier(String),
- Token = case get_keyword(Identifier) of
- Keyword when is_atom(Keyword) ->
- {Keyword, Line};
- {bareword, Bareword} ->
- {bareword, Line, Bareword}
- end,
- scan(Rest, [Token|Accum], Line);
-scan([], Accum, Line) ->
- lists:reverse([{'$end', Line}|Accum]);
-scan([C|_], _Accum, Line) ->
- erlang:error({invalid_character, [C], Line}).
-
-%% @hidden
-scan_identifier(String) ->
- scan_identifier(String, "").
-
-%% @hidden
-scan_identifier([C|Rest], Accum)
- when C >= $A, C =< $Z;
- C >= $a, C =< $z;
- C >= $0, C =< $9;
- C =:= $_;
- C =:= $. ->
- scan_identifier(Rest, [C|Accum]);
-scan_identifier(Rest, Accum) ->
- {lists:reverse(Accum), Rest}.
-
-%% @hidden
-scan_number(String) ->
- {A, Rest1} = scan_integer(String),
- case Rest1 of
- [$.|Fraction] ->
- {B, Rest2} = scan_identifier(Fraction),
- {A + list_to_float("0." ++ B), Rest2};
- [$e|Exp] ->
- {B, Rest2} = scan_integer(Exp),
- {list_to_float(integer_to_list(A) ++ ".0e" ++ integer_to_list(B)), Rest2};
- [$x|Rest] when A =:= 0 ->
- {Hex, Rest2} = scan_identifier(Rest),
- {erlang:list_to_integer(Hex, 16), Rest2};
- _ ->
- {A, Rest1}
- end.
-
-%% @hidden
-scan_integer(String) ->
- scan_integer(String, 0).
-
-%% @hidden
-scan_integer([D|Rest], Accum)
- when D >= $0, D =< $9 ->
- scan_integer(Rest, Accum * 10 + (D - $0));
-scan_integer(Rest, Accum) ->
- {Accum, Rest}.
-
-%% @hidden
-scan_string([$"|String], Line) ->
- scan_string(String, "", Line).
-
-%% @hidden
-scan_string([$"|Rest], Accum, Line) ->
- {lists:reverse(Accum), Rest, Line};
-scan_string([$\\, $a|Rest], Accum, Line) ->
- scan_string(Rest, [7|Accum], Line);
-scan_string([$\\, $e|Rest], Accum, Line) ->
- scan_string(Rest, [$\e|Accum], Line);
-scan_string([$\\, $f|Rest], Accum, Line) ->
- scan_string(Rest, [$\f|Accum], Line);
-scan_string([$\\, $n|Rest], Accum, Line) ->
- scan_string(Rest, [$\n|Accum], Line);
-scan_string([$\\, $r|Rest], Accum, Line) ->
- scan_string(Rest, [$\r|Accum], Line);
-scan_string([$\\, $t|Rest], Accum, Line) ->
- scan_string(Rest, [$\t|Accum], Line);
-scan_string([$\\, $v|Rest], Accum, Line) ->
- scan_string(Rest, [$\v|Accum], Line);
-scan_string([$\\, D1, D2, D3|Rest], Accum, Line)
- when D1 >= $0, D1 =< $7, D2 >= $0, D2 =< $7, D3 >= $0, D3 =< $7 ->
- scan_string(Rest, [erlang:list_to_integer([D1, D2, D3], 8)|Accum], Line);
-scan_string([$\\, $x, H1, H2|Rest], Accum, Line) ->
- scan_string(Rest, [erlang:list_to_integer([H1, H2], 16)|Accum], Line);
-scan_string([$\\, Char|Rest], Accum, Line) ->
- scan_string(Rest, [Char|Accum], Line);
-scan_string([$\n|Rest], Accum, Line) ->
- scan_string(Rest, [$\n|Accum], Line + 1);
-scan_string([Char|Rest], Accum, Line) ->
- scan_string(Rest, [Char|Accum], Line).
-
-%% @hidden
-skip_to_newline([$\n|Rest]) ->
- Rest;
-skip_to_newline([]) ->
- [];
-skip_to_newline([_|Rest]) ->
- skip_to_newline(Rest).
-
-%% @hidden
-skip_comment([$*, $/|Rest], Line) ->
- {Rest, Line};
-skip_comment([$\n|Rest], Line) ->
- skip_comment(Rest, Line + 1);
-skip_comment([_|Rest], Line) ->
- skip_comment(Rest, Line).
-
-%% @hidden
-get_keyword("import") ->
- import;
-get_keyword("package") ->
- package;
-get_keyword("option") ->
- option;
-get_keyword("message") ->
- message;
-get_keyword("group") ->
- group;
-get_keyword("enum") ->
- enum;
-get_keyword("extend") ->
- extend;
-get_keyword("service") ->
- service;
-get_keyword("rpc") ->
- rpc;
-get_keyword("required") ->
- required;
-get_keyword("optional") ->
- optional;
-get_keyword("repeated") ->
- repeated;
-get_keyword("returns") ->
- returns;
-get_keyword("extensions") ->
- extensions;
-get_keyword("max") ->
- max;
-get_keyword("to") ->
- to;
-get_keyword("true") ->
- true;
-get_keyword("false") ->
- false;
-get_keyword(Bareword) ->
- {bareword, Bareword}.
View
129 src/protobuffs_compile.et
@@ -0,0 +1,129 @@
+<%?
+ Parsed = protobuffs_parser:parse_file(Data),
+ Messages = protobuffs_utils:collect_full_messages(Parsed),
+ Include = filename:basename(Data, ".proto") ++ "_pb.hrl",
+ protobuffs_utils:write_header(Messages, Include)
+%>
+-module(<% filename:basename(Data, ".proto") %>_pb).
+-export([<% protobuffs_utils:function_exports(Messages) %>]).
+-include("<% Include %>").
+
+<% [write_encode(Name, Fields) || {Name, Fields} <- Messages] %>
+
+<% [write_decode(Name, Fields) || {Name, Fields} <- Messages] %>
+
+<%! ====================
+ WRITE_DECODE
+ ==================== %>
+<%@ write_encode(Name, Record_Fields) %>
+encode_<% string:to_lower(Name) %>(Rec) ->
+ EncodeData = [<% protobuffs_utils:record_data(Name, Record_Fields) %>],
+ erlang:iolist_to_binary(lists:reverse(
+ lists:foldl(
+ fun({Pos, Rule, Data, Type, Default}, Acc) ->
+ case [Rule, Data, Type] of
+ [_, undefined, _] ->
+ case Default of
+ none -> Acc;
+ _ ->
+ [protobuffs:encode(Pos, Data, Type) | Acc]
+ end;
+ [_, Data, Type] when is_binary(Data), Type =/= bytes ->
+ [protobuffs:encode(Pos, Data, bytes) | Acc];
+ [_, Data, Type] when is_tuple(Data) ->
+ [RecName | _] = erlang:tuple_to_list(Data),
+ ToEncode = apply(?MODULE, list_to_atom("encode_" ++ atom_to_list(RecName)), [Data]),
+ [protobuffs:encode(Pos, ToEncode, bytes) | Acc];
+ [repeated, [Head|_]=List, Type] when is_tuple(Head) ->
+ [RecName | _] = erlang:tuple_to_list(Head),
+ Encoded =
+ list_to_binary([begin
+ Method = list_to_atom("encode_" ++ atom_to_list(RecName)),
+ ToEncode = apply(?MODULE, Method, [Record]),
+ protobuffs:encode(Pos, ToEncode, bytes)
+ end || Record <- List]),
+ [Encoded | Acc];
+ [repeated, List, Type] ->
+ Encoded = [protobuffs:encode(Pos, Item, Type) || Item <- List],
+ [Encoded | Acc];
+ [_, Data, Type] ->
+ case atom_to_list(Type) of
+ "int" ++ _ when is_list(Data) ->
+ [protobuffs:encode(Pos, list_to_integer(Data), Type) | Acc];
+ _ ->
+ [protobuffs:encode(Pos, Data, Type) | Acc]
+ end
+ end
+ end, [], EncodeData))).
+<%! ====================
+ GET RECORD ENCODE DATA
+ ==================== %>
+<%@ encoded_data(Name, {Position, Rule, FieldType, FieldName, _, Default}) %>
+{<% Position %>, <% atom_to_list(Rule) %>, <% string:to_lower(Name) %>, <% FieldName %>, <% string:to_lower(FieldType) %>, <% atom_to_list(Default) %>}
+<%! ====================
+ WRITE_DECODE
+ ==================== %>
+<%@ write_decode(Name, Record_Fields) %>
+decode_<% string:to_lower(Name) %>(Data) when is_binary(Data) ->
+ DecodedData = protobuffs:decode_many(Data),
+ <% string:to_lower(Name) %>_to_record(DecodedData ++ [<% protobuffs_utils:default_values(Record_Fields) %>]).
+<%! ====================
+ DATA TO RECORD
+ ==================== %>
+<% string:to_lower(Name) %>_to_record(DecodedData) ->
+ <% string:to_lower(Name) %>_to_record(DecodedData, #<% string:to_lower(Name) %>{}).
+<% string:to_lower(Name) %>_to_record([], Acc) -> Acc;
+<% string:to_lower(Name) %>_to_record([Head | Tail], Rec) ->
+ NewRec =
+ case Head of
+<% [record_case(Name, Field) || Field <- lists:reverse(Record_Fields)] %>
+ _ -> Rec %% Ruh-roh
+ end,
+ <% string:to_lower(Name) %>_to_record(Tail, NewRec).
+
+<%! ====================
+ DECODE CASE STATEMENTS
+ ==================== %>
+<%@ record_case(Name, {FPos, repeated, [C|_]=RecName, FName, _, _}) when C >= $A, C =< $Z %>
+ {<% integer_to_list(FPos) %>, Data} ->
+ DecodedData = apply(?MODULE, decode_<% string:to_lower(RecName) %>, [Data]),
+ case Rec#<% string:to_lower(Name) %>.<% FName %> of
+ undefined ->
+ Rec#<% string:to_lower(Name) %>{ <% FName %> = [DecodedData]};
+ List ->
+ Rec#<% string:to_lower(Name) %>{ <% FName %> = [DecodedData | List] }
+ end;
+
+<%@ record_case(Name, {FPos, repeated, FType, FName, _, _}) %>
+<%?
+ DecodedData =
+ case FType of
+ "string" -> "binary_to_list(Data)";
+ _ -> "Data"
+ end
+%>
+ {<% integer_to_list(FPos) %>, Data} ->
+ DecodedData = <% DecodedData %>,
+ case Rec#<% string:to_lower(Name) %>.<% FName %> of
+ undefined ->
+ Rec#<% string:to_lower(Name) %>{ <% FName %> = [DecodedData]};
+ List ->
+ Rec#<% string:to_lower(Name) %>{ <% FName %> = [DecodedData | List] }
+ end;
+
+<%@ record_case(Name, {FPos, _, [C|_]=RecName, FName, _, _}) when C >= $A, C =< $Z %>
+ {<% integer_to_list(FPos) %>, Data} ->
+ Data1 = apply(?MODULE, decode_<% string:to_lower(RecName) %>, [Data]),
+ Rec#<% string:to_lower(Name) %>{ <% FName %> = Data1};
+
+<%@ record_case(Name, {FPos, _, FType, FName, _, Default}) %>
+<%?
+ DecodedData =
+ case FType of
+ "string" -> "binary_to_list(Data)";
+ _ -> "Data"
+ end
+%>
+ {<% integer_to_list(FPos) %>, Data} ->
+ DecodedData = <% DecodedData %>,
+ Rec#<% string:to_lower(Name) %>{ <% FName %> = DecodedData};
View
223 src/protobuffs_parser.erl
@@ -0,0 +1,223 @@
+-module(protobuffs_parser).
+
+-export([parse_file/1]).
+
+parse_file(Filename) ->
+ {ok, Data} = file:read_file(Filename),
+ Raw = scan(binary_to_list(Data)),
+ parse(Raw).
+
+%% @hidden
+parse(Data) -> parse(Data, []).
+
+%% @hidden
+parse([], Acc) -> lists:reverse(Acc);
+parse([{'}', _Line} | Tail], Acc) -> {Acc, Tail};
+parse([{enum, _Line}, {bareword, _Line, MessageName}, {'{', _Line} | Tail], Acc) ->
+ {Res, Tail2} = parse(Tail, []),
+ parse(Tail2, [{enum, MessageName, lists:reverse(Res)} | Acc]);
+parse([{message, _Line}, {bareword, _Line, MessageName}, {'{', _Line} | Tail], Acc) ->
+ {Res, Tail2} = parse(Tail, []),
+ parse(Tail2, [{message, MessageName, lists:reverse(Res)} | Acc]);
+parse([{bareword, _Line, FieldName}, {'=', _Line}, {number, _Line, Value}, {';', _Line} | Tail], Acc) ->
+ parse(Tail, [{enum, Value, FieldName} | Acc]);
+parse([{Type, _Line}, {bareword, _Line, Field}, {bareword, _Line, FieldName}, {'=', _Line}, {FieldType, _Line, Position}, {'[', _Line}, {bareword, _Line,"default"}, {'=', _Line}, {_DefaultType, _Line, Default}, {']', _Line}, {';', _Line} | Tail], Acc) ->
+ parse(Tail, [{Position, Type, Field, FieldName, FieldType, Default} | Acc]);
+parse([{Type, _Line}, {bareword, _Line, Field}, {bareword, _Line, FieldName}, {'=', _Line}, {FieldType, _Line, Position}, {';', _Line} | Tail], Acc) ->
+ parse(Tail, [{Position, Type, Field, FieldName, FieldType, none} | Acc]);
+parse([{'$end', _} | Tail], Acc) ->
+ parse(Tail, Acc);
+parse([Head | Tail], Acc) ->
+ parse(Tail, [Head | Acc]).
+
+scan(String) ->
+ scan(String, [], 1).
+
+%% @hidden
+scan([${|Rest], Accum, Line) ->
+ scan(Rest, [{'{', Line}|Accum], Line);
+scan([$}|Rest], Accum, Line) ->
+ scan(Rest, [{'}', Line}|Accum], Line);
+scan([$[|Rest], Accum, Line) ->
+ scan(Rest, [{'[', Line}|Accum], Line);
+scan([$]|Rest], Accum, Line) ->
+ scan(Rest, [{']', Line}|Accum], Line);
+scan([$(|Rest], Accum, Line) ->
+ scan(Rest, [{'(', Line}|Accum], Line);
+scan([$)|Rest], Accum, Line) ->
+ scan(Rest, [{')', Line}|Accum], Line);
+scan([$=|Rest], Accum, Line) ->
+ scan(Rest, [{'=', Line}|Accum], Line);
+scan([$;|Rest], Accum, Line) ->
+ scan(Rest, [{';', Line}|Accum], Line);
+scan([$,|Rest], Accum, Line) ->
+ scan(Rest, [{',', Line}|Accum], Line);
+scan([Digit|_] = String, Accum, Line)
+ when Digit >= $0, Digit =< $9 ->
+ {Number, Rest} = scan_number(String),
+ scan(Rest, [{number, Line, Number}|Accum], Line);
+scan([$-, Digit|_] = String, Accum, Line)
+ when Digit >= $0, Digit =< $9 ->
+ {Number, Rest} = scan_number(tl(String)),
+ scan(Rest, [{number, Line, -Number}|Accum], Line);
+scan([$\n|Rest], Accum, Line) ->
+ scan(Rest, Accum, Line + 1);
+scan([WS|Rest], Accum, Line)
+ when WS =:= 32; WS =:= $\t ->
+ scan(Rest, Accum, Line);
+scan([$/, $/|Rest], Accum, Line) ->
+ scan(skip_to_newline(Rest), Accum, Line);
+scan([$/, $*|Rest], Accum, Line) ->
+ {Rest1, Line1} = skip_comment(Rest, Line),
+ scan(Rest1, Accum, Line1);
+scan([$"|_] = String, Accum, Line) ->
+ {Strval, Rest, Line1} = scan_string(String, Line),
+ scan(Rest, [{string, Line, Strval}|Accum], Line1);
+scan([C|_] = String, Accum, Line)
+ when C >= $A, C =< $Z;
+ C >= $a, C =< $z;
+ C =:= $_ ->
+ {Identifier, Rest} = scan_identifier(String),
+ Token = case get_keyword(Identifier) of
+ Keyword when is_atom(Keyword) ->
+ {Keyword, Line};
+ {bareword, Bareword} ->
+ {bareword, Line, Bareword}
+ end,
+ scan(Rest, [Token|Accum], Line);
+scan([], Accum, Line) ->
+ lists:reverse([{'$end', Line}|Accum]);
+scan([C|_], _Accum, Line) ->
+ erlang:error({invalid_character, [C], Line}).
+
+%% @hidden
+scan_identifier(String) ->
+ scan_identifier(String, "").
+
+%% @hidden
+scan_identifier([C|Rest], Accum)
+ when C >= $A, C =< $Z;
+ C >= $a, C =< $z;
+ C >= $0, C =< $9;
+ C =:= $_;
+ C =:= $. ->
+ scan_identifier(Rest, [C|Accum]);
+scan_identifier(Rest, Accum) ->
+ {lists:reverse(Accum), Rest}.
+
+%% @hidden
+scan_number(String) ->
+ {A, Rest1} = scan_integer(String),
+ case Rest1 of
+ [$.|Fraction] ->
+ {B, Rest2} = scan_identifier(Fraction),
+ {A + list_to_float("0." ++ B), Rest2};
+ [$e|Exp] ->
+ {B, Rest2} = scan_integer(Exp),
+ {list_to_float(integer_to_list(A) ++ ".0e" ++ integer_to_list(B)), Rest2};
+ [$x|Rest] when A =:= 0 ->
+ {Hex, Rest2} = scan_identifier(Rest),
+ {erlang:list_to_integer(Hex, 16), Rest2};
+ _ ->
+ {A, Rest1}
+ end.
+
+%% @hidden
+scan_integer(String) ->
+ scan_integer(String, 0).
+
+%% @hidden
+scan_integer([D|Rest], Accum)
+ when D >= $0, D =< $9 ->
+ scan_integer(Rest, Accum * 10 + (D - $0));
+scan_integer(Rest, Accum) ->
+ {Accum, Rest}.
+
+%% @hidden
+scan_string([$"|String], Line) ->
+ scan_string(String, "", Line).
+
+%% @hidden
+scan_string([$"|Rest], Accum, Line) ->
+ {lists:reverse(Accum), Rest, Line};
+scan_string([$\\, $a|Rest], Accum, Line) ->
+ scan_string(Rest, [7|Accum], Line);
+scan_string([$\\, $e|Rest], Accum, Line) ->
+ scan_string(Rest, [$\e|Accum], Line);
+scan_string([$\\, $f|Rest], Accum, Line) ->
+ scan_string(Rest, [$\f|Accum], Line);
+scan_string([$\\, $n|Rest], Accum, Line) ->
+ scan_string(Rest, [$\n|Accum], Line);
+scan_string([$\\, $r|Rest], Accum, Line) ->
+ scan_string(Rest, [$\r|Accum], Line);
+scan_string([$\\, $t|Rest], Accum, Line) ->
+ scan_string(Rest, [$\t|Accum], Line);
+scan_string([$\\, $v|Rest], Accum, Line) ->
+ scan_string(Rest, [$\v|Accum], Line);
+scan_string([$\\, D1, D2, D3|Rest], Accum, Line)
+ when D1 >= $0, D1 =< $7, D2 >= $0, D2 =< $7, D3 >= $0, D3 =< $7 ->
+ scan_string(Rest, [erlang:list_to_integer([D1, D2, D3], 8)|Accum], Line);
+scan_string([$\\, $x, H1, H2|Rest], Accum, Line) ->
+ scan_string(Rest, [erlang:list_to_integer([H1, H2], 16)|Accum], Line);
+scan_string([$\\, Char|Rest], Accum, Line) ->
+ scan_string(Rest, [Char|Accum], Line);
+scan_string([$\n|Rest], Accum, Line) ->
+ scan_string(Rest, [$\n|Accum], Line + 1);
+scan_string([Char|Rest], Accum, Line) ->
+ scan_string(Rest, [Char|Accum], Line).
+
+%% @hidden
+skip_to_newline([$\n|Rest]) ->
+ Rest;
+skip_to_newline([]) ->
+ [];
+skip_to_newline([_|Rest]) ->
+ skip_to_newline(Rest).
+
+%% @hidden
+skip_comment([$*, $/|Rest], Line) ->
+ {Rest, Line};
+skip_comment([$\n|Rest], Line) ->
+ skip_comment(Rest, Line + 1);
+skip_comment([_|Rest], Line) ->
+ skip_comment(Rest, Line).
+
+%% @hidden
+get_keyword("import") ->
+ import;
+get_keyword("package") ->
+ package;
+get_keyword("option") ->
+ option;
+get_keyword("message") ->
+ message;
+get_keyword("group") ->
+ group;
+get_keyword("enum") ->
+ enum;
+get_keyword("extend") ->
+ extend;
+get_keyword("service") ->
+ service;
+get_keyword("rpc") ->
+ rpc;
+get_keyword("required") ->
+ required;
+get_keyword("optional") ->
+ optional;
+get_keyword("repeated") ->
+ repeated;
+get_keyword("returns") ->
+ returns;
+get_keyword("extensions") ->
+ extensions;
+get_keyword("max") ->
+ max;
+get_keyword("to") ->
+ to;
+get_keyword("true") ->
+ true;
+get_keyword("false") ->
+ false;
+get_keyword(Bareword) ->
+ {bareword, Bareword}.
View
74 src/protobuffs_utils.erl
@@ -0,0 +1,74 @@
+-module(protobuffs_utils).
+
+-export([
+ collect_full_messages/1,
+ function_exports/1,
+ record_data/2,
+ default_values/1,
+ write_header/2
+]).
+
+write_header(Messages, Filename) ->
+ {ok, FileRef} = file:open(Filename, [write]),
+ lists:foreach(
+ fun({Name, Fields}) ->
+ OutFields = [string:to_lower(B) || {_A, _, _, B, _, _} <- lists:keysort(1, Fields)],
+ JoinedFields = string:join(OutFields, ", "),
+ io:format(FileRef, "-record(~s, {~s}).~n", [string:to_lower(Name), JoinedFields])
+ end,
+ Messages
+ ),
+ ok == file:close(FileRef).
+
+collect_full_messages(Data) -> collect_full_messages(Data, []).
+
+%% @hidden
+collect_full_messages([], Acc) -> Acc;
+collect_full_messages([{message, Name, Fields} | Tail], Acc) ->
+ FieldsOut = lists:foldl(
+ fun (Input, TmpAcc) ->
+ case Input of
+ {_, _, _, _, _, _} -> [Input | TmpAcc];
+ _ -> TmpAcc
+ end
+ end,
+ [],
+ Fields
+ ),
+ SubMessages = lists:foldl(
+ fun ({message, C, D}, TmpAcc) -> [{message, C, D} | TmpAcc];
+ (_, TmpAcc) -> TmpAcc
+ end,
+ [],
+ Fields
+ ),
+ collect_full_messages(Tail ++ SubMessages, [{Name, FieldsOut} | Acc]).
+
+function_exports(Messages) -> function_exports(Messages, []).
+
+function_exports([{Name,_}|Tail], Acc) ->
+ E = "encode_" ++ string:to_lower(Name) ++ "/1",
+ D = "decode_" ++ string:to_lower(Name) ++ "/1",
+ function_exports(Tail, [E,D|Acc]);
+function_exports([], Acc) -> string:join(Acc, ",").
+
+record_data(Name, Fields) -> record_data(Name, lists:keysort(1, Fields), []).
+
+record_data(Name, [{Position, Rule, FieldType, FieldName, _, Default}|Tail], Acc) ->
+ Data = lists:flatten(io_lib:format("{~p, ~p, Rec#~s.~s, ~p, ~p}", [Position, Rule, string:to_lower(Name), FieldName, list_to_atom(string:to_lower(FieldType)), Default])),
+ record_data(Name, Tail, [Data|Acc]);
+record_data(_, [], Acc) -> string:join(lists:reverse(Acc), ",").
+
+default_values(Fields) ->
+ AllElements = lists:foldl(
+ fun(Field, Acc) ->
+ case Field of
+ {_, _, _, _, _, none} -> Acc;
+ {Position, _, _, _, _, Default} ->
+ [io_lib:format("{~p, ~p}", [Position, Default]) | Acc]
+ end
+ end,
+ [],
+ lists:keysort(1, Fields)
+ ),
+ string:join(lists:reverse(AllElements), ", ").
View
2  support/include.mk
@@ -36,7 +36,7 @@ $(EBIN_DIR)/%.$(EMULATOR): %.erl
$(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $<
$(EBIN_DIR)/%.$(EMULATOR): %.et
- $(ERL) -noshell -pa ../../elib/erltl/ebin/ -eval "erltl:compile(atom_to_list('$<'), [{outdir, \"../ebin\"}, report_errors, report_warnings, nowarn_unused_vars])." -s init stop
+ $(ERL) -noshell -eval "erltl:compile($<, [{outdir, \"../ebin\"}, report_errors, report_warnings, nowarn_unused_vars])." -s init stop
./%.$(EMULATOR): %.erl
$(ERLC) $(ERLC_FLAGS) -o . $<
View
2  t/Makefile
@@ -3,7 +3,7 @@ include ../support/include.mk
all: $(EBIN_FILES)
clean:
- rm -rf $(EBIN_FILES) erl_crash.dump simple_pb.*
+ rm -rf $(EBIN_FILES) erl_crash.dump
test: $(MODULES)
View
7 t/protobuffs_t_001.t
@@ -3,13 +3,14 @@
%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell
main(_) ->
- etap:plan(8),
+ etap:plan(9),
etap_can:loaded_ok(protobuffs, "module 'protobuffs' loaded"),
etap_can:can_ok(protobuffs, encode),
etap_can:can_ok(protobuffs, encode, 3),
etap_can:can_ok(protobuffs, decode),
etap_can:can_ok(protobuffs, decode, 2),
etap_can:loaded_ok(protobuffs_compile, "module 'protobuffs_compile' loaded"),
- etap_can:can_ok(protobuffs_compile, scan_file),
- etap_can:can_ok(protobuffs_compile, scan_file, 1),
+ etap_can:can_ok(protobuffs, generate, 1),
+ etap_can:can_ok(protobuffs_compile, render),
+ etap_can:can_ok(protobuffs_compile, render, 1),
etap:end_tests().
View
2  t/protobuffs_t_005.t
@@ -6,7 +6,7 @@
main(_) ->
etap:plan(1),
- etap:is(protobuffs_compile:scan_file("t/simple.proto"), ok, "simple.proto compiled"),
+ etap:is(protobuffs:generate("t/simple.proto"), ok, "simple.proto compiled"),
compile:file("simple_pb.erl", [{outdir,"./ebin"}]),
Data = [{1, <<"Nick">>, string}, {2, <<"Mountain View">>, string}, {3, <<"+1 (000) 555-1234">>, string}, {4, 25, int32}],
BinData = erlang:iolist_to_binary([protobuffs:encode(Pos, Value, Type) || {Pos, Value, Type} <- Data]),
View
2  t/protobuffs_t_006.t
@@ -7,7 +7,7 @@
main(_) ->
etap:plan(1),
- etap:is(protobuffs_compile:scan_file("t/simple.proto"), ok, "simple.proto compiled"),
+ etap:is(protobuffs:generate("t/simple.proto"), ok, "simple.proto compiled"),
compile:file("simple_pb.erl", [{outdir,"./ebin"}]),
Region = "California",
View
2  t/protobuffs_t_007.t
@@ -7,7 +7,7 @@
main(_) ->
etap:plan(1),
- etap:is(protobuffs_compile:scan_file("t/repeater.proto"), ok, "repeater.proto compiled"),
+ etap:is(protobuffs:generate("t/repeater.proto"), ok, "repeater.proto compiled"),
compile:file("repeater_pb.erl", [{outdir,"./ebin"}]),
Location1 = #locationa{region = "Lyon", country = "France"},
View
2  t/protobuffs_t_008.t
@@ -7,7 +7,7 @@
main(_) ->
etap:plan(2),
- etap:is(protobuffs_compile:scan_file("t/hasdefault.proto"), ok, "hasdefault.proto created"),
+ etap:is(protobuffs:generate("t/hasdefault.proto"), ok, "hasdefault.proto created"),
etap:is(compile:file("hasdefault_pb.erl", [{outdir,"./ebin"}]), {ok, hasdefault_pb}, "hasdefault_pb.erl compiled"),
Region = <<"California">>,
Please sign in to comment.
Something went wrong with that request. Please try again.