set(CMAKE_BUILD_WITH_INSTALL_RPATH true)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -std=c++17")
+set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DDEBUG")
+set(CMAKE_CXX_FLAGS_MINSIZEREL "-O2 '-DCHECK=(void)'")
+set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUG")
set(SOURCES_F18
tools/f18/f18.cc
constexpr DebugParser(const char *str, size_t n) : str_{str}, length_{n} {}
std::optional<Success> Parse(ParseState *state) const {
if (auto context = state->context()) {
- std::cout << *context;
+ context->Emit(std::cout, *state->cooked().allSources());
}
- state->cooked().allSources().Identify(
+ state->cooked().allSources()->Identify(
std::cout, state->GetProvenance(), "");
std::cout << " parser debug: " << std::string{str_, length_} << '\n';
return {Success{}};
maybe(label), isLabelOk, spaces >> p);
}
-constexpr auto endOfLine =
- CharMatch<'\n'>{} || fail<char>("expected end of line");
+constexpr auto endOfLine = CharMatch<'\n'>{} / skipMany("\n"_tok) ||
+ fail<char>("expected end of line");
-constexpr auto
- endOfStmt = spaces >> CharMatch<';'>{} / skipMany(";"_tok) || endOfLine;
+constexpr auto endOfStmt = spaces >>
+ (CharMatch<';'>{} / skipMany(";"_tok) / maybe(endOfLine) || endOfLine);
template<typename PA> inline constexpr auto statement(const PA &p) {
return unterminatedStatement(p) / endOfStmt;
} startNewSubprogram;
TYPE_PARSER(construct<Program>{}(
- some(startNewSubprogram >> Parser<ProgramUnit>{} / endOfLine)) /
- skipMany(endOfLine) / consumedAllInput)
+ // statements consume only trailing noise; consume leading noise here.
+ skipMany("\n"_tok) >>
+ some(startNewSubprogram >> Parser<ProgramUnit>{} / endOfLine) /
+ consumedAllInput))
// R502 program-unit ->
// main-program | external-subprogram | module | submodule | block-data
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.
+// To disable, compile with '-DCHECK=(void)'
#ifndef CHECK
#define CHECK(x) \
((x) || \
class ParseState {
public:
+ // TODO: Add a constructor for parsing a normalized module file.
ParseState(const CookedSource &cooked)
- : cooked_{cooked}, p_{&cooked[0]},
- remaining_{cooked.size()}, messages_{*cooked.allSources()} {}
+ : cooked_{cooked}, p_{&cooked[0]}, limit_{p_ + cooked.size()},
+ messages_{*cooked.allSources()} {}
ParseState(const ParseState &that)
- : cooked_{that.cooked_}, p_{that.p_}, remaining_{that.remaining_},
+ : cooked_{that.cooked_}, p_{that.p_}, limit_{that.limit_},
column_{that.column_}, messages_{*that.cooked_.allSources()},
userState_{that.userState_}, inCharLiteral_{that.inCharLiteral_},
inFortran_{that.inFortran_}, inFixedForm_{that.inFixedForm_},
anyErrorRecovery_{that.anyErrorRecovery_}, prescanned_{that.prescanned_} {
}
ParseState(ParseState &&that)
- : cooked_{that.cooked_}, p_{that.p_},
- remaining_{that.remaining_}, column_{that.column_},
- messages_{std::move(that.messages_)}, context_{std::move(that.context_)},
- userState_{that.userState_}, inCharLiteral_{that.inCharLiteral_},
- inFortran_{that.inFortran_}, inFixedForm_{that.inFixedForm_},
+ : cooked_{that.cooked_}, p_{that.p_}, limit_{that.limit_},
+ column_{that.column_}, messages_{std::move(that.messages_)},
+ context_{std::move(that.context_)}, userState_{that.userState_},
+ inCharLiteral_{that.inCharLiteral_}, inFortran_{that.inFortran_},
+ inFixedForm_{that.inFixedForm_},
enableOldDebugLines_{that.enableOldDebugLines_}, columns_{that.columns_},
enableBackslashEscapesInCharLiterals_{
that.enableBackslashEscapesInCharLiterals_},
void set_userState(UserState *u) { userState_ = u; }
MessageContext context() const { return context_; }
- MessageContext set_context(MessageContext c) {
- MessageContext was{context_};
+ ParseState &set_context(MessageContext c) {
context_ = c;
- return was;
+ return *this;
}
bool inCharLiteral() const { return inCharLiteral_; }
- bool set_inCharLiteral(bool yes) {
- bool was{inCharLiteral_};
+ ParseState &set_inCharLiteral(bool yes) {
inCharLiteral_ = yes;
- return was;
+ return *this;
}
bool inFortran() const { return inFortran_; }
- bool set_inFortran(bool yes) {
- bool was{inFortran_};
+ ParseState &set_inFortran(bool yes) {
inFortran_ = yes;
- return was;
+ return *this;
}
bool inFixedForm() const { return inFixedForm_; }
- bool set_inFixedForm(bool yes) {
- bool was{inFixedForm_};
+ ParseState &set_inFixedForm(bool yes) {
inFixedForm_ = yes;
- return was;
+ return *this;
}
bool enableOldDebugLines() const { return enableOldDebugLines_; }
- bool set_enableOldDebugLines(bool yes) {
- bool was{enableOldDebugLines_};
+ ParseState &set_enableOldDebugLines(bool yes) {
enableOldDebugLines_ = yes;
- return was;
+ return *this;
}
int columns() const { return columns_; }
- int set_columns(int cols) {
- int was{columns_};
+ ParseState &set_columns(int cols) {
columns_ = cols;
- return was;
+ return *this;
}
bool enableBackslashEscapesInCharLiterals() const {
return enableBackslashEscapesInCharLiterals_;
}
- bool set_enableBackslashEscapesInCharLiterals(bool yes) {
- bool was{enableBackslashEscapesInCharLiterals_};
+ ParseState &set_enableBackslashEscapesInCharLiterals(bool yes) {
enableBackslashEscapesInCharLiterals_ = yes;
- return was;
+ return *this;
}
bool strictConformance() const { return strictConformance_; }
- bool set_strictConformance(bool yes) {
- bool was{strictConformance_};
+ ParseState &set_strictConformance(bool yes) {
strictConformance_ = yes;
- return was;
+ return *this;
}
bool warnOnNonstandardUsage() const { return warnOnNonstandardUsage_; }
- bool set_warnOnNonstandardUsage(bool yes) {
- bool was{warnOnNonstandardUsage_};
+ ParseState &set_warnOnNonstandardUsage(bool yes) {
warnOnNonstandardUsage_ = yes;
- return was;
+ return *this;
}
bool warnOnDeprecatedUsage() const { return warnOnDeprecatedUsage_; }
- bool set_warnOnDeprecatedUsage(bool yes) {
- bool was{warnOnDeprecatedUsage_};
+ ParseState &set_warnOnDeprecatedUsage(bool yes) {
warnOnDeprecatedUsage_ = yes;
- return was;
+ return *this;
}
int skippedNewLines() const { return skippedNewLines_; }
const char *GetLocation() const { return p_; }
Provenance GetProvenance(const char *at) const {
- return cooked_.GetProvenance(at).start;
+ return cooked_.GetProvenance(at).LocalOffsetToProvenance(0);
}
Provenance GetProvenance() const { return GetProvenance(p_); }
}
void PutMessage(const char *msg) { PutMessage(p_, msg); }
- bool IsAtEnd() const { return remaining_ == 0; }
+ bool IsAtEnd() const { return p_ >= limit_; }
std::optional<char> GetNextRawChar() const {
- if (remaining_ > 0) {
+ if (p_ < limit_) {
return {*p_};
}
return {};
}
void Advance() {
- CHECK(remaining_ > 0);
- --remaining_;
+ CHECK(p_ < limit_);
if (*p_ == '\n') {
column_ = 1;
tabInCurrentLine_ = false;
private:
// Text remaining to be parsed
const CookedSource &cooked_;
- const char *p_{nullptr};
- size_t remaining_{0};
+ const char *p_{nullptr}, *limit_{nullptr};
int column_{1};
// Accumulated messages and current nested context.
replacement_{Tokenize(argNames, repl, firstToken, tokens)} {}
Definition::Definition(const std::string &predefined, AllSources *sources)
- : isPredefined_{true}, replacement_{predefined,
- sources->AddCompilerInsertion(predefined).start} {}
+ : isPredefined_{true},
+ replacement_{predefined,
+ sources->AddCompilerInsertion(predefined).LocalOffsetToProvenance(0)} {}
bool Definition::set_isDisabled(bool disable) {
bool was{isDisabled_};
if (IsIdentifierFirstCharacter(tok)) {
auto it = args.find(tok.ToString());
if (it != args.end()) {
- result.Put(it->second, 0);
+ result.Put(it->second, token.GetProvenance(j));
continue;
}
}
size_t argBytes{args[index][k].size()};
for (size_t n{0}; n < argBytes; ++n) {
char ch{arg[n]};
- Provenance from{args[index].GetProvenance(k, n).start};
+ Provenance from{args[index].GetProvenance(k, n)};
if (ch == '"' || ch == '\\') {
result.PutNextTokenChar(ch, from);
}
if (!def.isFunctionLike()) {
if (def.isPredefined()) {
std::string name{def.replacement()[0].ToString()};
+ std::string repl;
if (name == "__FILE__") {
- std::string f{"\""s +
- allSources_->GetPath(prescanner.GetCurrentProvenance()) + '"'};
- result->Put(f, allSources_->AddCompilerInsertion(f).start);
- continue;
- }
- if (name == "__LINE__") {
+ repl = "\""s +
+ allSources_->GetPath(prescanner.GetCurrentProvenance()) + '"';
+ } else if (name == "__LINE__") {
std::stringstream ss;
ss << allSources_->GetLineNumber(prescanner.GetCurrentProvenance());
- std::string s{ss.str()};
- result->Put(s, allSources_->AddCompilerInsertion(s).start);
+ repl = ss.str();
+ }
+ if (!repl.empty()) {
+ ProvenanceRange insert{allSources_->AddCompilerInsertion(repl)};
+ ProvenanceRange call{allSources_->AddMacroCall(
+ insert, input.GetProvenanceRange(j), repl)};
+ result->Put(repl, call.LocalOffsetToProvenance(0));
continue;
}
}
prescanner->Complain("#include: missing name of file to include");
return false;
}
- ProvenanceRange includeDirRange{dir.GetProvenance(j)};
+ ProvenanceRange includeDirRange{dir.GetProvenanceRange(j)};
std::string include;
if (dir[j].ToString() == "<") {
if (dir[tokens - 1].ToString() != ">") {
COMMA
};
static const int precedence[]{
- 15, 15, 15, 15, // (), 0, !, ~
+ 15, 15, 15, 15, // (), 6, !, ~
14, 14, // unary +, -
13, 12, 12, 12, 11, 11, 10, 10, // **, *, /, %, +, -, <<, >>
9, 8, 7, // &, ^, |
that.enableBackslashEscapesInCharLiterals_} {}
bool Prescanner::Prescan(ProvenanceRange range) {
- startProvenance_ = range.start;
- ProvenanceRange around{
- cooked_->allSources()->GetContiguousRangeAround(startProvenance_)};
- CHECK(startProvenance_ + range.bytes <= around.start + around.bytes);
+ AllSources *allSources{cooked_->allSources()};
+ ProvenanceRange around{allSources->GetContiguousRangeAround(range)};
+ startProvenance_ = range.LocalOffsetToProvenance(0);
+ size_t offset{0};
const SourceFile *source{
- cooked_->allSources()->GetSourceFile(startProvenance_)};
- size_t offset{startProvenance_ - around.start};
- lineStart_ = start_ = source->content() + offset;
- limit_ = start_ + range.bytes;
- BeginSourceLine(start_);
+ allSources->GetSourceFile(startProvenance_, &offset)};
+ CHECK(source != nullptr);
+ start_ = source->content() + offset;
+ limit_ = start_ + range.size();
+ lineStart_ = start_;
+ BeginSourceLine(lineStart_);
TokenSequence tokens, preprocessed;
while (lineStart_ < limit_) {
if (CommentLinesAndPreprocessorDirectives() && lineStart_ >= limit_) {
- PayNewlineDebt();
break;
}
BeginSourceLineAndAdvance();
}
while (NextToken(&tokens)) {
}
+ Provenance newlineProvenance{GetCurrentProvenance()};
if (preprocessor_->MacroReplacement(tokens, *this, &preprocessed)) {
- preprocessed.PutNextTokenChar('\n', newlineProvenance_);
+ preprocessed.PutNextTokenChar('\n', newlineProvenance);
preprocessed.CloseToken();
if (IsFixedFormCommentLine(preprocessed.data()) ||
IsFreeFormComment(preprocessed.data())) {
tokens.EmitWithCaseConversion(cooked_);
}
tokens.clear();
- cooked_->Put('\n', newlineProvenance_);
- PayNewlineDebt();
+ ++newlineDebt_;
+ PayNewlineDebt(newlineProvenance);
}
- PayNewlineDebt();
return !anyFatalErrors_;
}
return true;
}
-void Prescanner::PayNewlineDebt() {
+void Prescanner::PayNewlineDebt(Provenance p) {
for (; newlineDebt_ > 0; --newlineDebt_) {
- cooked_->Put('\n', newlineProvenance_);
+ cooked_->Put('\n', p);
}
}
} // namespace parser
}
Provenance GetProvenance(const char *sourceChar) const {
- return startProvenance_ + sourceChar - start_;
+ return startProvenance_ + (sourceChar - start_);
}
void EmitChar(TokenSequence *tokens, char ch) {
const char *FixedFormContinuationLine();
bool FixedFormContinuation();
bool FreeFormContinuation();
- void PayNewlineDebt();
+ void PayNewlineDebt(Provenance);
Messages *messages_;
CookedSource *cooked_;
Preprocessor *preprocessor_;
- Provenance startProvenance_{0};
+ Provenance startProvenance_;
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_
bool enableOldDebugLines_{false};
bool enableBackslashEscapesInCharLiterals_{true};
int delimiterNesting_{0};
- Provenance newlineProvenance_{
- cooked_->allSources()->CompilerInsertionProvenance('\n')};
Provenance spaceProvenance_{
cooked_->allSources()->CompilerInsertionProvenance(' ')};
};
#include "provenance.h"
#include "idioms.h"
#include <utility>
+
namespace Fortran {
namespace parser {
-void OffsetToProvenanceMappings::clear() {
- bytes_ = 0;
- provenanceMap_.clear();
+void OffsetToProvenanceMappings::clear() { provenanceMap_.clear(); }
+
+size_t OffsetToProvenanceMappings::size() const {
+ if (provenanceMap_.empty()) {
+ return 0;
+ }
+ const ContiguousProvenanceMapping &last{provenanceMap_.back()};
+ return last.start + last.range.size();
}
void OffsetToProvenanceMappings::Put(ProvenanceRange range) {
if (provenanceMap_.empty()) {
- provenanceMap_.push_back({bytes_, range});
+ provenanceMap_.push_back({0, range});
} else {
ContiguousProvenanceMapping &last{provenanceMap_.back()};
- if (range.start == last.range.start + last.range.bytes) {
- last.range.bytes += range.bytes;
- } else {
- provenanceMap_.push_back({bytes_, range});
+ if (!last.range.AnnexIfPredecessor(range)) {
+ provenanceMap_.push_back({last.start + last.range.size(), range});
}
}
- bytes_ += range.bytes;
}
void OffsetToProvenanceMappings::Put(const OffsetToProvenanceMappings &that) {
}
ProvenanceRange OffsetToProvenanceMappings::Map(size_t at) const {
- CHECK(at < bytes_);
+ CHECK(!provenanceMap_.empty());
size_t low{0}, count{provenanceMap_.size()};
while (count > 1) {
size_t mid{low + (count >> 1)};
}
}
size_t offset{at - provenanceMap_[low].start};
- return {provenanceMap_[low].start + offset,
- provenanceMap_[low].range.bytes - offset};
+ return provenanceMap_[low].range.Suffix(offset);
}
void OffsetToProvenanceMappings::RemoveLastBytes(size_t bytes) {
for (; bytes > 0; provenanceMap_.pop_back()) {
- if (provenanceMap_.empty()) {
- break;
- }
+ CHECK(!provenanceMap_.empty());
ContiguousProvenanceMapping &last{provenanceMap_.back()};
- if (bytes < last.range.bytes) {
- last.range.bytes -= bytes;
+ size_t chunk{last.range.size()};
+ if (bytes < chunk) {
+ last.range = last.range.Prefix(chunk - bytes);
break;
}
- bytes -= last.range.bytes;
+ bytes -= chunk;
}
}
-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;
+AllSources::AllSources() : range_{1, 1} {
+ // Start the origin_ array with a dummy that has a forced provenance,
+ // so that provenance offset 0 remains reserved as an uninitialized
+ // value.
+ origin_.emplace_back(range_, std::string{'?'});
+
+ for (char ch : " ,\"01\n"s) {
+ compilerInsertionProvenance_[ch] =
+ AddCompilerInsertion(std::string{ch}).LocalOffsetToProvenance(0);
}
}
const char &AllSources::operator[](Provenance at) const {
const Origin &origin{MapToOrigin(at)};
- return origin[at - origin.start];
+ return origin[origin.covers.ProvenanceToLocalOffset(at)];
}
void AllSources::PushSearchPathDirectory(std::string directory) {
}
ProvenanceRange AllSources::AddIncludedFile(
- const SourceFile &source, ProvenanceRange from) {
- size_t start{bytes_}, bytes{source.bytes()};
- bytes_ += bytes;
- origin_.emplace_back(start, source, from);
- return {start, bytes};
+ const SourceFile &source, ProvenanceRange from, bool isModule) {
+ ProvenanceRange covers{range_.NextAfter(), source.bytes()};
+ CHECK(range_.AnnexIfPredecessor(covers));
+ CHECK(origin_.back().covers.IsPredecessor(covers));
+ origin_.emplace_back(covers, source, from, isModule);
+ return covers;
}
ProvenanceRange AllSources::AddMacroCall(
ProvenanceRange def, ProvenanceRange use, const std::string &expansion) {
- size_t start{bytes_}, bytes{expansion.size()};
- bytes_ += bytes;
- origin_.emplace_back(start, def, use, expansion);
- return {start, bytes};
+ ProvenanceRange covers{range_.NextAfter(), expansion.size()};
+ CHECK(range_.AnnexIfPredecessor(covers));
+ CHECK(origin_.back().covers.IsPredecessor(covers));
+ origin_.emplace_back(covers, def, use, expansion);
+ return covers;
}
-ProvenanceRange AllSources::AddCompilerInsertion(const std::string &text) {
- size_t start{bytes_}, bytes{text.size()};
- bytes_ += bytes;
- origin_.emplace_back(start, text);
- return {start, bytes};
+ProvenanceRange AllSources::AddCompilerInsertion(std::string text) {
+ ProvenanceRange covers{range_.NextAfter(), text.size()};
+ CHECK(range_.AnnexIfPredecessor(covers));
+ CHECK(origin_.back().covers.IsPredecessor(covers));
+ origin_.emplace_back(covers, text);
+ return covers;
}
void AllSources::Identify(
std::ostream &o, Provenance at, const std::string &prefix) const {
+ CHECK(IsValid(at));
static const std::string indented{prefix + " "};
const Origin &origin{MapToOrigin(at)};
std::visit(
- visitors{[&](const Inclusion &inc) {
- std::pair<int, int> pos{
- inc.source.FindOffsetLineAndColumn(at - origin.start)};
- o << prefix << "at line " << pos.first << ", column "
- << pos.second << " in the file " << inc.source.path()
- << '\n';
- if (origin.replaces.bytes > 0) {
- o << prefix << " that was included\n";
- Identify(o, origin.replaces.start, indented);
- }
- },
+ visitors{
+ [&](const Inclusion &inc) {
+ size_t offset{origin.covers.ProvenanceToLocalOffset(at)};
+ std::pair<int, int> pos{inc.source.FindOffsetLineAndColumn(offset)};
+ o << prefix << "at line " << pos.first << ", column " << pos.second
+ << " in the " << (inc.isModule ? "module " : "file ")
+ << inc.source.path() << '\n';
+ if (IsValid(origin.replaces)) {
+ o << prefix << " that was " << (inc.isModule ? "used\n" : "included\n");
+ Identify(o, origin.replaces.LocalOffsetToProvenance(0), indented);
+ }
+ },
[&](const Macro &mac) {
o << prefix << "in the expansion of a macro that was defined\n";
- Identify(o, mac.definition.start, indented);
+ Identify(o, mac.definition.LocalOffsetToProvenance(0), indented);
o << prefix << "... and called\n";
- Identify(o, origin.replaces.start, indented);
+ Identify(o, origin.replaces.LocalOffsetToProvenance(0), indented);
o << prefix << "... and expanded to\n"
<< indented << mac.expansion << '\n';
},
origin.u);
}
-const SourceFile *AllSources::GetSourceFile(Provenance at) const {
+const SourceFile *AllSources::GetSourceFile(
+ Provenance at, size_t *offset) const {
const Origin &origin{MapToOrigin(at)};
- return std::visit(visitors{[](const Inclusion &inc) { return &inc.source; },
- [&origin, this](const Macro &mac) {
- return GetSourceFile(origin.replaces.start);
- },
- [](const CompilerInsertion &) {
- return static_cast<const SourceFile *>(nullptr);
- }},
+ return std::visit(
+ visitors{[&](const Inclusion &inc) {
+ if (offset != nullptr) {
+ *offset = origin.covers.ProvenanceToLocalOffset(at);
+ }
+ return &inc.source;
+ },
+ [&](const Macro &mac) {
+ return GetSourceFile(
+ origin.replaces.LocalOffsetToProvenance(0), offset);
+ },
+ [offset](const CompilerInsertion &) {
+ if (offset != nullptr) {
+ *offset = 0;
+ }
+ return static_cast<const SourceFile *>(nullptr);
+ }},
origin.u);
}
-ProvenanceRange AllSources::GetContiguousRangeAround(Provenance at) const {
- const Origin &origin{MapToOrigin(at)};
- return {origin.start, origin.size()};
+ProvenanceRange AllSources::GetContiguousRangeAround(
+ ProvenanceRange range) const {
+ CHECK(IsValid(range));
+ const Origin &origin{MapToOrigin(range.LocalOffsetToProvenance(0))};
+ CHECK(origin.covers.Contains(range));
+ return origin.covers;
}
std::string AllSources::GetPath(Provenance at) const {
}
int AllSources::GetLineNumber(Provenance at) const {
- const SourceFile *source{GetSourceFile(at)};
- return source ? source->FindOffsetLineAndColumn(at).first : 0;
+ size_t offset{0};
+ const SourceFile *source{GetSourceFile(at, &offset)};
+ return source ? source->FindOffsetLineAndColumn(offset).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(ProvenanceRange r, const SourceFile &source)
+ : u{Inclusion{source}}, covers{r} {}
AllSources::Origin::Origin(
- size_t s, const SourceFile &included, ProvenanceRange from)
- : start{s}, u{Inclusion{included}}, replaces{from} {}
-AllSources::Origin::Origin(size_t s, ProvenanceRange def, ProvenanceRange use,
- const std::string &expansion)
- : start{s}, u{Macro{def, expansion}}, replaces{use} {}
-AllSources::Origin::Origin(size_t s, const std::string &text)
- : start{s}, u{CompilerInsertion{text}} {}
-
-size_t AllSources::Origin::size() const {
- return std::visit(
- visitors{[](const Inclusion &inc) { return inc.source.bytes(); },
- [](const Macro &mac) { return mac.expansion.size(); },
- [](const CompilerInsertion &ins) { return ins.text.size(); }},
- u);
-}
+ ProvenanceRange r, const SourceFile &included, ProvenanceRange from,
+ bool isModule)
+ : u{Inclusion{included, isModule}}, covers{r}, replaces{from} {}
+AllSources::Origin::Origin(ProvenanceRange r, ProvenanceRange def,
+ ProvenanceRange use, const std::string &expansion)
+ : u{Macro{def, expansion}}, covers{r}, replaces{use} {}
+AllSources::Origin::Origin(ProvenanceRange r, const std::string &text)
+ : u{CompilerInsertion{text}}, covers{r} {}
const char &AllSources::Origin::operator[](size_t n) const {
return std::visit(
}
const AllSources::Origin &AllSources::MapToOrigin(Provenance at) const {
- CHECK(at < bytes_);
+ CHECK(range_.Contains(at));
size_t low{0}, count{origin_.size()};
while (count > 1) {
size_t mid{low + (count >> 1)};
- if (origin_[mid].start > at) {
+ if (at < origin_[mid].covers.LocalOffsetToProvenance(0)) {
count = mid - low;
} else {
count -= mid - low;
low = mid;
}
}
- CHECK(at >= origin_[low].start);
- CHECK(low + 1 == origin_.size() || at < origin_[low + 1].start);
+ CHECK(origin_[low].covers.Contains(at));
return origin_[low];
}
void CookedSource::Marshal() {
CHECK(provenanceMap_.size() == buffer_.size());
- provenanceMap_.Put(allSources_->AddCompilerInsertion("EOF"));
+ provenanceMap_.Put(
+ allSources_->AddCompilerInsertion("(after end of source)"));
data_.resize(buffer_.size());
char *p{&data_[0]};
for (char ch : buffer_) {
}
buffer_.clear();
}
+
+void ProvenanceRange::Dump(std::ostream &o) const {
+ o << "[" << start_.offset() << ".." << (start_.offset() + bytes_ - 1) << "] ("
+ << bytes_ << " bytes)";
+}
+
+void OffsetToProvenanceMappings::Dump(std::ostream &o) const {
+ for (const ContiguousProvenanceMapping &m : provenanceMap_) {
+ size_t n{m.range.size()};
+ o << "offsets [" << m.start << ".." << (m.start + n - 1)
+ << "] -> provenances ";
+ m.range.Dump(o);
+ o << '\n';
+ }
+}
+
+void AllSources::Dump(std::ostream &o) const {
+ o << "AllSources range_ ";
+ range_.Dump(o);
+ o << '\n';
+ for (const Origin &m : origin_) {
+ o << " ";
+ m.covers.Dump(o);
+ o << " -> ";
+ std::visit(visitors{[&](const Inclusion &inc) {
+ if (inc.isModule) { o << "module "; }
+ o << "file " << inc.source.path();
+ },
+ [&](const Macro &mac) { o << "macro " << mac.expansion; },
+ [&](const CompilerInsertion &ins) {
+ o << "compiler " << ins.text;
+ }},
+ m.u);
+ o << '\n';
+ }
+}
+
+void CookedSource::Dump(std::ostream &o) const {
+ o << "CookedSource:\n";
+ allSources_->Dump(o);
+ o << "CookedSource::provenanceMap_:\n";
+ provenanceMap_.Dump(o);
+}
} // namespace parser
} // namespace Fortran
#define FORTRAN_PROVENANCE_H_
#include "char-buffer.h"
+#include "idioms.h"
#include "source.h"
#include <map>
#include <memory>
+#include <ostream>
#include <sstream>
#include <string>
#include <utility>
// The location of this original character to which a parsable character
// corresponds is its provenance.
//
-// Provenances are offsets into an unmaterialized marshaling of all of the
-// entire contents of the original source files, include files, macro
+// Provenances are offsets into an (unmaterialized) marshaling of the
+// entire contents of all the original source files, include files, macro
// expansions, &c. for each visit to each source. These origins of the
// original source characters constitute a forest whose roots are
// the original source files named on the compiler's command line.
-// We can describe provenances precisely by walking up this tree.
+// Given a Provenance, we can find the tree node that contains it in time
+// O(log(# of origins)), and describe the position precisely by walking
+// up the tree. (It would be possible, via a time/space trade-off, to
+// cap the time by the use of an intermediate table that would be indexed
+// by the upper bits of an offset, but that does not appear to be
+// necessary.)
+
+class Provenance {
+public:
+ Provenance() {}
+ Provenance(size_t offset) : offset_{offset} { CHECK(offset > 0); }
+ Provenance(const Provenance &that) = default;
+ Provenance(Provenance &&that) = default;
+ Provenance &operator=(const Provenance &that) = default;
+ Provenance &operator=(Provenance &&that) = default;
+ Provenance operator+(ptrdiff_t n) const {
+ CHECK(n > -static_cast<ptrdiff_t>(offset_));
+ return {offset_ + static_cast<size_t>(n)};
+ }
+ Provenance operator+(size_t n) const { return {offset_ + n}; }
+ bool operator<(Provenance that) const { return offset_ < that.offset_; }
+ bool operator<=(Provenance that) const { return offset_ <= that.offset_; }
+ bool operator==(Provenance that) const { return offset_ == that.offset_; }
+ size_t offset() const { return offset_; }
-using Provenance = size_t;
+private:
+ size_t offset_{0};
+};
-struct ProvenanceRange {
+class ProvenanceRange {
+public:
ProvenanceRange() {}
- ProvenanceRange(Provenance s, size_t n) : start{s}, bytes{n} {}
+ ProvenanceRange(Provenance s, size_t n) : start_{s}, bytes_{n} {
+ CHECK(n > 0);
+ }
ProvenanceRange(const ProvenanceRange &) = default;
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;
+ bool operator==(ProvenanceRange that) const {
+ return start_ == that.start_ && bytes_ == that.bytes_;
+ }
+
+ size_t size() const { return bytes_; }
+
+ bool Contains(Provenance at) const {
+ return start_ <= at && at < start_ + bytes_;
+ }
+
+ bool Contains(ProvenanceRange that) const {
+ return Contains(that.start_) && Contains(that.start_ + (that.bytes_ - 1));
+ }
+
+ size_t ProvenanceToLocalOffset(Provenance at) const {
+ CHECK(Contains(at));
+ return at.offset() - start_.offset();
+ }
+
+ Provenance LocalOffsetToProvenance(size_t at) const {
+ CHECK(at < bytes_);
+ return start_ + at;
+ }
+
+ Provenance NextAfter() const { return start_ + bytes_; }
+
+ ProvenanceRange Suffix(size_t at) const {
+ CHECK(at < bytes_);
+ return {start_ + at, bytes_ - at};
+ }
+
+ ProvenanceRange Prefix(size_t bytes) const {
+ CHECK(bytes > 0);
+ return {start_, std::min(bytes_, bytes)};
+ }
+
+ bool IsPredecessor(ProvenanceRange next) {
+ return start_ + bytes_ == next.start_;
}
- Provenance start{0};
- size_t bytes{0};
+ bool AnnexIfPredecessor(ProvenanceRange next) {
+ if (IsPredecessor(next)) {
+ bytes_ += next.bytes_;
+ return true;
+ }
+ return false;
+ }
+
+ void Dump(std::ostream &) const;
+
+private:
+ Provenance start_;
+ size_t bytes_{0};
};
+// Maps 0-based local offsets in some contiguous range (e.g., a token
+// sequence) to their provenances. Lookup time is on the order of
+// O(log(#of intervals with contiguous provenances)). As mentioned
+// above, this time could be capped via a time/space trade-off.
class OffsetToProvenanceMappings {
public:
OffsetToProvenanceMappings() {}
- size_t size() const { return bytes_; }
+ size_t size() const;
void clear();
void shrink_to_fit() { provenanceMap_.shrink_to_fit(); }
void Put(ProvenanceRange);
void Put(const OffsetToProvenanceMappings &);
ProvenanceRange Map(size_t at) const;
void RemoveLastBytes(size_t);
+ void Dump(std::ostream &) const;
private:
struct ContiguousProvenanceMapping {
ProvenanceRange range;
};
- size_t bytes_{0};
std::vector<ContiguousProvenanceMapping> provenanceMap_;
};
AllSources();
~AllSources();
- size_t size() const { return bytes_; }
+ size_t size() const { return range_.size(); }
const char &operator[](Provenance) const;
void PushSearchPathDirectory(std::string);
std::string PopSearchPathDirectory();
const SourceFile *Open(std::string path, std::stringstream *error);
- ProvenanceRange AddIncludedFile(const SourceFile &, ProvenanceRange);
+ ProvenanceRange AddIncludedFile(const SourceFile &, ProvenanceRange,
+ bool isModule = false);
ProvenanceRange AddMacroCall(
ProvenanceRange def, ProvenanceRange use, const std::string &expansion);
- ProvenanceRange AddCompilerInsertion(const std::string &);
+ ProvenanceRange AddCompilerInsertion(std::string);
+ bool IsValid(Provenance at) const { return range_.Contains(at); }
+ bool IsValid(ProvenanceRange range) const {
+ return range.size() > 0 && range_.Contains(range);
+ }
void Identify(std::ostream &, Provenance, const std::string &prefix) const;
- const SourceFile *GetSourceFile(Provenance) const;
- ProvenanceRange GetContiguousRangeAround(Provenance) const;
+ const SourceFile *GetSourceFile(Provenance, size_t *offset = nullptr) const;
+ ProvenanceRange GetContiguousRangeAround(ProvenanceRange) const;
std::string GetPath(Provenance) const; // __FILE__
int GetLineNumber(Provenance) const; // __LINE__
Provenance CompilerInsertionProvenance(char ch) const;
+ void Dump(std::ostream &) const;
private:
struct Inclusion {
const SourceFile &source;
+ bool isModule;
+ };
+ struct Module {
+ const SourceFile &source;
};
struct Macro {
ProvenanceRange definition;
};
struct Origin {
- Origin(size_t start, const SourceFile &);
- Origin(size_t start, const SourceFile &, ProvenanceRange);
- Origin(size_t start, ProvenanceRange def, ProvenanceRange use,
+ Origin(ProvenanceRange, const SourceFile &);
+ Origin(ProvenanceRange, const SourceFile &, ProvenanceRange,
+ bool isModule = false);
+ Origin(ProvenanceRange, ProvenanceRange def, ProvenanceRange use,
const std::string &expansion);
- Origin(size_t start, const std::string &);
+ Origin(ProvenanceRange, const std::string &);
- size_t size() const;
const char &operator[](size_t) const;
- size_t start;
std::variant<Inclusion, Macro, CompilerInsertion> u;
- ProvenanceRange replaces;
+ ProvenanceRange covers, replaces;
};
const Origin &MapToOrigin(Provenance) const;
std::vector<Origin> origin_;
- size_t bytes_{0};
+ ProvenanceRange range_;
std::map<char, Provenance> compilerInsertionProvenance_;
std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
std::vector<std::string> searchPath_;
void PutProvenanceMappings(const OffsetToProvenanceMappings &pm) {
provenanceMap_.Put(pm);
}
- void Marshal(); // marshalls all text into one contiguous block
+ void Marshal(); // marshals all text into one contiguous block
+ void Dump(std::ostream &) const;
private:
AllSources *allSources_;
CharPointerWithLength tok{that[at]};
size_t tokBytes{tok.size()};
for (size_t j{0}; j < tokBytes; ++j) {
- if (offset == provenance.bytes) {
+ if (offset == provenance.size()) {
offset = 0;
provenance = that.provenances_.Map(that.start_[at] + j);
}
- PutNextTokenChar(tok[j], provenance.start + offset++);
+ PutNextTokenChar(tok[j], provenance.LocalOffsetToProvenance(offset++));
}
CloseToken();
}
void TokenSequence::Put(const char *s, size_t bytes, Provenance provenance) {
for (size_t j{0}; j < bytes; ++j) {
- PutNextTokenChar(s[j], provenance++);
+ PutNextTokenChar(s[j], provenance + j);
}
CloseToken();
}
return {&char_[0], char_.size()};
}
-ProvenanceRange TokenSequence::GetProvenance(
+Provenance TokenSequence::GetProvenance(size_t token, size_t offset) const {
+ ProvenanceRange range{provenances_.Map(start_[token] + offset)};
+ return range.LocalOffsetToProvenance(0);
+}
+
+ProvenanceRange TokenSequence::GetProvenanceRange(
size_t token, size_t offset) const {
ProvenanceRange range{provenances_.Map(start_[token] + offset)};
- return {range.start, std::min(range.bytes, TokenBytes(token) - offset)};
+ return range.Prefix(TokenBytes(token) - offset);
}
} // namespace parser
} // namespace Fortran
void Put(const std::stringstream &, Provenance);
void EmitWithCaseConversion(CookedSource *) const;
std::string ToString() const;
- ProvenanceRange GetProvenance(size_t token, size_t offset = 0) const;
+ Provenance GetProvenance(size_t token, size_t offset = 0) const;
+ ProvenanceRange GetProvenanceRange(size_t token, size_t offset = 0) const;
private:
size_t TokenBytes(size_t token) const {
std::string progName{args.front()};
args.pop_front();
- bool dumpCookedChars{false};
+ bool dumpCookedChars{false}, dumpProvenance{false};
bool fixedForm{false};
bool backslashEscapes{true};
bool standard{false};
columns = 132;
} else if (flag == "-fdebug-dump-cooked-chars") {
dumpCookedChars = true;
+ } else if (flag == "-fdebug-dump-provenance") {
+ dumpProvenance = true;
} else if (flag == "-ed") {
enableOldDebugLines = true;
} else if (flag == "-I") {
columns = std::numeric_limits<int>::max();
cooked.Marshal();
+ if (dumpProvenance) {
+ cooked.Dump(std::cout);
+ }
+
Fortran::parser::ParseState state{cooked};
- state.set_inFixedForm(fixedForm);
- state.set_enableBackslashEscapesInCharLiterals(backslashEscapes);
- state.set_strictConformance(standard);
- state.set_columns(columns);
- state.set_enableOldDebugLines(enableOldDebugLines);
Fortran::parser::UserState ustate;
- state.set_userState(&ustate);
+ state.set_inFixedForm(fixedForm)
+ .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
+ .set_strictConformance(standard)
+ .set_columns(columns)
+ .set_enableOldDebugLines(enableOldDebugLines)
+ .set_userState(&ustate);
if (dumpCookedChars) {
while (std::optional<char> och{
return 0;
}
- std::optional<typename decltype(grammar)::resultType> result;
-#if 0
- for (int j = 0; j < 1000; ++j) {
- Fortran::parser::ParseState state1{state};
- result = grammar.Parse(&state1);
- if (!result) {
- std::cerr << "demo FAIL in timing loop\n";
- break;
- }
- }
-#endif
- result = grammar.Parse(&state);
+ std::optional<typename decltype(grammar)::resultType> result{
+ grammar.Parse(&state)};
if (result.has_value() && !state.anyErrorRecovery()) {
std::cout << "demo PASS\n" << *result << '\n';
} else {