【问题标题】:auto generate code based on a packet structure根据数据包结构自动生成代码
【发布时间】:2016-10-03 17:49:39
【问题描述】:

我们有不同结构的数据包。它们应该以不同的语言读/写。示例:

| ClassId | Data |

ClassId = "datapoint" (Data structure):
  temperature - 1bytes
  elevation - 2bytes
  gradient - 1bytes
ClassId = "config" (Data structure):
  frequency - 1bytes
  deviceId - 3bytes
ClassId = "accelerometer" (Data structure):
  time - 2bytes
  x - 2bytes
  y - 2bytes
  z - 2bytes

我希望有一个配置文件,然后代码(python/c/etc.)是自动生成,可以读写数据包。大致如下:

lib.set(packet, "datapoint", {
  elevation: 933,
  temperature: 18,
  gradient: 20
});
lib.get(packet, "datapoint");
=>
{
  elevation: 933,
  temperature: 18,
  gradient: 20
}

谷歌搜索并没有把我带到任何地方。任何指针都会非常有帮助。

【问题讨论】:

    标签: embedded config code-generation packet


    【解决方案1】:

    您需要一个代码生成系统,将数据包规范编译成代码来解析/解解析数据包。

    您可以使用解析器生成器构建一个 ad hoc,并编写 ad hoc 代码以程序方式遍历解析树并输出相关代码。

    或者您可以使用program transformation system (PTS),它将您的数据包规范视为源代码,并转换为目标语言的源代码。您向 PTS 解释数据包的语法与向解析器生成器解释语法的方式几乎相同。

    但是使用 PTS,您可以用表面语法符号编写转换规则,该符号可识别数据包系统语法并将其映射到目标语言函数语法。这使得编写和维护这样的工具变得更加容易,尤其是在数据包语法发生变化和/或您更改目标语言基础结构以以不同方式解析数据包的情况下。

    EDIT 10/3:OP 要求一个具体的例子,大概是一个 PTS。

    我将展示我们的 DMS 软件再造工具包的外观(有关 DMS 的更多信息,请参阅简介)。

    首先,您需要包语言的(DMS 兼容)语法。根据我所看到的,这很简单:

    Packets = Packet ;
    Packets = Packets Packet ; -- allow a list of packet defintions
    
    Packet = 'ClassID' '=' STRING members ;
    
     members = ;
     members = members member ; -- allow list of members
    
     member = IDENTIFIER '-' NATURAL 'bytes' ;
    

    我认为这种语法很幼稚,因为实际上数据包成员可能有不同的类型(可能是字符串、浮点数、布尔值……); OP 的示例仅显示了我假设的是 N 字节二进制整数。您还需要各种目标语言的语法。我会假设你有这些语法(这是相当的假设);让我们暂时使用 C。 [DMS 确实有很多这样的]。

    我们还必须假设传输数据的表示。 OP提出了一些建议,但我认为他试图暗示生成的代码(“lib.set ...”)。 相反,我将假设数据包内容是从 Stream 中读取的,只是简单地附加在一起的二进制字节;这使得数据包大小尽可能小,从而缩短传输时间。

    所以,现在我们指定我们的代码生成器,作为将数据包定义映射到代码的重写规则集。

    对于背景,PTS 的重写规则通常如下所示:

              if you see *this*, replace it by *that*
    

    因此,您实际上是在用另一种结构替换一种结构。这些通常在 AST 上运行,但为了便于阅读,使用 thisthat 的表面语法。

    以下是 DMS 的源到源重写规则;它们看起来像是对文本进行操作,但实际上它们对 DMS 解析器生成的 AST 进行操作。 DMS 有自己的规则语法,但它基本上遵循上述典型样式:

     rule rule_name( pattern_variables ): 
          source_syntax_category -> target_syntax_category =
          " this_pattern "  ->  " that_pattern " ;
    

    源和目标模式用 *metaquotes" " 括起来;因此,实际的文字引号字符被转义为 \".

    对于 DMS 规则,this 始终是 Packet 表示法的片段,that 始终是我们选择的目标语言 (C) 的片段。规则头中的模式变量名称被赋予语法类型,并且只能匹配 AST 中的相应类型。在元引用中找到的模式变量写为 \variable。元函数可以计算派生结果;它们在模式中被调用为“\function(args)”。详情请见DMS Rewrite Rules

        source domain Packet; -- the little language we defined
        target domain C; -- what we will generate code for
                -- you'll write one of these rulesets for each target language
    
        rule top_level(pl: Packets): Packets -> Statements =
          " \pl "
         -> " ReadPacketType(stream, packet_type);
              switch(packet_type) {
                  \pl
                 default: Panic(\"unrecognized packet type\");
              }"  if  IsRoot(pl); -- do this once [at root of tree]
    
    
        rule translate_packet_definitions(p: Packet, pl: packet_list): Packets -> switch_case_list
             " \p  \pl ";
    
        rule translate_packet_definition(s:STRING, ms: members, pl: Packets): Packets -> switch_case =
           " ClassID = \s \m \pl "
            -> " case \concatenate\(\"enum_\"\,\string_to_identifier\(\s\)\): { 
                    \string_to_identifier\(\s\)* p=malloc(sizeof(\string_to_identifier\(\s\)));
                    \m
                    return p;
                 }
                ";
    
         rule translate_members(m: member, ms: members) : members -> Statements
            = " \m \ms ";
    
         rule translate_member(i: IDENTIFIER, n: NATURAL) = member -> StatementList =
              " \i - \n bytes " ->
              "  p-> \toCIdentifer\(\i\) = ReadNByteValue(stream,\toCNatural\(\n\)) ; "
    

    这并不完整(特别是,我需要另一组规则来生成一组数据包类型的枚举声明),我怀疑它是否完全正确,但它给出了规则的味道。使用这些规则,OP 的示例输入将生成以下 C 代码:

    ReadPacketType(stream, packet_type);
    switch(packet_type) {
        case enum_datapoint: {
           datapoint* p=malloc(sizeof(datapoint));
           p->temperature=ReadNByteValue(stream,1);
           p->elevation=ReadNByteValue(stream,2);
           p->gradient=ReadNByteValue(stream,2);
           return p;
        }
        case enum_config: {
           config* p=malloc(sizeof(config));
           p->frequency=ReadNByteValue(stream,1);
           p->deviceId=ReadNByteValue(stream,3);
           return p;
        }
        case enum_accelerometer: {
           accelerometer* p=malloc(sizeof(accelerometer)); 
           p-time>=ReadNByteValue(stream,2);
           p->x=ReadNByteValue(stream,2);
           p->y=ReadNByteValue(stream,2);
           p->z=ReadNByteValue(stream,2);
           return p;
         }
         default: Panic(\"unrecognized packet type\");
    } 
    

    【讨论】:

    • 投反对票的人:我认为你没有礼貌地解释你的理由。答案是事实和有用的。
    • 能否根据问题中的示例结构给出一个具体的例子?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    • 2011-01-08
    • 2015-08-28
    • 1970-01-01
    相关资源
    最近更新 更多