void CharBuffer::Put(const std::string &str) { Put(str.data(), str.size()); }
+std::string CharBuffer::Marshal() const {
+ std::string result;
+ std::size_t bytes{bytes_};
+ result.reserve(bytes);
+ for (const Block &block : blocks_) {
+ std::size_t chunk{std::min(bytes, Block::capacity)};
+ for (std::size_t j{0}; j < chunk; ++j) {
+ result += block.data[j];
+ }
+ bytes -= chunk;
+ }
+ result.shrink_to_fit();
+ CHECK(result.size() == bytes_);
+ return result;
+}
+
+std::string CharBuffer::MarshalNormalized() const {
+ std::string result;
+ std::size_t bytes{bytes_};
+ result.reserve(bytes + 1 /* for terminal line feed */);
+ char ch{'\0'};
+ for (const Block &block : blocks_) {
+ std::size_t chunk{std::min(bytes, Block::capacity)};
+ for (std::size_t j{0}; j < chunk; ++j) {
+ ch = block.data[j];
+ if (ch != '\r') {
+ result += ch;
+ }
+ }
+ bytes -= chunk;
+ }
+ if (ch != '\n') {
+ result += '\n';
+ }
+ result.shrink_to_fit();
+ return result;
+}
+
} // namespace Fortran::parser
void Put(const std::string &);
void Put(char x) { Put(&x, 1); }
+ std::string Marshal() const;
+
+ // Removes carriage returns ('\r') and ensures a final line feed ('\n').
+ std::string MarshalNormalized() const;
+
private:
struct Block {
static constexpr std::size_t capacity{1 << 20};
char data[capacity];
};
-public:
- class iterator {
- public:
- iterator() {}
- iterator(std::forward_list<Block>::const_iterator block, int offset)
- : block_{block}, offset_{offset} {}
- iterator(const iterator &that)
- : block_{that.block_}, offset_{that.offset_} {}
- iterator &operator=(const iterator &that) {
- block_ = that.block_;
- offset_ = that.offset_;
- return *this;
- }
- const char &operator*() const { return block_->data[offset_]; }
- iterator &operator++(/*++prefix*/) {
- if (++offset_ == Block::capacity) {
- ++block_;
- offset_ = 0;
- }
- return *this;
- }
- iterator operator++(int /*postfix++*/) {
- iterator result{*this};
- ++*this;
- return result;
- }
- iterator &operator+=(std::size_t n) {
- while (n >= Block::capacity - offset_) {
- n -= Block::capacity - offset_;
- offset_ = 0;
- ++block_;
- }
- offset_ += n;
- return *this;
- }
- bool operator==(const iterator &that) const {
- return block_ == that.block_ && offset_ == that.offset_;
- }
- bool operator!=(const iterator &that) const {
- return block_ != that.block_ || offset_ != that.offset_;
- }
-
- private:
- std::forward_list<Block>::const_iterator block_;
- int offset_;
- };
-
- iterator begin() const { return iterator(blocks_.begin(), 0); }
- iterator end() const {
- int offset = LastBlockOffset();
- if (offset != 0 || lastBlockEmpty_) {
- return iterator(last_, offset);
- }
- return iterator(blocks_.end(), 0);
- }
-
-private:
int LastBlockOffset() const { return bytes_ % Block::capacity; }
std::forward_list<Block> blocks_;
std::forward_list<Block>::iterator last_{blocks_.end()};
text_);
}
-ProvenanceRange Message::GetProvenanceRange(const CookedSource &cooked) const {
+std::optional<ProvenanceRange> Message::GetProvenanceRange(
+ const CookedSource &cooked) const {
return std::visit(common::visitors{[&](const CharBlock &cb) {
return cooked.GetProvenanceRange(cb);
},
- [](const ProvenanceRange &pr) { return pr; }},
+ [](const ProvenanceRange &pr) {
+ return std::optional<ProvenanceRange>{pr};
+ }},
location_);
}
void Message::Emit(
std::ostream &o, const CookedSource &cooked, bool echoSourceLine) const {
- ProvenanceRange provenanceRange{GetProvenanceRange(cooked)};
+ std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(cooked)};
std::string text;
if (IsFatal()) {
text += "error: ";
}
text += ToString();
- AllSources &sources{cooked.allSources()};
+ const AllSources &sources{cooked.allSources()};
sources.EmitMessage(o, provenanceRange, text, echoSourceLine);
if (attachmentIsContext_) {
for (const Message *context{attachment_.get()}; context != nullptr;
context = context->attachment_.get()) {
- ProvenanceRange contextProvenance{context->GetProvenanceRange(cooked)};
+ std::optional<ProvenanceRange> contextProvenance{
+ context->GetProvenanceRange(cooked)};
text = "in the context: ";
text += context->ToString();
// TODO: don't echo the source lines of a context when it's the
bool SortBefore(const Message &that) const;
bool IsFatal() const;
std::string ToString() const;
- ProvenanceRange GetProvenanceRange(const CookedSource &) const;
+ std::optional<ProvenanceRange> GetProvenanceRange(const CookedSource &) const;
void Emit(
std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
public:
// TODO: Add a constructor for parsing a normalized module file.
ParseState(const CookedSource &cooked)
- : p_{&cooked[0]}, limit_{p_ + cooked.size()} {}
+ : p_{&cooked.data().front()}, limit_{&cooked.data().back() + 1} {}
ParseState(const ParseState &that)
: p_{that.p_}, limit_{that.limit_}, context_{that.context_},
userState_{that.userState_}, inFixedForm_{that.inFixedForm_},
std::stringstream fileError;
const SourceFile *sourceFile;
+ AllSources &allSources{cooked_.allSources()};
if (path == "-") {
- sourceFile = allSources_.ReadStandardInput(&fileError);
+ sourceFile = allSources.ReadStandardInput(&fileError);
} else {
- sourceFile = allSources_.Open(path, &fileError);
+ sourceFile = allSources.Open(path, &fileError);
}
if (sourceFile == nullptr) {
- ProvenanceRange range{allSources_.AddCompilerInsertion(path)};
+ ProvenanceRange range{allSources.AddCompilerInsertion(path)};
MessageFormattedText msg("%s"_err_en_US, fileError.str().data());
messages_.Put(Message{range, std::move(msg)});
return;
}
if (sourceFile->bytes() == 0) {
- ProvenanceRange range{allSources_.AddCompilerInsertion(path)};
+ ProvenanceRange range{allSources.AddCompilerInsertion(path)};
messages_.Put(Message{range, "file is empty"_err_en_US});
return;
}
// working directory, we don't want to accidentally read another foo.f
// from another directory that's on the search path.
for (const auto &path : options.searchDirectories) {
- allSources_.PushSearchPathDirectory(path);
+ allSources.PushSearchPathDirectory(path);
}
- Preprocessor preprocessor{allSources_};
+ Preprocessor preprocessor{allSources};
for (const auto &predef : options.predefinitions) {
if (predef.second.has_value()) {
preprocessor.Define(predef.first, *predef.second);
prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
}
ProvenanceRange range{
- allSources_.AddIncludedFile(*sourceFile, ProvenanceRange{})};
+ allSources.AddIncludedFile(*sourceFile, ProvenanceRange{})};
prescanner.Prescan(range);
cooked_.Marshal();
}
void EmitMessage(std::ostream &o, const char *at, const std::string &message,
bool echoSourceLine = false) const {
- allSources_.EmitMessage(
- o, cooked_.GetProvenanceRange(at).start(), message, echoSourceLine);
+ cooked_.allSources().EmitMessage(
+ o, cooked_.GetProvenanceRange(CharBlock(at)), message, echoSourceLine);
}
bool ForTesting(std::string path, std::ostream &);
private:
Options options_;
- AllSources allSources_;
- CookedSource cooked_{allSources_};
+ CookedSource cooked_;
Messages messages_;
bool consumedWholeFile_{false};
const char *finalRestingPlace_{nullptr};
}
void Prescanner::Say(Message &&message) {
- CHECK(cooked_.IsValid(message.GetProvenanceRange(cooked_)));
+ std::optional<ProvenanceRange> range{message.GetProvenanceRange(cooked_)};
+ CHECK(!range.has_value() || cooked_.IsValid(*range));
messages_.Put(std::move(message));
}
return covers;
}
-void AllSources::EmitMessage(std::ostream &o, ProvenanceRange range,
- const std::string &message, bool echoSourceLine) const {
- CHECK(IsValid(range));
- const Origin &origin{MapToOrigin(range.start())};
+void AllSources::EmitMessage(std::ostream &o,
+ const std::optional<ProvenanceRange> &range, const std::string &message,
+ bool echoSourceLine) const {
+ if (!range.has_value()) {
+ o << message << '\n';
+ return;
+ }
+ CHECK(IsValid(*range));
+ const Origin &origin{MapToOrigin(range->start())};
std::visit(
common::visitors{
[&](const Inclusion &inc) {
o << inc.source.path();
- std::size_t offset{origin.covers.MemberOffset(range.start())};
+ std::size_t offset{origin.covers.MemberOffset(range->start())};
std::pair<int, int> pos{inc.source.FindOffsetLineAndColumn(offset)};
o << ':' << pos.first << ':' << pos.second;
o << ": " << message << '\n';
o << (ch == '\t' ? '\t' : ' ');
}
o << '^';
- if (range.size() > 1) {
- auto last{range.start() + range.size() - 1};
+ if (range->size() > 1) {
+ auto last{range->start() + range->size() - 1};
if (&MapToOrigin(last) == &origin) {
auto endOffset{origin.covers.MemberOffset(last)};
auto endPos{inc.source.FindOffsetLineAndColumn(endOffset)};
if (echoSourceLine) {
o << "that expanded to:\n " << mac.expansion << "\n ";
for (std::size_t j{0};
- origin.covers.OffsetMember(j) < range.start(); ++j) {
+ origin.covers.OffsetMember(j) < range->start(); ++j) {
o << (mac.expansion[j] == '\t' ? '\t' : ' ');
}
o << "^\n";
return origin_[low];
}
-ProvenanceRange CookedSource::GetProvenanceRange(CharBlock cookedRange) const {
+CookedSource::CookedSource() {}
+CookedSource::~CookedSource() {}
+
+std::optional<ProvenanceRange> CookedSource::GetProvenanceRange(
+ CharBlock cookedRange) const {
+ if (!IsValid(cookedRange)) {
+ return std::nullopt;
+ }
ProvenanceRange first{provenanceMap_.Map(cookedRange.begin() - &data_[0])};
if (cookedRange.size() <= first.size()) {
return first.Prefix(cookedRange.size());
}
ProvenanceRange last{provenanceMap_.Map(cookedRange.end() - &data_[0])};
- return {first.start(), last.start() - first.start()};
+ return {ProvenanceRange{first.start(), last.start() - first.start()}};
}
void CookedSource::Marshal() {
CHECK(provenanceMap_.size() == buffer_.size());
provenanceMap_.Put(allSources_.AddCompilerInsertion("(after end of source)"));
- data_.resize(buffer_.size());
- char *p{&data_[0]};
- for (char ch : buffer_) {
- *p++ = ch;
- }
+ data_ = buffer_.Marshal();
buffer_.clear();
}
#include <cstddef>
#include <map>
#include <memory>
+#include <optional>
#include <ostream>
#include <sstream>
#include <string>
bool IsValid(ProvenanceRange range) const {
return range.size() > 0 && range_.Contains(range);
}
- void EmitMessage(std::ostream &, ProvenanceRange, const std::string &message,
- bool echoSourceLine = false) const;
+ void EmitMessage(std::ostream &, const std::optional<ProvenanceRange> &,
+ const std::string &message, bool echoSourceLine = false) const;
const SourceFile *GetSourceFile(
Provenance, std::size_t *offset = nullptr) const;
ProvenanceRange GetContiguousRangeAround(ProvenanceRange) const;
class CookedSource {
public:
- explicit CookedSource(AllSources &sources) : allSources_{sources} {}
+ CookedSource();
+ ~CookedSource();
- std::size_t size() const { return data_.size(); }
- const char &operator[](std::size_t n) const { return data_[n]; }
- const char &at(std::size_t n) const { return data_.at(n); }
-
- AllSources &allSources() const { return allSources_; }
+ AllSources &allSources() { return allSources_; }
+ const AllSources &allSources() const { return allSources_; }
+ const std::string &data() const { return data_; }
bool IsValid(const char *p) const {
return p >= &data_.front() && p <= &data_.back() + 1;
bool IsValid(CharBlock range) const {
return !range.empty() && IsValid(range.begin()) && IsValid(range.end() - 1);
}
- bool IsValid(Provenance p) const { return allSources_.IsValid(p); }
bool IsValid(ProvenanceRange r) const { return allSources_.IsValid(r); }
- ProvenanceRange GetProvenanceRange(CharBlock) const;
+ std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
void Put(const char *data, std::size_t bytes) { buffer_.Put(data, bytes); }
void Put(char ch) { buffer_.Put(&ch, 1); }
buffer_.Put(&ch, 1);
provenanceMap_.Put(ProvenanceRange{p, 1});
}
+ void PutProvenance(Provenance p) { provenanceMap_.Put(ProvenanceRange{p}); }
+ void PutProvenance(ProvenanceRange pr) { provenanceMap_.Put(pr); }
void PutProvenanceMappings(const OffsetToProvenanceMappings &pm) {
provenanceMap_.Put(pm);
}
- void Marshal(); // marshals all text into one contiguous block
- std::vector<char> MoveChars() { return std::move(data_); }
+ void Marshal(); // marshals text into one contiguous block
std::ostream &Dump(std::ostream &) const;
private:
- AllSources &allSources_;
+ AllSources allSources_;
CharBuffer buffer_; // before Marshal()
- std::vector<char> data_; // all of it, prescanned and preprocessed
+ std::string data_; // all of it, prescanned and preprocessed
OffsetToProvenanceMappings provenanceMap_;
};
if (bytes_ == 0) {
// empty file
content_ = nullptr;
- return true;
+ } else {
+ buffer_ = buffer.MarshalNormalized(); // no '\r' chars, ensure final '\n'
+ content_ = buffer_.data();
+ bytes_ = buffer_.size();
+ lineStart_ = FindLineStarts(content_, bytes_);
}
-
- char *contig{new char[bytes_ + 1 /* for extra newline if needed */]};
- content_ = contig;
- char *to{contig};
- for (char ch : buffer) {
- if (ch != '\r') {
- *to++ = ch;
- }
- }
- if (to == contig || to[-1] != '\n') {
- *to++ = '\n'; // supply a missing terminal newline
- }
- bytes_ = to - contig;
- lineStart_ = FindLineStarts(content_, bytes_);
return true;
}
const char *content_{nullptr};
std::size_t bytes_{0};
std::vector<std::size_t> lineStart_;
+ std::string buffer_;
};
} // namespace Fortran::parser
return false;
}
auto &modSymbol{*it->second};
- modSymbol.scope()->set_chars(parsing.cooked().MoveChars());
+ modSymbol.scope()->set_chars(parsing.cooked().data());
modSymbol.set(Symbol::Flag::ModFile);
return true;
}
// For modules read from module files, this is the stream of characters
// that are referenced by SourceName objects.
- void set_chars(std::vector<char> chars) {
+ void set_chars(std::string &&chars) {
chars_ = std::move(chars);
}
std::list<Scope> children_;
mapType symbols_;
std::list<DerivedTypeSpec> derivedTypeSpecs_;
- std::vector<char> chars_;
+ std::string chars_;
// Storage for all Symbols. Every Symbol is in allSymbols and every Symbol*
// or Symbol& points to one in there.