brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.0 KiB · 2df6881 Raw
299 lines · cpp
1//===-- lib/Parser/parsing.cpp --------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Parser/parsing.h"10#include "prescan.h"11#include "type-parsers.h"12#include "flang/Parser/message.h"13#include "flang/Parser/preprocessor.h"14#include "flang/Parser/provenance.h"15#include "flang/Parser/source.h"16#include "llvm/Support/raw_ostream.h"17 18namespace Fortran::parser {19 20Parsing::Parsing(AllCookedSources &allCooked) : allCooked_{allCooked} {}21Parsing::~Parsing() {}22 23const SourceFile *Parsing::Prescan(const std::string &path, Options options) {24  options_ = options;25  AllSources &allSources{allCooked_.allSources()};26  allSources.ClearSearchPath();27  if (options.isModuleFile) {28    for (const auto &path : options.searchDirectories) {29      allSources.AppendSearchPathDirectory(path);30    }31  }32 33  std::string buf;34  llvm::raw_string_ostream fileError{buf};35  const SourceFile *sourceFile{nullptr};36  if (path == "-") {37    sourceFile = allSources.ReadStandardInput(fileError);38  } else if (options.isModuleFile) {39    // Don't mess with intrinsic module search path40    sourceFile = allSources.Open(path, fileError);41  } else {42    sourceFile =43        allSources.Open(path, fileError, "."s /*prepend to search path*/);44  }45  if (!buf.empty()) {46    ProvenanceRange range{allSources.AddCompilerInsertion(path)};47    messages_.Say(range, "%s"_err_en_US, buf);48    return sourceFile;49  }50  CHECK(sourceFile);51 52  if (!options.isModuleFile) {53    // For .mod files we always want to look in the search directories.54    // For normal source files we don't add them until after the primary55    // source file has been opened.  If foo.f is missing from the current56    // working directory, we don't want to accidentally read another foo.f57    // from another directory that's on the search path.58    for (const auto &path : options.searchDirectories) {59      allSources.AppendSearchPathDirectory(path);60    }61  }62 63  if (!options.predefinitions.empty()) {64    preprocessor_.DefineStandardMacros();65    for (const auto &predef : options.predefinitions) {66      if (predef.second) {67        preprocessor_.Define(predef.first, *predef.second);68      } else {69        preprocessor_.Undefine(predef.first);70      }71    }72  }73  currentCooked_ = &allCooked_.NewCookedSource();74  Prescanner prescanner{75      messages_, *currentCooked_, preprocessor_, options.features};76  prescanner.set_fixedForm(options.isFixedForm)77      .set_fixedFormColumnLimit(options.fixedFormColumns)78      .set_preprocessingOnly(options.prescanAndReformat)79      .set_expandIncludeLines(!options.prescanAndReformat ||80          options.expandIncludeLinesInPreprocessedOutput)81      .AddCompilerDirectiveSentinel("dir$");82  bool noneOfTheAbove{!options.features.IsEnabled(LanguageFeature::OpenACC) &&83      !options.features.IsEnabled(LanguageFeature::OpenMP) &&84      !options.features.IsEnabled(LanguageFeature::CUDA)};85  if (options.features.IsEnabled(LanguageFeature::OpenACC) ||86      (options.prescanAndReformat && noneOfTheAbove)) {87    prescanner.AddCompilerDirectiveSentinel("$acc");88    prescanner.AddCompilerDirectiveSentinel("@acc");89  }90  if (options.features.IsEnabled(LanguageFeature::OpenMP) ||91      (options.prescanAndReformat && noneOfTheAbove)) {92    prescanner.AddCompilerDirectiveSentinel("$omp");93    prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line94  }95  if (options.features.IsEnabled(LanguageFeature::CUDA) ||96      (options.prescanAndReformat && noneOfTheAbove)) {97    prescanner.AddCompilerDirectiveSentinel("$cuf");98    prescanner.AddCompilerDirectiveSentinel("@cuf");99  }100  ProvenanceRange range{allSources.AddIncludedFile(101      *sourceFile, ProvenanceRange{}, options.isModuleFile)};102  prescanner.Prescan(range);103  if (currentCooked_->BufferedBytes() == 0 && !options.isModuleFile) {104    // Input is empty.  Append a newline so that any warning105    // message about nonstandard usage will have provenance.106    currentCooked_->Put('\n', range.start());107  }108  currentCooked_->Marshal(allCooked_);109  if (options.needProvenanceRangeToCharBlockMappings) {110    currentCooked_->CompileProvenanceRangeToOffsetMappings(allSources);111  }112  if (options.showColors) {113    allSources.setShowColors(/*showColors=*/true);114  }115  return sourceFile;116}117 118void Parsing::EmitPreprocessorMacros(llvm::raw_ostream &out) const {119  preprocessor_.PrintMacros(out);120}121 122void Parsing::EmitPreprocessedSource(123    llvm::raw_ostream &out, bool lineDirectives) const {124  const std::string *sourcePath{nullptr};125  int sourceLine{0};126  int column{1};127  bool inDirective{false};128  bool ompConditionalLine{false};129  bool inContinuation{false};130  bool lineWasBlankBefore{true};131  const AllSources &allSources{allCooked().allSources()};132  // All directives that flang supports are known to have a length of 4 chars,133  // except for OpenMP conditional compilation lines (!$).134  constexpr int directiveNameLength{4};135  // We need to know the current directive in order to provide correct136  // continuation for the directive137  std::string directive;138  for (const char &atChar : cooked().AsCharBlock()) {139    char ch{atChar};140    if (ch == '\n') {141      out << '\n'; // TODO: DOS CR-LF line ending if necessary142      column = 1;143      inDirective = false;144      ompConditionalLine = false;145      inContinuation = false;146      lineWasBlankBefore = true;147      ++sourceLine;148      directive.clear();149    } else {150      auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})};151 152      // Preserves original case of the character153      const auto getOriginalChar{[&](char ch) {154        if (IsLetter(ch) && provenance && provenance->size() == 1) {155          if (const char *orig{allSources.GetSource(*provenance)}) {156            char upper{ToUpperCaseLetter(ch)};157            if (*orig == upper) {158              return upper;159            }160          }161        }162        return ch;163      }};164 165      bool inDirectiveSentinel{false};166      if (ch == '!' && lineWasBlankBefore) {167        // Other comment markers (C, *, D) in original fixed form source168        // input card column 1 will have been deleted or normalized to !,169        // which signifies a comment (directive) in both source forms.170        inDirective = true;171        inDirectiveSentinel = true;172      } else if (inDirective && !ompConditionalLine &&173          directive.size() < directiveNameLength) {174        if (IsLetter(ch) || ch == '$' || ch == '@') {175          directive += getOriginalChar(ch);176          inDirectiveSentinel = true;177        } else if (directive == "$"s) {178          ompConditionalLine = true;179        }180      }181 182      std::optional<SourcePosition> position{provenance183              ? allSources.GetSourcePosition(provenance->start())184              : std::nullopt};185      if (column == 1 && position) {186        if (lineDirectives) {187          if (&*position->path != sourcePath) {188            out << "#line \"" << *position->path << "\" " << position->line189                << '\n';190          } else if (position->line != sourceLine) {191            if (sourceLine < position->line &&192                sourceLine + 10 >= position->line) {193              // Emit a few newlines to catch up when they'll likely194              // require fewer bytes than a #line directive would have195              // occupied.196              while (sourceLine++ < position->line) {197                out << '\n';198              }199            } else {200              out << "#line " << position->line << '\n';201            }202          }203        }204        sourcePath = &*position->path;205        sourceLine = position->line;206      }207      if (column > 72) {208        // Wrap long lines in a portable fashion that works in both209        // of the Fortran source forms. The first free-form continuation210        // marker ("&") lands in column 73, which begins the card commentary211        // field of fixed form, and the second one is put in column 6,212        // where it signifies fixed form line continuation.213        // The standard Fortran fixed form column limit (72) is used214        // for output, even if the input was parsed with a nonstandard215        // column limit override option.216        // OpenMP and OpenACC directives' continuations should have the217        // corresponding sentinel at the next line.218        out << "&\n";219        if (inDirective) {220          if (ompConditionalLine) {221            out << "!$   &";222          } else {223            out << '!' << directive << '&';224          }225        } else {226          out << "     &";227        }228        column = 7; // start of fixed form source field229        ++sourceLine;230        inContinuation = true;231      } else if (!inDirective && !ompConditionalLine && ch != ' ' &&232          (ch < '0' || ch > '9')) {233        // Put anything other than a label or directive into the234        // Fortran fixed form source field (columns [7:72]).235        for (int toCol{ch == '&' ? 6 : 7}; column < toCol; ++column) {236          out << ' ';237        }238      }239      if (ch != ' ') {240        if (ompConditionalLine) {241          // Only digits can stay in the label field242          if (!(ch >= '0' && ch <= '9')) {243            for (int toCol{ch == '&' ? 6 : 7}; column < toCol; ++column) {244              out << ' ';245            }246          }247        } else if (!inContinuation && !inDirectiveSentinel && position &&248            position->line == sourceLine && position->column < 72) {249          // Preserve original indentation250          for (; column < position->column; ++column) {251            out << ' ';252          }253        }254      }255      out << getOriginalChar(ch);256      lineWasBlankBefore = ch == ' ' && lineWasBlankBefore;257      ++column;258    }259  }260}261 262void Parsing::DumpCookedChars(llvm::raw_ostream &out) const {263  UserState userState{allCooked_, common::LanguageFeatureControl{}};264  ParseState parseState{cooked()};265  parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);266  while (std::optional<const char *> p{parseState.GetNextChar()}) {267    out << **p;268  }269}270 271void Parsing::DumpProvenance(llvm::raw_ostream &out) const {272  allCooked_.Dump(out);273}274 275void Parsing::DumpParsingLog(llvm::raw_ostream &out) const {276  log_.Dump(out, allCooked_);277}278 279void Parsing::Parse(llvm::raw_ostream &out) {280  UserState userState{allCooked_, options_.features};281  userState.set_debugOutput(out)282      .set_instrumentedParse(options_.instrumentedParse)283      .set_log(&log_);284  ParseState parseState{cooked()};285  parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);286  // Don't bother managing message buffers when parsing module files.287  parseState.set_deferMessages(options_.isModuleFile);288  parseTree_ = program.Parse(parseState);289  CHECK(290      !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());291  consumedWholeFile_ = parseState.IsAtEnd();292  messages_.Annex(std::move(parseState.messages()));293  finalRestingPlace_ = parseState.GetLocation();294}295 296void Parsing::ClearLog() { log_.clear(); }297 298} // namespace Fortran::parser299