@@ -26,6 +26,7 @@ namespace clang {
2626namespace dpct {
2727std::vector<clang::tooling::UnifiedPath> MetaRuleObject::RuleFiles;
2828std::vector<std::shared_ptr<MetaRuleObject>> MetaRules;
29+ llvm::DenseSet<llvm::StringRef> ProcessedYamlFiles;
2930
3031OutputBuilder::~OutputBuilder () {}
3132
@@ -344,17 +345,147 @@ MetaRuleObject::PatternRewriter::PatternRewriter(
344345 Subrules = S;
345346}
346347
348+ // Read a YAML file recursively and substitute any "!include <filename>"
349+ // directive with the contents of the referenced file.
350+ std::unique_ptr<llvm::MemoryBuffer>
351+ readYAMLFile (const llvm::StringRef &RuleFilePath) {
352+ // Check if the rule file has already been processed
353+ // to avoid infinite recursion
354+ if (!ProcessedYamlFiles.insert (RuleFilePath).second ) {
355+ return llvm::MemoryBuffer::getMemBufferCopy (" " );
356+ }
357+
358+ // Load the rule file into a MemoryBuffer
359+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
360+ llvm::MemoryBuffer::getFile (RuleFilePath);
361+ if (!Buffer) {
362+ llvm::errs () << " Error: failed to read " << RuleFilePath << " : "
363+ << Buffer.getError ().message () << " \n " ;
364+ clang::dpct::ShowStatus (MigrationErrorInvalidRuleFilePath);
365+ dpctExit (MigrationErrorInvalidRuleFilePath);
366+ }
367+
368+ // Get the directory path of the rule file.
369+ llvm::SmallString<128 > DirectoryPath (RuleFilePath);
370+ llvm::sys::path::remove_filename (DirectoryPath);
371+
372+ // Iterate over the input line by line.
373+ std::stringstream Output, IncRuleFilePath;
374+ const std::string IncDirective = " !include" ;
375+
376+ size_t Idx = 0 ;
377+ bool IncDirectiveFound = false ;
378+ bool SkipWhiteSpaces = false ;
379+ bool IsLineBeginning = true ;
380+
381+ llvm::StringRef BuffContent = std::move (*Buffer)->getBuffer ();
382+ const auto BuffSize = BuffContent.size ();
383+ while (Idx < BuffSize) {
384+ unsigned char Ch = BuffContent[Idx];
385+
386+ // Skip white spaces at the beginning of the line if it contains a directive
387+ if (IsLineBeginning) {
388+ auto i = Idx;
389+ auto c = Ch;
390+
391+ // lookahead for "!" directive after white spaces
392+ for (; i < BuffContent.size (); i++) {
393+ c = BuffContent[i];
394+
395+ // Stop at new line or first non-white space character
396+ if (c == ' \n ' || !std::isspace (c)) {
397+ break ;
398+ }
399+ }
400+
401+ // Check if the line starts with a directive
402+ if (c == ' !' ) {
403+ // Move Idx to the beginning of directive
404+ Idx = i;
405+
406+ // Check if the directive is "!include"
407+ if (!IncDirectiveFound && Idx + IncDirective.length () <= BuffSize &&
408+ BuffContent.substr (Idx, IncDirective.length ()) == IncDirective) {
409+ // Move Idx to the end of directive
410+ Idx += IncDirective.length ();
411+ IncDirectiveFound = true ;
412+ SkipWhiteSpaces = true ;
413+ }
414+
415+ // Update current character
416+ Ch = BuffContent[Idx];
417+ }
418+
419+ IsLineBeginning = false ;
420+ }
421+
422+ // Skip return carriage character
423+ if (Ch == ' \r ' ) {
424+ Idx++;
425+ continue ;
426+ }
427+
428+ // Process IncRuleFilePath at end of the line
429+ if (Ch == ' \n ' ) {
430+ if (IncDirectiveFound) {
431+ auto IncRuleFilePathStr = IncRuleFilePath.str ();
432+
433+ if (!IncRuleFilePathStr.empty ()) {
434+ // Find the absolute path for the included rule file path
435+ llvm::SmallString<128 > IncRuleFileAbsPath = DirectoryPath;
436+ llvm::sys::path::append (IncRuleFileAbsPath, IncRuleFilePathStr);
437+
438+ // Recursively process the included file
439+ if (llvm::sys::fs::exists (IncRuleFileAbsPath)) {
440+ Output << readYAMLFile (IncRuleFileAbsPath.str ())->getBuffer ().str ();
441+ } else {
442+ Output << readYAMLFile (IncRuleFilePathStr)->getBuffer ().str ();
443+ }
444+
445+ // Clear the contents of include rule file path
446+ IncRuleFilePath.str (" " );
447+ }
448+ }
449+
450+ // Reset include directive info for each new line
451+ IncDirectiveFound = false ;
452+ SkipWhiteSpaces = false ;
453+ IsLineBeginning = true ;
454+ }
455+
456+ if (IncDirectiveFound) {
457+ // Skip adding quotes to the include rule file path
458+ if (Ch == ' "' || Ch == ' \' ' ) {
459+ Idx++;
460+ // Flip white space skip flag at the boundaries of quotes
461+ SkipWhiteSpaces = !SkipWhiteSpaces;
462+ continue ;
463+ }
464+
465+ // Skip white space characters
466+ if (SkipWhiteSpaces && std::isspace (Ch)) {
467+ Idx++;
468+ continue ;
469+ }
470+
471+ // Append the character to the include rule file path for !include line
472+ IncRuleFilePath << Ch;
473+ } else {
474+ // Append the character to the output buffer
475+ Output << Ch;
476+ }
477+
478+ Idx++;
479+ }
480+
481+ return llvm::MemoryBuffer::getMemBufferCopy (Output.str (), RuleFilePath);
482+ }
483+
347484void importRules (std::vector<clang::tooling::UnifiedPath> &RuleFiles) {
348485 for (auto &RuleFile : RuleFiles) {
349486 // open the yaml file
350487 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
351- llvm::MemoryBuffer::getFile (RuleFile.getCanonicalPath ());
352- if (!Buffer) {
353- llvm::errs () << " Error: failed to read " << RuleFile << " : "
354- << Buffer.getError ().message () << " \n " ;
355- clang::dpct::ShowStatus (MigrationErrorInvalidRuleFilePath);
356- dpctExit (MigrationErrorInvalidRuleFilePath);
357- }
488+ readYAMLFile (RuleFile.getCanonicalPath ());
358489
359490 // load rules
360491 std::vector<std::shared_ptr<MetaRuleObject>> CurrentRules;
0 commit comments