您需要一個代碼生成系統,將數據包規範編譯爲代碼以解析/解析數據包。
您可以使用解析器生成器構建一個ad hoc,並編寫專用代碼以程序化地遍歷解析樹並吐出相關代碼。
或者您可以使用program transformation system (PTS),它將您的數據包規範視爲源代碼,並轉換爲目標語言的源代碼。您向PTS解釋數據包的語法與解析器生成器的語法解釋非常相似。
但是對於PTS,您可以使用表面語法表示法編寫轉換規則,以識別包系統語法並將其映射到目標語言函數語法。這使得編寫和維護這樣一個工具變得更加容易,尤其是在數據包語法發生變化的情況下,和/或您更改目標語言基礎結構以不同方式解析數據包。
編輯10/3:OP要求一個具體的例子,可能與PTS。
我將展示DMS Software Reengineering Toolkit的外觀(有關DMS的更多信息,請參閱bio)。
首先,您需要一個(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上運行,但爲了便於閱讀,使用這個和的表面語法來表示。
以下是DMS的源代碼重寫規則的來源;他們看起來像是對文本進行操作,但實際上他們使用DMS的解析器生成的AST。 DMS有它自己的語法規則,但基本上遵循上面的典型風格:
rule rule_name(pattern_variables):
source_syntax_category -> target_syntax_category =
" this_pattern " -> " that_pattern " ;
源和目標模式是封閉在*邁達克「」;因此,實際的文字引號字符轉義爲\」。
對於DMS規則這總是分組符號的片段,並那始終是我們選擇的目標語言(C)的片段。模式規則頭部中的變量名稱是語法類型,只能匹配AST中的相應類型,在metaquotes中找到的模式變量被寫爲\ 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\");
}
Downvoter:我不認爲你會禮貌地解釋你的理由。答案是事實和有用的。 –
基於問題中的示例結構,您能否給出一個具體的例子? – vohtaski