上文介绍了protobuffs的词法与语法分析过程,发现其收集了目标proto文件及其import文件的语法树,此处继续观察语义分析过程。
语法树的格式如下:
[Message1 = {message,MessageName,[FieldRecord1,FieldRecord2,...,FieldRecordn]},Message2,...,Messagen],
FieldRecord = {FieldID,required/optional/repeated/repeated_packed,FieldType,FieldName,DefaultValue}。
protobuffs_compile.erl
generate_source(ProtoFile,Options) when is_list (ProtoFile) ->
Basename = filename:basename(ProtoFile, ".proto") ++ "_pb",
{ok,FirstParsed} = parse(ProtoFile),
ImportPaths = ["./", "src/" | proplists:get_value(imports_dir, Options, [])],
Parsed = parse_imports(FirstParsed, ImportPaths),
Collected = collect_full_messages(Parsed),
Messages = resolve_types(Collected#collected.msg,Collected#collected.enum),
output_source (Basename, Messages, Collected#collected.enum, Options).
语义分析需要收集所有message、enum、extends定义,收集到所有的message域定义,enum定义,并根据extends定义对message进行扩展。
collect_full_messages(Data) -> collect_full_messages(Data, #collected{}).
collect_full_messages([{message, Name, Fields} | Tail], Collected) ->
ListName = case erlang:is_list (hd(Name)) of
true -> Name;
false -> [Name]
end,
FieldsOut = lists:foldl(
fun ({_,_,_,_,_} = Input, TmpAcc) -> [Input | TmpAcc];
(_, TmpAcc) -> TmpAcc
end, [], Fields),
Enums = lists:foldl(
fun ({enum,C,D}, TmpAcc) -> [{enum, [C | ListName], D} | TmpAcc];
(_, TmpAcc) -> TmpAcc
end, [], Fields),
Extensions = lists:foldl(
fun ({extensions, From, To}, TmpAcc) -> [{From,To}|TmpAcc];
(_, TmpAcc) -> TmpAcc
end, [], Fields),
SubMessages = lists:foldl(
fun ({message, C, D}, TmpAcc) -> [{message, [C | ListName], D} | TmpAcc];
(_, TmpAcc) -> TmpAcc
end, [], Fields),
NewCollected = Collected#collected{
msg=[{ListName, FieldsOut} | Collected#collected.msg],
extensions=[{ListName,Extensions} | Collected#collected.extensions]
},
collect_full_messages(Tail ++ SubMessages ++ Enums, NewCollected);
...
仍旧分析主角message的处理过程:
可以看到对message的处理主要包括取出一个message中的所有域信息、枚举定义、扩展定义、嵌套message,并将枚举定义和嵌套message合并到上级语法树中继续处理,而将域信息和扩展定义收集到一个collected结构中。
collect_full_messages([{enum, Name, Fields} | Tail], Collected) ->
ListName = case erlang:is_list (hd(Name)) of
true -> Name;
false -> [Name]
end,
FieldsOut = lists:foldl(
fun (Field, TmpAcc) ->
case Field of
{EnumAtom, IntValue} -> [{enum, type_path_to_type(ListName),
IntValue, EnumAtom} | TmpAcc];
_ -> TmpAcc
end
end, [], Fields),
NewCollected = Collected#collected{enum=FieldsOut++Collected#collected.enum},
collect_full_messages(Tail, NewCollected);
type_path_to_type (TypePath) ->
string:join (lists:reverse (TypePath), "_").
...
对enum的处理过程:
enum的处理比较简单,仅仅是提取所有的enum定义,并为其生成一条记录{enum,EnumName,EnumValue,EnumAtom},将枚举定义继续收集到collected结构中。
collect_full_messages([{extend, Name, ExtendedFields} | Tail], Collected) ->
ListName = case erlang:is_list (hd(Name)) of
true -> Name;
false -> [Name]
end,
CollectedMsg = Collected#collected.msg,
{ListName,FieldsOut} = lists:keyfind(ListName,1,CollectedMsg),
{ListName,Extensions} = lists:keyfind(ListName,1,Collected#collected.extensions),
FunNotInReservedRange = fun(Id) -> not(19000 =< Id andalso Id =< 19999) end,
FunInRange = fun(Id,From,max) -> From =< Id andalso Id =< 16#1fffffff;
(Id,From,To) -> From =< Id andalso Id =< To
end,
ExtendedFieldsOut = lists:append(FieldsOut,
lists:foldl(
fun ({Id, _, _, FieldName, _} = Input, TmpAcc) ->
case lists:any(fun({From,To}) -> FunNotInReservedRange(Id)
andalso FunInRange(Id,From,To)
end,Extensions) of
true ->
[Input | TmpAcc];
_ ->
error_logger:error_report(["Extended field not in valid range",
{message, Name},
{field_id,Id},
{field_name,FieldName},
{defined_ranges,Extensions},
{reserved_range,{19000,19999}},
{max,16#1fffffff}]),
throw(out_of_range)
end;
(_, TmpAcc) -> TmpAcc
end, [], ExtendedFields)
),
NewCollected = Collected#collected{msg=lists:keyreplace(ListName,1,CollectedMsg,{ListName,ExtendedFieldsOut})},
collect_full_messages(Tail, NewCollected);
对
extends的处理过程:
extends本质是一种依赖关系的体现,此处要检查extends是否满足message的extensions定义,同时也是对extends依赖的message是否存在的检查,然后将extends的域追加到原始message定义中,并替换collected中message的定义。
generate_source(ProtoFile,Options) when is_list (ProtoFile) ->
Basename = filename:basename(ProtoFile, ".proto") ++ "_pb",
{ok,FirstParsed} = parse(ProtoFile),
ImportPaths = ["./", "src/" | proplists:get_value(imports_dir, Options, [])],
Parsed = parse_imports(FirstParsed, ImportPaths),
Collected = collect_full_messages(Parsed),
Messages = resolve_types(Collected#collected.msg,Collected#collected.enum),
output_source (Basename, Messages, Collected#collected.enum, Options).
收集完了所有的message定义和enum定义,接着将进行所有message的类型解析了。
resolve_types (Data, Enums) -> resolve_types (Data, Data, Enums, []).
resolve_types ([{TypePath, Fields} | Tail], AllPaths, Enums, Acc) ->
FieldsOut = lists:foldl(
fun (Input, TmpAcc) ->
case Input of
{Index, Rules, Type, Identifier, Other} ->
case is_scalar_type (Type) of
true -> [Input | TmpAcc];
false ->
PossiblePaths =
case string:tokens (Type,".") of
[Type] ->
all_possible_type_paths (Type, TypePath);
FullPath ->
% handle types of the form Foo.Bar which are absolute,
% so we just convert to a type path and check it.
[lists:reverse (FullPath)]
end,
RealPath =
case find_type (PossiblePaths, AllPaths) of
false ->
case is_enum_type(Type, PossiblePaths, Enums) of
{true,EnumType} ->
EnumType;
false ->
throw (["Unknown Type ", Type])
end;
ResultType ->
ResultType
end,
[{Index, Rules, type_path_to_type (RealPath), Identifier, Other} | TmpAcc]
end;
_ -> TmpAcc
end
end, [], Fields),
resolve_types (Tail, AllPaths, Enums, [{type_path_to_type (TypePath), lists:reverse (FieldsOut) } | Acc]);
resolve_types ([], _, _, Acc) ->
Acc.
由于protocol buffers实质是一种对类型的描述,此处类型解析将是protobuffs的核心语义分析过程,对message的域进行类型检查和类型绑定,主要根据以下原则进行:
对于标量类型,也即protocol buffers的内建类型,无需过多检查;对于枚举类型和自定义类型,需要检查所依赖的类型,将其绑定到具体的枚举类型或自定义类型上。
至此,protobuffers的语义分析过程就结束了,这个过程比较简单,仅仅是类型收集、类型检查和类型绑定,此处已经收集到了类型的符号表:
[
Message1 = {MessageName,
[
Field1 = {FieldId,
FieldRule(required/optional/repeated/repeated_packed...),
FieldType(ScalarType, EnumType,OtherType),
FieldName,Other(DefaultValue)},
Field2,...,Fieldn]},
Message2,...Messagen,
Enum1 = {enum, EnumName, EnumValue, EnumAtom},
Enum2,...,Enumn
]。
未完待续...
分享到:
相关推荐
标题中的“在erlang项目中使用protobuf例子”指的是在Erlang编程环境中使用Protocol Buffers(protobuf)这一数据序列化工具。protobuf是由Google开发的一种高效、跨语言的数据表示和序列化格式,它允许开发者定义...
2. **生成Erlang代码**:使用protobuf编译器(protoc)和对应的Erlang插件,将`.proto`文件转换为Erlang模块。这些模块提供了编码和解码函数。 3. **编码与解码**:在Erlang程序中,你可以使用生成的模块来编码和...
标题中的“改进erlang版的protobuf代码”指的是在Erlang编程语言中对Protocol Buffers(protobuf)进行了优化和改进的代码实现。Protocol Buffers是一种数据序列化协议,由Google开发,它允许开发者定义数据结构,...
这些生成的文件随后可以与enif_protobuf库一起使用,通过Erlang接口进行序列化和反序列化操作。 在`enif_protobuf-master`这个压缩包中,可能包含了以下内容: - `src/` 目录:包含enif_protobuf的源代码,包括...
Erlang Protobuffs是一个用于Erlang的协议缓冲区(Protocol Buffers)实现,它允许Erlang程序与使用Google Protobuf编译器生成的消息格式进行交互。Google Protobuf是一种高效的数据序列化协议,广泛应用于跨语言的...
《远古封神》与《英雄远征》是两款受欢迎的网络游戏,它们的后端服务器采用了ERLANG这一编程语言来构建。ERLANG是一种为并发、分布式和容错系统设计的函数式编程语言,因其在实时系统和大规模并发处理中的优秀性能而...
Erlang深度分析所包含的内容非常广泛,涵盖了从Erlang虚拟机的工作原理、性能优化、最佳编程实践,到最新的Erlang版本中新增特性的介绍。Erlang语言因其独特的并发模型、高效的分布式计算能力和灵活的网络编程接口,...
1. **准备新版本**:编写并编译新的Erlang模块代码,生成`.beam`文件。 2. **加载新代码**:使用`code:load_file/1`函数加载新版本的模块到代码服务器,但此时旧代码仍然在运行。 3. **检查类型签名**:Erlang会对比...
本项目提供了一个使用Erlang编写的聊天室服务器端代码以及Java编写的客户端代码,这为我们深入理解Erlang的并发特性和Java与Erlang的交互提供了实践案例。 一、Erlang聊天室服务器端 1. 并发处理:Erlang的轻量级...
其中一个原因是,项目组自己写了个打包和解包的工具,而且代码也简单,可以很方便的扩展到自动生成xml之类的配置文件,已经能很好的符合项目的需要。但是最近发现protobuf有个很不错的功能,就是可以向已有的协议中...
BEAM是Erlang的字节码解释器,模拟器可以用于开发阶段模拟运行和测试Erlang代码,有助于调试程序和优化性能。 #### 7. 内存管理 Erlang的内存管理是非常高效的,它使用了一种特殊的垃圾回收机制来确保低延迟。了解...
erlang otp in action 代码
7. **标准库与第三方库**:Erlang的标准库介绍,如ETS(Erlang Term Storage)和Mnesia数据库,以及常用的第三方库如 Cowboy(Web服务器)和Rebar3(构建工具)。 8. **实战案例**:可能包含一些实际项目或案例研究...
erlang并发编程实战源代码erlang并发编程实战源代码
本篇将深入探讨Erlang应用的部署与热代码替换。 一、Erlang应用部署 在Erlang环境中,应用通常被打包成一个`.app`文件,包含应用元数据,以及一个或多个beam文件(编译后的Erlang代码)。部署Erlang应用的步骤如下...
read/1与Erlang源文件(即带有.erl后缀的文件)和Erlang二进制文件(即带有.beam后缀的文件)一起使用。 下面的行将从Erlang的内部lists模块中读取表单。 forms : read ( lists ). 同样,以下行将从开发人员提供...
### Erlang深度分析知识点概述 #### 1. Erlang虚拟机(VM)分析 - **概念**: Erlang VM,也称为BEAM (Bytecode for the Erlang Abstract Machine),是Erlang语言的基础运行环境。 - **特性**: - **轻量级进程**: ...
《英雄远征》是一款基于Erlang编程语言开发的网络游戏,其源码的公开为开发者提供了深入了解游戏服务端架构和Erlang在大型分布式系统应用的机会。Erlang是一种为并发、容错和实时系统设计的函数式编程语言,以其在高...
12. **Erlang模拟器工作原理分析**:深入理解BEAM模拟器的工作原理有助于编写更高效、更稳定的Erlang代码,书中对此进行了详细的理论和实践分析。 通过以上内容,读者可以全面了解Erlang的高级特性和实战技巧,无论...
本文将对这三种语言进行对比分析,并给出一些示例代码。 Go语言的设计核心思想在于简洁、高效和并发。它继承了C语言的语法风格,Pascal的面向对象设计,以及CSP的并发模型。Go语言舍弃了一些传统面向对象编程中的...