如何从生成的源代码中解析 Simulink Coder 系统层次结构
在 Simulink Coder 生成的源代码中,主 .h 文件中有一个包含系统层次结构的注释。为了理解文件中其他注释的含义(如 Referenced by: '<S291>/index4'),此注释是必需的。
系统层次结构注释如下所示:
parse_hierarchy.cpp
/**
* [...]
*
* Here is the system hierarchy for this model
* [...]
* '<S27>' : 'MySimulinkModel/MyImportantSubsystem'
* [...]
*/每个子系统一行。
在我们之前的文章如何使用 clang 从源代码中解析 C++ 注释中,我们展示了如何使用 clang 解析 C++ 注释。我们可以使用相同的方法来解析系统层次结构注释。
解析系统层次结构注释
以下代码使用 clang 注释解析器(见上文链接)和基于正则表达式的方法从注释中提取系统层次结构。
parse_hierarchy.cpp
#include <clang-c/Index.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <functional>
#include <regex>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/filewritestream.h>
#include <rapidjson/prettywriter.h>
void extractCommentBlocks(CXTranslationUnit unit, std::function<void(const std::string&, unsigned, unsigned)> processor) {
CXSourceRange range = clang_getCursorExtent(clang_getTranslationUnitCursor(unit));
CXToken *tokens;
unsigned numTokens;
clang_tokenize(unit, range, &tokens, &numTokens);
for (unsigned i = 0; i < numTokens; ++i) {
CXTokenKind tokenKind = clang_getTokenKind(tokens[i]);
if (tokenKind == CXToken_Comment) {
CXString tokenSpelling = clang_getTokenSpelling(unit, tokens[i]);
CXSourceLocation location = clang_getTokenLocation(unit, tokens[i]);
unsigned line, column;
CXFile file;
clang_getFileLocation(location, &file, &line, &column, nullptr);
std::string comment = clang_getCString(tokenSpelling);
processor(comment, line, column);
clang_disposeString(tokenSpelling);
}
}
clang_disposeTokens(unit, tokens, numTokens);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <source_file> <output_json_file>" << std::endl;
return 1;
}
CXIndex index = clang_createIndex(0, 0);
const char *filename = argv[1];
const char *outputFile = argv[2];
CXTranslationUnit unit = clang_parseTranslationUnit(index, filename, nullptr, 0, nullptr, 0, CXTranslationUnit_None);
if (unit == nullptr) {
std::cerr << "Failed to parse translation unit: " << filename << std::endl;
return 1;
}
CXCursor cursor = clang_getTranslationUnitCursor(unit);
std::unordered_map<std::string, std::string> hierarchyMap;
std::regex pattern(R"('\<([S]\d+)\>' : '([^']+)')");
extractCommentBlocks(unit, [&hierarchyMap, &pattern](const std::string& comment, unsigned line, unsigned column) {
if(comment.find("Here is the system hierarchy for this model") != std::string::npos) {
std::cout << "Found system hierarchy comment." << std::endl;
std::cout << "Comment at line " << line << ", column " << column << ": "
<< comment << std::endl;
// Parse each line in the comment using regex
std::istringstream stream(comment);
std::string line_text;
while (std::getline(stream, line_text)) {
std::smatch matches;
if (std::regex_search(line_text, matches, pattern)) {
std::string sNumber = matches[1].str();
std::string path = matches[2].str();
hierarchyMap[sNumber] = path;
}
}
}
});
// Print all entries in the map
std::cout << "\nExtracted hierarchy entries:" << std::endl;
for (const auto& entry : hierarchyMap) {
std::cout << entry.first << " -> " << entry.second << std::endl;
}
// Generate JSON output using RapidJSON
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
rapidjson::Value hierarchyObject(rapidjson::kObjectType);
for (const auto& entry : hierarchyMap) {
rapidjson::Value key(entry.first.c_str(), allocator);
rapidjson::Value value(entry.second.c_str(), allocator);
hierarchyObject.AddMember(key, value, allocator);
}
document.AddMember("hierarchy", hierarchyObject, allocator);
// Write to file
FILE* fp = fopen(outputFile, "wb");
if (!fp) {
std::cerr << "Failed to open output file: " << outputFile << std::endl;
clang_disposeTranslationUnit(unit);
clang_disposeIndex(index);
return 1;
}
char writeBuffer[65536];
rapidjson::FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));
rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(os);
document.Accept(writer);
fclose(fp);
std::cout << "JSON output written to: " << outputFile << std::endl;
clang_disposeTranslationUnit(unit);
clang_disposeIndex(index);
return 0;
}使用以下命令编译
build_parse_hierarchy.sh
g++ test.cpp -o parse_hierarchy -std=c++17 -I/usr/lib/llvm-19/include -L/usr/lib/llvm-19/lib -lclang使用源文件和输出 JSON 文件作为参数运行程序:
run_parse_hierarchy.sh
./parse_hierarchy my_model.h output.json之后,output.json 文件将以 JSON 格式包含系统层次结构:
output.json
{
"hierarchy": {
"<S27>": "MySimulinkModel/MyImportantSubsystem",
...
}
}Check out similar posts by category:
Matlab/Simulink
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow