Fortran::parser::die("no case at " __FILE__ "(%d)", __LINE__)
// For cheap assertions that should be applied in production.
+// To disable all CHECK tests, compile with -DCHECK.
+#ifndef CHECK
#define CHECK(x) \
((x) || \
(Fortran::parser::die( \
"CHECK(" #x ") failed at " __FILE__ "(%d)", __LINE__), \
false))
+#endif
// To make error messages more informative, wrap some type information
// around a false compile-time value, e.g.
}
TokenSequence Definition::Apply(
- const std::vector<TokenSequence> &args, const Prescanner &prescanner) {
+ const std::vector<TokenSequence> &args, const AllSources &allSources) {
TokenSequence result;
bool pasting{false};
bool skipping{false};
while (result.size() >= afterLastNonBlank) {
result.pop_back();
}
- result.PutNextTokenChar(
- '"', prescanner.CompilerInsertionProvenance('"'));
+ Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')};
+ result.PutNextTokenChar('"', quoteProvenance);
for (size_t k{0}; k < argTokens; ++k) {
const CharPointerWithLength &arg{args[index][k]};
size_t argBytes{args[index][k].size()};
result.PutNextTokenChar(ch, from);
}
}
- result.PutNextTokenChar(
- '"', prescanner.CompilerInsertionProvenance('"'));
+ result.PutNextTokenChar('"', quoteProvenance);
result.CloseToken();
} else {
for (size_t k{0}; k < argTokens; ++k) {
token.ToString() == "__VA_ARGS__") {
for (size_t k{argumentCount_}; k < args.size(); ++k) {
if (k > argumentCount_) {
- result.Put(","s, prescanner.CompilerInsertionProvenance(','));
+ result.Put(","s, allSources.CompilerInsertionProvenance(','));
}
result.Put(args[k]);
}
std::strftime(buffer, sizeof buffer, format, std::localtime(&now))};
}
-Preprocessor::Preprocessor(Prescanner &ps) : prescanner_{ps} {
+Preprocessor::Preprocessor(AllSources *allSources) : allSources_{allSources} {
// Capture current local date & time once now to avoid having the values
// of __DATE__ or __TIME__ change during compilation.
std::time_t now;
std::time(&now);
definitions_.emplace(SaveTokenAsName("__DATE__"s), // e.g., "Jun 16 1904"
- Definition{FormatTime(now, "\"%h %e %Y\""), ps.allSources()});
+ Definition{FormatTime(now, "\"%h %e %Y\""), allSources});
definitions_.emplace(SaveTokenAsName("__TIME__"s), // e.g., "23:59:60"
- Definition{FormatTime(now, "\"%T\""), ps.allSources()});
+ Definition{FormatTime(now, "\"%T\""), allSources});
// The values of these predefined macros depend on their invocation sites.
definitions_.emplace(
- SaveTokenAsName("__FILE__"s), Definition{"__FILE__"s, ps.allSources()});
+ SaveTokenAsName("__FILE__"s), Definition{"__FILE__"s, allSources});
definitions_.emplace(
- SaveTokenAsName("__LINE__"s), Definition{"__LINE__"s, ps.allSources()});
+ SaveTokenAsName("__LINE__"s), Definition{"__LINE__"s, allSources});
}
-bool Preprocessor::MacroReplacement(
- const TokenSequence &input, TokenSequence *result) {
+bool Preprocessor::MacroReplacement(const TokenSequence &input,
+ const Prescanner &prescanner, TokenSequence *result) {
// Do quick scan for any use of a defined name.
size_t tokens{input.size()};
size_t j;
std::string name{def.replacement()[0].ToString()};
if (name == "__FILE__") {
std::string f{"\""s +
- prescanner_.allSources()->GetPath(
- prescanner_.GetCurrentProvenance()) +
- '"'};
- result->Put(
- f, prescanner_.allSources()->AddCompilerInsertion(f).start);
+ allSources_->GetPath(prescanner.GetCurrentProvenance()) + '"'};
+ result->Put(f, allSources_->AddCompilerInsertion(f).start);
continue;
}
if (name == "__LINE__") {
std::stringstream ss;
- ss << prescanner_.allSources()->GetLineNumber(
- prescanner_.GetCurrentProvenance());
+ ss << allSources_->GetLineNumber(prescanner.GetCurrentProvenance());
std::string s{ss.str()};
- result->Put(
- s, prescanner_.allSources()->AddCompilerInsertion(s).start);
+ result->Put(s, allSources_->AddCompilerInsertion(s).start);
continue;
}
}
def.set_isDisabled(true);
- result->Put(ReplaceMacros(def.replacement()));
+ result->Put(ReplaceMacros(def.replacement(), prescanner));
def.set_isDisabled(false);
continue;
}
args.emplace_back(TokenSequence(input, at, count));
}
def.set_isDisabled(true);
- result->Put(ReplaceMacros(def.Apply(args, prescanner_)));
+ result->Put(ReplaceMacros(def.Apply(args, *allSources_), prescanner));
def.set_isDisabled(false);
}
return true;
}
-TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens) {
+TokenSequence Preprocessor::ReplaceMacros(
+ const TokenSequence &tokens, const Prescanner &prescanner) {
TokenSequence repl;
- return MacroReplacement(tokens, &repl) ? repl : tokens;
+ return MacroReplacement(tokens, prescanner, &repl) ? repl : tokens;
}
static size_t SkipBlanks(
return ConvertToLowerCase(line[j].ToString());
}
-bool Preprocessor::Directive(const TokenSequence &dir) {
+bool Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
size_t tokens{dir.size()};
size_t j{SkipBlanks(dir, 0, tokens)};
if (j == tokens) {
return true;
}
if (dir[j].ToString() != "#") {
- Complain("missing '#'");
+ prescanner->Complain("missing '#'");
return false;
}
j = SkipBlanks(dir, j + 1, tokens);
}
if (dirName == "define") {
if (nameToken.empty()) {
- Complain("#define: missing or invalid name");
+ prescanner->Complain("#define: missing or invalid name");
return false;
}
nameToken = SaveTokenAsName(nameToken);
isVariadic = true;
} else {
if (an.empty() || !IsIdentifierFirstCharacter(an[0])) {
- Complain("#define: missing or invalid argument name");
+ prescanner->Complain("#define: missing or invalid argument name");
return false;
}
argName.push_back(an);
}
j = SkipBlanks(dir, j + 1, tokens);
if (j == tokens) {
- Complain("#define: malformed argument list");
+ prescanner->Complain("#define: malformed argument list");
return false;
}
std::string punc{dir[j].ToString()};
break;
}
if (punc != ",") {
- Complain("#define: malformed argument list");
+ prescanner->Complain("#define: malformed argument list");
return false;
}
j = SkipBlanks(dir, j + 1, tokens);
if (j == tokens || isVariadic) {
- Complain("#define: malformed argument list");
+ prescanner->Complain("#define: malformed argument list");
return false;
}
}
if (std::set<std::string>(argName.begin(), argName.end()).size() !=
argName.size()) {
- Complain("#define: argument names are not distinct");
+ prescanner->Complain("#define: argument names are not distinct");
return false;
}
}
}
if (dirName == "undef") {
if (nameToken.empty()) {
- Complain("# missing or invalid name");
+ prescanner->Complain("# missing or invalid name");
return false;
}
j = SkipBlanks(dir, j + 1, tokens);
if (j != tokens) {
- Complain("#undef: excess tokens at end of directive");
+ prescanner->Complain("#undef: excess tokens at end of directive");
return false;
}
definitions_.erase(nameToken);
}
if (dirName == "ifdef" || dirName == "ifndef") {
if (nameToken.empty()) {
- Complain("#"s + dirName + ": missing name");
+ prescanner->Complain("#"s + dirName + ": missing name");
return false;
}
j = SkipBlanks(dir, j + 1, tokens);
if (j != tokens) {
- Complain("#"s + dirName + ": excess tokens at end of directive");
+ prescanner->Complain(
+ "#"s + dirName + ": excess tokens at end of directive");
return false;
}
if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
ifStack_.push(CanDeadElseAppear::Yes);
return true;
}
- return SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
+ return SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner);
}
if (dirName == "if") {
- if (IsIfPredicateTrue(dir, j, tokens - j)) {
+ if (IsIfPredicateTrue(dir, j, tokens - j, prescanner)) {
ifStack_.push(CanDeadElseAppear::Yes);
return true;
}
- return SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
+ return SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner);
}
if (dirName == "else") {
if (j != tokens) {
- Complain("#else: excess tokens at end of directive");
+ prescanner->Complain("#else: excess tokens at end of directive");
return false;
}
if (ifStack_.empty()) {
- Complain("#else: not nested within #if, #ifdef, or #ifndef");
+ prescanner->Complain("#else: not nested within #if, #ifdef, or #ifndef");
return false;
}
if (ifStack_.top() != CanDeadElseAppear::Yes) {
- Complain("#else: already appeared within this #if, #ifdef, or #ifndef");
+ prescanner->Complain(
+ "#else: already appeared within this #if, #ifdef, or #ifndef");
return false;
}
ifStack_.pop();
- return SkipDisabledConditionalCode("else", IsElseActive::No);
+ return SkipDisabledConditionalCode("else", IsElseActive::No, prescanner);
}
if (dirName == "elif") {
if (ifStack_.empty()) {
- Complain("#elif: not nested within #if, #ifdef, or #ifndef");
+ prescanner->Complain("#elif: not nested within #if, #ifdef, or #ifndef");
return false;
}
if (ifStack_.top() != CanDeadElseAppear::Yes) {
- Complain("#elif: #else previously appeared within this "
- "#if, #ifdef, or #ifndef");
+ prescanner->Complain("#elif: #else previously appeared within this "
+ "#if, #ifdef, or #ifndef");
return false;
}
ifStack_.pop();
- return SkipDisabledConditionalCode("elif", IsElseActive::No);
+ return SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner);
}
if (dirName == "endif") {
if (j != tokens) {
- Complain("#endif: excess tokens at end of directive");
+ prescanner->Complain("#endif: excess tokens at end of directive");
return false;
}
if (ifStack_.empty()) {
- Complain("#endif: no #if, #ifdef, or #ifndef");
+ prescanner->Complain("#endif: no #if, #ifdef, or #ifndef");
return false;
}
ifStack_.pop();
return true;
}
if (dirName == "error" || dirName == "warning") {
- Complain(dir.ToString());
+ prescanner->Complain(dir.ToString());
return dirName != "error";
}
- Complain("#"s + dirName + ": unknown or unimplemented directive");
+ prescanner->Complain("#"s + dirName + ": unknown or unimplemented directive");
return false;
}
return definitions_.find(token) != definitions_.end();
}
-bool Preprocessor::SkipDisabledConditionalCode(
- const std::string &dirName, IsElseActive isElseActive) {
+bool Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
+ IsElseActive isElseActive, Prescanner *prescanner) {
int nesting{0};
- while (std::optional<TokenSequence> line{prescanner_.NextTokenizedLine()}) {
+ while (std::optional<TokenSequence> line{prescanner->NextTokenizedLine()}) {
size_t rest{0};
std::string dn{GetDirectiveName(*line, &rest)};
if (dn == "ifdef" || dn == "ifndef" || dn == "if") {
ifStack_.push(CanDeadElseAppear::No);
return true;
}
- if (dn == "elif" && IsIfPredicateTrue(*line, rest, line->size() - rest)) {
+ if (dn == "elif" &&
+ IsIfPredicateTrue(*line, rest, line->size() - rest, prescanner)) {
ifStack_.push(CanDeadElseAppear::Yes);
return true;
}
}
}
- Complain("#"s + dirName + ": missing #endif");
+ prescanner->Complain("#"s + dirName + ": missing #endif");
return false;
}
-void Preprocessor::Complain(const std::string &message) {
- prescanner_.messages()->Put({prescanner_.GetCurrentProvenance(), message});
-}
-
// Precedence level codes used here to accommodate mixed Fortran and C:
// 15: parentheses and constants, logical !, bitwise ~
// 14: unary + and -
return 0; // silence compiler warning
}
-bool Preprocessor::IsIfPredicateTrue(
- const TokenSequence &expr, size_t first, size_t exprTokens) {
+bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, size_t first,
+ size_t exprTokens, Prescanner *prescanner) {
TokenSequence expr1{StripBlanks(expr, first, first + exprTokens)};
TokenSequence expr2;
for (size_t j{0}; j < expr1.size(); ++j) {
}
if (!name.empty()) {
char truth{IsNameDefined(name) ? '1' : '0'};
- expr2.Put(&truth, 1, prescanner_.CompilerInsertionProvenance(truth));
+ expr2.Put(&truth, 1, allSources_->CompilerInsertionProvenance(truth));
continue;
}
}
expr2.Put(expr1, j);
}
- TokenSequence expr3{ReplaceMacros(expr2)};
+ TokenSequence expr3{ReplaceMacros(expr2, *prescanner)};
TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
size_t atToken{0};
std::string error;
bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
if (!error.empty()) {
- Complain(error);
+ prescanner->Complain(error);
} else if (atToken < expr4.size()) {
- Complain(atToken == 0 ? "could not parse any expression"
- : "excess characters after expression");
+ prescanner->Complain(atToken == 0 ? "could not parse any expression"
+ : "excess characters after expression");
}
return result;
}
namespace Fortran {
namespace parser {
-class CookedSource;
class Prescanner;
// Just a const char pointer with an associated length; does not own the
bool set_isDisabled(bool disable);
TokenSequence Apply(
- const std::vector<TokenSequence> &args, const Prescanner &);
+ const std::vector<TokenSequence> &args, const AllSources &);
private:
static TokenSequence Tokenize(const std::vector<std::string> &argNames,
// Preprocessing state
class Preprocessor {
public:
- explicit Preprocessor(Prescanner &);
+ explicit Preprocessor(AllSources *);
// When the input contains macros to be replaced, the new token sequence
// is appended to the output and the returned value is true. When
// no macro replacement is necessary, the output is unmodified and the
// return value is false.
- bool MacroReplacement(const TokenSequence &, TokenSequence *);
+ bool MacroReplacement(
+ const TokenSequence &, const Prescanner &, TokenSequence *);
// Implements a preprocessor directive; returns true when no fatal error.
- bool Directive(const TokenSequence &);
+ bool Directive(const TokenSequence &, Prescanner *);
private:
enum class IsElseActive { No, Yes };
enum class CanDeadElseAppear { No, Yes };
- void Complain(const std::string &);
CharPointerWithLength SaveTokenAsName(const CharPointerWithLength &);
bool IsNameDefined(const CharPointerWithLength &);
- TokenSequence ReplaceMacros(const TokenSequence &);
- bool SkipDisabledConditionalCode(const std::string &dirName, IsElseActive);
+ TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &);
+ bool SkipDisabledConditionalCode(
+ const std::string &dirName, IsElseActive, Prescanner *);
bool IsIfPredicateTrue(
- const TokenSequence &expr, size_t first, size_t exprTokens);
+ const TokenSequence &expr, size_t first, size_t exprTokens, Prescanner *);
- Prescanner &prescanner_;
+ AllSources *allSources_;
std::list<std::string> names_;
std::unordered_map<CharPointerWithLength, Definition> definitions_;
std::stack<CanDeadElseAppear> ifStack_;
namespace Fortran {
namespace parser {
-Prescanner::Prescanner(Messages *messages, AllSources *allSources)
- : messages_{messages}, allSources_{allSources}, start_{&(*allSources)[0]},
- limit_{start_ + allSources->size()}, preprocessor_{*this} {
- std::string compilerInserts{" ,\"01\n"};
- ProvenanceRange range{allSources->AddCompilerInsertion(compilerInserts)};
- for (size_t j{0}; j < compilerInserts.size(); ++j) {
- compilerInsertionProvenance_[compilerInserts[j]] = range.start + j;
- }
- newlineProvenance_ = CompilerInsertionProvenance('\n');
-}
+Prescanner::Prescanner(
+ Messages *messages, CookedSource *cooked, Preprocessor *preprocessor)
+ : messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor} {}
-CookedSource Prescanner::Prescan() {
- lineStart_ = start_;
+bool Prescanner::Prescan(ProvenanceRange range) {
+ startProvenance_ = range.start;
+ ProvenanceRange around{
+ cooked_->allSources()->GetContiguousRangeAround(startProvenance_)};
+ CHECK(startProvenance_ + range.bytes <= around.start + around.bytes);
+ const SourceFile *source{
+ cooked_->allSources()->GetSourceFile(startProvenance_)};
+ size_t offset{startProvenance_ - around.start};
+ lineStart_ = start_ = source->content() + offset;
+ limit_ = start_ + range.bytes;
BeginSourceLine(start_);
TokenSequence tokens, preprocessed;
- CookedSource cooked{allSources_};
while (lineStart_ < limit_) {
if (CommentLinesAndPreprocessorDirectives() && lineStart_ >= limit_) {
break;
}
while (NextToken(&tokens)) {
}
- if (preprocessor_.MacroReplacement(tokens, &preprocessed)) {
+ if (preprocessor_->MacroReplacement(tokens, *this, &preprocessed)) {
preprocessed.PutNextTokenChar('\n', newlineProvenance_);
preprocessed.CloseToken();
if (IsFixedFormCommentLine(preprocessed.data()) ||
++newlineDebt_;
} else {
preprocessed.pop_back(); // clip the newline added above
- preprocessed.EmitWithCaseConversion(&cooked);
+ preprocessed.EmitWithCaseConversion(cooked_);
}
preprocessed.clear();
} else {
- tokens.EmitWithCaseConversion(&cooked);
+ tokens.EmitWithCaseConversion(cooked_);
}
tokens.clear();
- cooked.Put('\n', newlineProvenance_);
- PayNewlineDebt(&cooked);
+ cooked_->Put('\n', newlineProvenance_);
+ PayNewlineDebt(cooked_);
}
- PayNewlineDebt(&cooked);
- cooked.Marshal();
- return cooked;
+ PayNewlineDebt(cooked_);
+ return !anyFatalErrors_;
}
std::optional<TokenSequence> Prescanner::NextTokenizedLine() {
return {std::move(tokens)};
}
-Provenance Prescanner::CompilerInsertionProvenance(char ch) const {
- return compilerInsertionProvenance_.find(ch)->second;
+void Prescanner::Complain(const std::string &message) {
+ messages_->Put({GetCurrentProvenance(), message});
}
void Prescanner::NextLine() {
token->CloseToken();
}
if (outCol < 7) {
- Provenance provenance{CompilerInsertionProvenance(' ')};
for (; outCol < 7; ++outCol) {
- token->PutNextTokenChar(' ', provenance);
+ token->PutNextTokenChar(' ', spaceProvenance_);
}
token->CloseToken();
}
}
void Prescanner::NextChar() {
- CHECK(*at_ != '\n'); // TODO pmk
+ CHECK(*at_ != '\n');
++at_;
++column_;
if (inPreprocessorDirective_) {
}
bool Prescanner::NextToken(TokenSequence *tokens) {
- CHECK(at_ >= start_ && at_ < limit_); // TODO pmk rm?
+ CHECK(at_ >= start_ && at_ < limit_);
if (inFixedForm_) {
SkipSpaces();
} else if (*at_ == ' ' || *at_ == '\t') {
inCharLiteral_ = true;
while (n-- > 0) {
if (PadOutCharacterLiteral()) {
- tokens->PutNextTokenChar(' ', compilerInsertionProvenance_[' ']);
+ tokens->PutNextTokenChar(' ', spaceProvenance_);
} else {
if (*at_ == '\n') {
break; // TODO error
do {
EmitCharAndAdvance(tokens, *at_);
while (PadOutCharacterLiteral()) {
- tokens->PutNextTokenChar(' ', compilerInsertionProvenance_[' ']);
+ tokens->PutNextTokenChar(' ', spaceProvenance_);
}
if (*at_ == '\\' && enableBackslashEscapesInCharLiterals_) {
EmitCharAndAdvance(tokens, '\\');
while (PadOutCharacterLiteral()) {
- tokens->PutNextTokenChar(' ', compilerInsertionProvenance_[' ']);
+ tokens->PutNextTokenChar(' ', spaceProvenance_);
}
} else if (*at_ == quote) {
// A doubled quote mark becomes a single instance of the quote character
NextLine();
} else if (IsPreprocessorDirectiveLine(lineStart_)) {
if (std::optional<TokenSequence> tokens{NextTokenizedLine()}) {
- anyFatalErrors_ |= !preprocessor_.Directive(*tokens);
+ anyFatalErrors_ |= !preprocessor_->Directive(*tokens, this);
}
} else {
break;
// character-level features of the language that can be inefficient to
// support directly in a backtracking parser. This phase handles Fortran
// line continuation, comment removal, card image margins, padding out
-// fixed form character literals on truncated card images, and drives the
-// Fortran source preprocessor.
+// fixed form character literals on truncated card images, file
+// inclusion, and driving the Fortran source preprocessor.
#include "message.h"
#include "preprocessor.h"
#include "provenance.h"
#include "source.h"
-#include <map>
#include <optional>
#include <string>
class Prescanner {
public:
- Prescanner(Messages *, AllSources *);
+ Prescanner(Messages *, CookedSource *, Preprocessor *);
Messages *messages() const { return messages_; }
- bool anyFatalErrors() const { return anyFatalErrors_; }
Prescanner &set_fixedForm(bool yes) {
inFixedForm_ = yes;
return *this;
}
- AllSources *allSources() const { return allSources_; }
+ CookedSource *cooked() const { return cooked_; }
- CookedSource Prescan();
+ bool Prescan(ProvenanceRange);
std::optional<TokenSequence> NextTokenizedLine();
Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
- Provenance CompilerInsertionProvenance(char ch) const;
+ void Complain(const std::string &message);
private:
void BeginSourceLine(const char *at) {
}
Provenance GetProvenance(const char *sourceChar) const {
- return startProvenance_ /*TODO pmk rm?*/ + sourceChar - start_;
+ return startProvenance_ + sourceChar - start_;
}
void EmitChar(TokenSequence *tokens, char ch) {
void PayNewlineDebt(CookedSource *);
Messages *messages_;
- AllSources *allSources_;
+ CookedSource *cooked_;
+ Preprocessor *preprocessor_;
Provenance startProvenance_{0};
- const char *start_{nullptr}; // beginning of sourceFile_ content
- const char *limit_{nullptr}; // first address after end of source
+ const char *start_{nullptr}; // beginning of current source file content
+ const char *limit_{nullptr}; // first address after end of current source
const char *at_{nullptr}; // next character to process; < lineStart_
int column_{1}; // card image column position of next character
const char *lineStart_{nullptr}; // next line to process; <= limit_
bool enableOldDebugLines_{false};
bool enableBackslashEscapesInCharLiterals_{true};
int delimiterNesting_{0};
- Preprocessor preprocessor_;
- std::map<char, Provenance> compilerInsertionProvenance_;
- Provenance newlineProvenance_{0};
+ Provenance newlineProvenance_{
+ cooked_->allSources()->CompilerInsertionProvenance('\n')};
+ Provenance spaceProvenance_{
+ cooked_->allSources()->CompilerInsertionProvenance(' ')};
};
} // namespace parser
} // namespace Fortran
}
}
-AllSources::AllSources(const SourceFile &initialSourceFile) {
- AddIncludedFile(initialSourceFile, ProvenanceRange{});
+AllSources::AllSources() {
+ std::string compilerInserts{" ,\"01\n"};
+ ProvenanceRange range{AddCompilerInsertion(compilerInserts)};
+ for (size_t j{0}; j < range.bytes; ++j) {
+ compilerInsertionProvenance_[compilerInserts[j]] = range.start + j;
+ }
}
const char &AllSources::operator[](Provenance at) const {
origin.u);
}
+ProvenanceRange AllSources::GetContiguousRangeAround(Provenance at) const {
+ const Origin &origin{MapToOrigin(at)};
+ return {origin.start, origin.size()};
+}
+
std::string AllSources::GetPath(Provenance at) const {
const SourceFile *source{GetSourceFile(at)};
return source ? source->path() : ""s;
return source ? source->FindOffsetLineAndColumn(at).first : 0;
}
+Provenance AllSources::CompilerInsertionProvenance(char ch) const {
+ return compilerInsertionProvenance_.find(ch)->second;
+}
+
AllSources::Origin::Origin(size_t s, const SourceFile &source)
: start{s}, u{Inclusion{source}} {}
AllSources::Origin::Origin(
#define FORTRAN_PROVENANCE_H_
#include "char-buffer.h"
#include "source.h"
+#include <map>
#include <ostream>
#include <string>
#include <utility>
ProvenanceRange(ProvenanceRange &&) = default;
ProvenanceRange &operator=(const ProvenanceRange &) = default;
ProvenanceRange &operator=(ProvenanceRange &&) = default;
+
+ bool operator==(const ProvenanceRange &that) const {
+ return start == that.start && bytes == that.bytes;
+ }
+
Provenance start{0};
size_t bytes{0};
};
class AllSources {
public:
- explicit AllSources(const SourceFile &initialSourceFile);
+ AllSources();
size_t size() const { return bytes_; }
const char &operator[](Provenance) const;
void Identify(std::ostream &, Provenance, const std::string &prefix) const;
const SourceFile *GetSourceFile(Provenance) const;
+ ProvenanceRange GetContiguousRangeAround(Provenance) const;
std::string GetPath(Provenance) const; // __FILE__
int GetLineNumber(Provenance) const; // __LINE__
+ Provenance CompilerInsertionProvenance(char ch) const;
private:
struct Inclusion {
std::vector<Origin> origin_;
size_t bytes_{0};
+ std::map<char, Provenance> compilerInsertionProvenance_;
};
class CookedSource {
#include "../../lib/parser/idioms.h"
#include "../../lib/parser/message.h"
#include "../../lib/parser/parse-tree.h"
+#include "../../lib/parser/preprocessor.h"
#include "../../lib/parser/prescan.h"
#include "../../lib/parser/provenance.h"
#include "../../lib/parser/source.h"
return 1;
}
- Fortran::parser::AllSources allSources{sourceFile};
+ Fortran::parser::AllSources allSources;
+ Fortran::parser::ProvenanceRange range{allSources.AddIncludedFile(
+ sourceFile, Fortran::parser::ProvenanceRange{})};
Fortran::parser::Messages messages{allSources};
- Fortran::parser::Prescanner prescanner{&messages, &allSources};
- Fortran::parser::CookedSource cooked{
- prescanner.set_fixedForm(fixedForm)
- .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
- .set_fixedFormColumnLimit(columns)
- .set_enableOldDebugLines(enableOldDebugLines)
- .Prescan()};
+ Fortran::parser::CookedSource cooked{&allSources};
+ Fortran::parser::Preprocessor preprocessor{&allSources};
+ Fortran::parser::Prescanner prescanner{&messages, &cooked, &preprocessor};
+ bool prescanOk{prescanner.set_fixedForm(fixedForm)
+ .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
+ .set_fixedFormColumnLimit(columns)
+ .set_enableOldDebugLines(enableOldDebugLines)
+ .Prescan(range)};
messages.Emit(std::cerr);
- if (prescanner.anyFatalErrors()) {
+ if (!prescanOk) {
return 1;
}
columns = std::numeric_limits<int>::max();
+ cooked.Marshal();
Fortran::parser::ParseState state{cooked};
state.set_inFixedForm(fixedForm);
state.set_enableBackslashEscapesInCharLiterals(backslashEscapes);