`
wqtn22
  • 浏览: 101575 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

erlang抽象码与basho的protobuf(三)代码生成原理之语义分析

 
阅读更多

上文介绍了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项目中使用protobuf例子”指的是在Erlang编程环境中使用Protocol Buffers(protobuf)这一数据序列化工具。protobuf是由Google开发的一种高效、跨语言的数据表示和序列化格式,它允许开发者定义...

    erlang版本的protobuf(erl_protobuffs)

    2. **生成Erlang代码**:使用protobuf编译器(protoc)和对应的Erlang插件,将`.proto`文件转换为Erlang模块。这些模块提供了编码和解码函数。 3. **编码与解码**:在Erlang程序中,你可以使用生成的模块来编码和...

    改进erlang版的protobuf代码

    标题中的“改进erlang版的protobuf代码”指的是在Erlang编程语言中对Protocol Buffers(protobuf)进行了优化和改进的代码实现。Protocol Buffers是一种数据序列化协议,由Google开发,它允许开发者定义数据结构,...

    enif_protobuf:使用enif(Erlang nif)的Google Protobuf实现

    这些生成的文件随后可以与enif_protobuf库一起使用,通过Erlang接口进行序列化和反序列化操作。 在`enif_protobuf-master`这个压缩包中,可能包含了以下内容: - `src/` 目录:包含enif_protobuf的源代码,包括...

    erlang_protobuffs

    Erlang Protobuffs是一个用于Erlang的协议缓冲区(Protocol Buffers)实现,它允许Erlang程序与使用Google Protobuf编译器生成的消息格式进行交互。Google Protobuf是一种高效的数据序列化协议,广泛应用于跨语言的...

    远古封神+英雄远征的ERLANG游戏服务器代码

    《远古封神》与《英雄远征》是两款受欢迎的网络游戏,它们的后端服务器采用了ERLANG这一编程语言来构建。ERLANG是一种为并发、分布式和容错系统设计的函数式编程语言,因其在实时系统和大规模并发处理中的优秀性能而...

    Erlang深度分析

    Erlang深度分析所包含的内容非常广泛,涵盖了从Erlang虚拟机的工作原理、性能优化、最佳编程实践,到最新的Erlang版本中新增特性的介绍。Erlang语言因其独特的并发模型、高效的分布式计算能力和灵活的网络编程接口,...

    erlang代码热替换与应用部署

    1. **准备新版本**:编写并编译新的Erlang模块代码,生成`.beam`文件。 2. **加载新代码**:使用`code:load_file/1`函数加载新版本的模块到代码服务器,但此时旧代码仍然在运行。 3. **检查类型签名**:Erlang会对比...

    一个我自己学习Erlang的聊天室服务器及客户端代码

    本项目提供了一个使用Erlang编写的聊天室服务器端代码以及Java编写的客户端代码,这为我们深入理解Erlang的并发特性和Java与Erlang的交互提供了实践案例。 一、Erlang聊天室服务器端 1. 并发处理:Erlang的轻量级...

    erlang-protobuffs

    其中一个原因是,项目组自己写了个打包和解包的工具,而且代码也简单,可以很方便的扩展到自动生成xml之类的配置文件,已经能很好的符合项目的需要。但是最近发现protobuf有个很不错的功能,就是可以向已有的协议中...

    erlang深度分析.pdf

    BEAM是Erlang的字节码解释器,模拟器可以用于开发阶段模拟运行和测试Erlang代码,有助于调试程序和优化性能。 #### 7. 内存管理 Erlang的内存管理是非常高效的,它使用了一种特殊的垃圾回收机制来确保低延迟。了解...

    erlang otp in action 书上的 代码

    erlang otp in action 代码

    erlang高级原理和应用PPT

    7. **标准库与第三方库**:Erlang的标准库介绍,如ETS(Erlang Term Storage)和Mnesia数据库,以及常用的第三方库如 Cowboy(Web服务器)和Rebar3(构建工具)。 8. **实战案例**:可能包含一些实际项目或案例研究...

    erlang并发编程实战源代码

    erlang并发编程实战源代码erlang并发编程实战源代码

    Erlang应用部署与热代码替换--理解2

    本篇将深入探讨Erlang应用的部署与热代码替换。 一、Erlang应用部署 在Erlang环境中,应用通常被打包成一个`.app`文件,包含应用元数据,以及一个或多个beam文件(编译后的Erlang代码)。部署Erlang应用的步骤如下...

    forms:一个简化使用Erlang抽象格式的库

    read/1与Erlang源文件(即带有.erl后缀的文件)和Erlang二进制文件(即带有.beam后缀的文件)一起使用。 下面的行将从Erlang的内部lists模块中读取表单。 forms : read ( lists ). 同样,以下行将从开发人员提供...

    erlang 深度分析

    ### Erlang深度分析知识点概述 #### 1. Erlang虚拟机(VM)分析 - **概念**: Erlang VM,也称为BEAM (Bytecode for the Erlang Abstract Machine),是Erlang语言的基础运行环境。 - **特性**: - **轻量级进程**: ...

    英雄远征源码[erlang]

    《英雄远征》是一款基于Erlang编程语言开发的网络游戏,其源码的公开为开发者提供了深入了解游戏服务端架构和Erlang在大型分布式系统应用的机会。Erlang是一种为并发、容错和实时系统设计的函数式编程语言,以其在高...

    erlang深度分析中文版(业余研究)

    12. **Erlang模拟器工作原理分析**:深入理解BEAM模拟器的工作原理有助于编写更高效、更稳定的Erlang代码,书中对此进行了详细的理论和实践分析。 通过以上内容,读者可以全面了解Erlang的高级特性和实战技巧,无论...

    Go/Python/Erlang编程语言对比分析及示例代码

    本文将对这三种语言进行对比分析,并给出一些示例代码。 Go语言的设计核心思想在于简洁、高效和并发。它继承了C语言的语法风格,Pascal的面向对象设计,以及CSP的并发模型。Go语言舍弃了一些传统面向对象编程中的...

Global site tag (gtag.js) - Google Analytics