Comment analyser la hiérarchie système Simulink Coder à partir du code source généré
Dans le code source généré par Simulink Coder, il y a un commentaire dans le fichier principal .h qui contient la hiérarchie système. Ce commentaire est nécessaire pour savoir ce que signifient les autres commentaires dans le fichier, tels que Referenced by: '<S291>/index4'.
Le commentaire de hiérarchie système ressemble à ceci :
/**
* [...]
*
* Here is the system hierarchy for this model
* [...]
* '<S27>' : 'MySimulinkModel/MyImportantSubsystem'
* [...]
*/avec une ligne par sous-système.
Dans notre post précédent Comment analyser les commentaires C++ du code source en utilisant clang nous avons montré comment analyser les commentaires C++ en utilisant clang. Nous pouvons utiliser la même approche pour analyser le commentaire de hiérarchie système.
Analyse du commentaire de hiérarchie système
Le code suivant utilise l’analyseur de commentaires clang (voir lien ci-dessus) et une approche basée sur les regex pour extraire la hiérarchie système du commentaire.
#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;
// Analyser chaque ligne du commentaire avec 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;
}
}
}
});
// Afficher toutes les entrées de la map
std::cout << "\nExtracted hierarchy entries:" << std::endl;
for (const auto& entry : hierarchyMap) {
std::cout << entry.first << " -> " << entry.second << std::endl;
}
// Générer la sortie JSON en utilisant 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);
// Écrire dans le fichier
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;
}Compilez avec
g++ test.cpp -o parse_hierarchy -std=c++17 -I/usr/lib/llvm-19/include -L/usr/lib/llvm-19/lib -lclangExécutez le programme avec le fichier source et le fichier JSON de sortie comme arguments :
./parse_hierarchy my_model.h output.jsonAprès cela, le fichier output.json contiendra la hiérarchie système au format JSON :
{
"hierarchy": {
"<S27>": "MySimulinkModel/MyImportantSubsystem",
...
}
}