set(CMAKE_INSTALL_RPATH "${GCC}/lib64")
set(CMAKE_BUILD_WITH_INSTALL_RPATH true)
-project(f18)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -std=c++17")
set(SOURCES_F18
tools/f18/f18.cc
lib/parser/idioms.cc
lib/parser/message.cc
lib/parser/parse-tree.cc
- lib/parser/position.cc
lib/parser/preprocessor.cc
lib/parser/prescan.cc
lib/parser/provenance.cc
#include "idioms.h"
#include "message.h"
#include "parse-state.h"
-#include "position.h"
+#include "provenance.h"
#include <cstring>
#include <functional>
#include <list>
constexpr FailParser(const FailParser &) = default;
constexpr explicit FailParser(const char *str) : str_{str} {}
std::optional<A> Parse(ParseState *state) const {
- state->messages()->Add(Message{state->position(), str_, state->context()});
+ state->PutMessage(str_);
return {};
}
}
// Both alternatives failed. Retain the state (and messages) from the
// alternative parse that went the furthest.
- if (state->position() <= paState.position()) {
+ if (state->GetLocation() <= paState.GetLocation()) {
messages.Annex(paState.messages());
state->swap(paState);
} else {
constexpr ManyParser(const PA &parser) : parser_{parser} {}
std::optional<resultType> Parse(ParseState *state) const {
resultType result;
- Position at{state->position()};
+ auto at = state->GetLocation();
while (std::optional<paType> x{parser_.Parse(state)}) {
result.emplace_back(std::move(*x));
- if (state->position() <= at) {
+ if (state->GetLocation() <= at) {
break; // no forward progress, don't loop
}
- at = state->position();
+ at = state->GetLocation();
}
return {std::move(result)};
}
constexpr SomeParser(const SomeParser &) = default;
constexpr SomeParser(const PA &parser) : parser_{parser} {}
std::optional<resultType> Parse(ParseState *state) const {
- Position start{state->position()};
+ auto start = state->GetLocation();
if (std::optional<paType> first{parser_.Parse(state)}) {
resultType result;
result.emplace_back(std::move(*first));
- if ((state->position() > start)) {
+ if ((state->GetLocation() > start)) {
result.splice(result.end(), *many(parser_).Parse(state));
}
return {std::move(result)};
constexpr SkipManyParser(const SkipManyParser &) = default;
constexpr SkipManyParser(const PA &parser) : parser_{parser} {}
std::optional<Success> Parse(ParseState *state) const {
- for (Position at{state->position()};
- parser_.Parse(state) && state->position() > at;
- at = state->position()) {
+ for (auto at = state->GetLocation();
+ parser_.Parse(state) && state->GetLocation() > at;
+ at = state->GetLocation()) {
}
return {Success{}};
}
state->Advance();
return ch;
}
- state->messages()->Add(
- Message{state->position(), "end of file", state->context()});
+ state->PutMessage("end of file");
return {};
}
} rawNextChar;
if (state->strictConformance()) {
return {};
}
- Position at{state->position()};
+ auto at = state->GetLocation();
auto result = parser_.Parse(state);
if (result) {
if (state->warnOnNonstandardUsage()) {
- state->messages()->Add(
- Message{at, "nonstandard usage", state->context()});
+ state->PutMessage(at, "nonstandard usage");
}
}
return result;
if (state->strictConformance()) {
return {};
}
- Position at{state->position()};
+ auto at = state->GetLocation();
auto result = parser_.Parse(state);
if (result) {
if (state->warnOnDeprecatedUsage()) {
- state->messages()->Add(
- Message{at, "deprecated usage", state->context()});
+ state->PutMessage(at, "deprecated usage");
}
}
return result;
using resultType = int;
constexpr GetColumn() {}
static std::optional<int> Parse(ParseState *state) {
- return {state->position().column()};
+ return {state->column()};
}
} getColumn;
-constexpr struct GetPosition {
- using resultType = Position;
- constexpr GetPosition() {}
- static std::optional<Position> Parse(ParseState *state) {
- return {state->position()};
+constexpr struct GetProvenance {
+ using resultType = Provenance;
+ constexpr GetProvenance() {}
+ static std::optional<Provenance> Parse(ParseState *state) {
+ return {state->GetProvenance()};
}
-} getPosition;
+} getProvenance;
} // namespace parser
} // namespace Fortran
}
void CharBuffer::Put(const std::string &str) { Put(str.data(), str.size()); }
-
-void CharBuffer::CopyToContiguous(char *data) {
- char *to{data};
- for (char ch : *this) {
- *to++ = ch;
- }
- CHECK(to == data + bytes_);
-}
} // namespace parser
} // namespace Fortran
return *this;
}
- size_t bytes() const { return bytes_; }
+ size_t size() const { return bytes_; }
void clear() {
blocks_.clear();
void Put(const char *data, size_t n);
void Put(const std::string &);
void Put(char x) { Put(&x, 1); }
- void CopyToContiguous(char *data);
private:
struct Block {
using resultType = char;
static std::optional<char> Parse(ParseState *state) {
if (state->inCharLiteral() && state->inFortran() && state->inFixedForm() &&
- state->position().column() <= state->columns()) {
+ state->column() <= state->columns()) {
if (std::optional<char> ch{state->GetNextRawChar()}) {
if (*ch == '\n') {
- state->AdvancePositionForPadding();
+ state->AdvanceColumnForPadding();
return {' '};
}
}
static inline bool InRightMargin(const ParseState &state) {
if (state.inFortran() && state.inFixedForm() &&
- state.position().column() > state.columns() &&
- !state.tabInCurrentLine()) {
+ state.column() > state.columns() && !state.tabInCurrentLine()) {
if (std::optional<char> ch{state.GetNextRawChar()}) {
return *ch != '\n';
}
constexpr AtFixedFormColumn(const AtFixedFormColumn &) {}
static std::optional<Success> Parse(ParseState *state) {
if (state->inFortran() && state->inFixedForm() && !state->IsAtEnd() &&
- state->position().column() == col) {
+ state->column() == col) {
return {Success{}};
}
return {};
constexpr AtColumn() {}
constexpr AtColumn(const AtColumn &) {}
static std::optional<Success> Parse(ParseState *state) {
- if (!state->IsAtEnd() && state->position().column() == col) {
+ if (!state->IsAtEnd() && state->column() == col) {
return {Success{}};
}
return {};
};
static inline bool AtOldDebugLineMarker(const ParseState &state) {
- if (state.inFortran() && state.inFixedForm() &&
- state.position().column() == 1) {
+ if (state.inFortran() && state.inFixedForm() && state.column() == 1) {
if (std::optional<char> ch{state.GetNextRawChar()}) {
return toupper(*ch) == 'D';
}
static std::optional<Success> Parse(ParseState *state) {
if (std::optional<char> ch{state->GetNextRawChar()}) {
if (*ch == ' ' || *ch == '\t' ||
- (toupper(*ch) == 'D' && state->position().column() == 1 &&
+ (toupper(*ch) == 'D' && state->column() == 1 &&
state->enableOldDebugLines() && state->inFortran() &&
state->inFixedForm())) {
state->Advance();
#include "basic-parsers.h"
#include "cooked-chars.h"
#include "idioms.h"
-#include "position.h"
+#include "provenance.h"
#include <cctype>
#include <cstring>
#include <functional>
constexpr CharPredicateGuardParser(bool (*f)(char), const char *msg)
: predicate_{f}, message_{msg} {}
std::optional<char> Parse(ParseState *state) const {
- Position at{state->position()};
+ auto at = state->GetLocation();
if (std::optional<char> result{cookedNextChar.Parse(state)}) {
if (predicate_(*result)) {
return result;
}
}
- state->messages()->Add(Message{at, message_, state->context()});
+ state->PutMessage(at, message_);
return {};
}
using resultType = char;
constexpr CharMatch() {}
static std::optional<char> Parse(ParseState *state) {
- Position at{state->position()};
+ auto at = state->GetLocation();
std::optional<char> result{cookedNextChar.Parse(state)};
if (result && *result != good) {
result.reset();
}
if (!result) {
- state->messages()->Add(Message{at, good, state->context()});
+ state->PutMessage(at, "expected '"s + good + '\'');
}
return {result};
}
: str_{str}, length_{n} {}
constexpr TokenStringMatch(const char *str) : str_{str} {}
std::optional<Success> Parse(ParseState *state) const {
- Position at{state->position()};
+ auto at = state->GetLocation();
if (!spaces.Parse(state)) {
return {};
}
} else if (*ch == tolower(*p)) {
ch.reset();
} else {
- state->messages()->Add(
- Message{at, "expected '"s + str_ + '\'', state->context()});
+ state->PutMessage(at, "expected '"s + str_ + '\'');
return {};
}
}
};
using resultType = Result;
static std::optional<Result> Parse(ParseState *state) {
- Position at{state->position()};
+ auto at = state->GetLocation();
std::optional<char> och{cookedNextChar.Parse(state)};
if (!och.has_value()) {
return {};
}
char ch{*och};
if (ch == '\n') {
- state->messages()->Add(
- Message{at, "unclosed character constant", state->context()});
+ state->PutMessage(at, "unclosed character constant");
return {};
}
if (ch != '\\' || !state->enableBackslashEscapesInCharLiterals()) {
case '"':
case '\'':
case '\\': return {Result::Escaped(ch)};
- case '\n':
- state->messages()->Add(
- Message{at, "unclosed character constant", state->context()});
- return {};
+ case '\n': state->PutMessage(at, "unclosed character constant"); return {};
default:
if (IsOctalDigit(ch)) {
ch -= '0';
}
}
} else {
- state->messages()->Add(
- Message{at, "bad escaped character", state->context()});
+ state->PutMessage(at, "bad escaped character");
}
return {Result::Escaped(ch)};
}
return {};
}
- Position at{state->position()};
+ auto at = state->GetLocation();
std::string content;
while (true) {
if (!(ch = cookedNextChar.Parse(state))) {
}
if (content.empty()) {
- state->messages()->Add(
- Message{at, "no digit in BOZ literal", state->context()});
+ state->PutMessage(at, "no digit in BOZ literal");
return {};
}
for (auto digit : content) {
digit = HexadecimalDigitValue(digit);
if ((digit >> *shift) > 0) {
- state->messages()->Add(
- Message{at, "bad digit in BOZ literal", state->context()});
+ state->PutMessage(at, "bad digit in BOZ literal");
return {};
}
std::uint64_t was{value};
value <<= *shift;
if ((value >> *shift) != was) {
- state->messages()->Add(
- Message{at, "excessive digits in BOZ literal", state->context()});
+ state->PutMessage(at, "excessive digits in BOZ literal");
return {};
}
value |= digit;
using resultType = std::uint64_t;
static std::optional<std::uint64_t> Parse(ParseState *state) {
static constexpr auto getDigit = attempt(digit);
- Position at{state->position()};
+ auto at = state->GetLocation();
std::optional<char> firstDigit{getDigit.Parse(state)};
if (!firstDigit) {
return {};
value += digitValue;
}
if (overflow) {
- state->messages()->Add(
- Message{at, "overflow in decimal literal", state->context()});
+ state->PutMessage(at, "overflow in decimal literal");
}
return {value};
}
if (!spaces.Parse(state)) {
return {};
}
- Position at{state->position()};
+ auto at = state->GetLocation();
std::optional<std::uint64_t> charCount{DigitString{}.Parse(state)};
if (!charCount || *charCount < 1) {
return {};
for (auto j = *charCount; j-- > 0;) {
std::optional<char> ch{cookedNextChar.Parse(state)};
if (!ch || !isprint(*ch)) {
- state->messages()->Add(Message{at,
- "insufficient or bad characters in Hollerith", state->context()});
+ state->PutMessage(at, "insufficient or bad characters in Hollerith");
state->set_inCharLiteral(false);
return {};
}
if (auto context = state->context()) {
std::cout << *context;
}
- std::cout << state->position() << ' ' << std::string{str_, length_} << '\n';
+ state->GetAllSources().Identify(std::cout, state->GetProvenance(), "");
+ std::cout << " parser debug: " << std::string{str_, length_} << '\n';
return {Success{}};
}
using statementConstructor = construct<Statement<typename PA::resultType>>;
template<typename PA> inline constexpr auto unterminatedStatement(const PA &p) {
- return skipMany("\n"_tok) >> statementConstructor<PA>{}(getPosition,
+ return skipMany("\n"_tok) >> statementConstructor<PA>{}(getProvenance,
maybe(label), isLabelOk, spaces >> p);
}
statement(indirect(dataStmt))) ||
extension(construct<ExecutionPartConstruct>{}(
statement(indirect(Parser<NamelistStmt>{})))),
- construct<ExecutionPartConstruct>{}(executionPartErrorRecovery)));
+ construct<ExecutionPartConstruct>{}(executionPartErrorRecovery)))
// R509 execution-part -> executable-construct [execution-part-construct]...
constexpr auto executionPart =
// "vector-subscript" is deferred to semantic analysis.
TYPE_PARSER(construct<SectionSubscript>{}(Parser<SubscriptTriplet>{}) ||
construct<SectionSubscript>{}(vectorSubscript) ||
- construct<SectionSubscript>{}(subscript));
+ construct<SectionSubscript>{}(subscript))
// R921 subscript-triplet -> [subscript] : [subscript] [: stride]
TYPE_PARSER(construct<SubscriptTriplet>{}(
return {FunctionReference{std::move(call.value())}};
}
}
- state->messages()->Add(
- Message{state->position(), "expected (arguments)", state->context()});
+ state->PutMessage("expected (arguments)");
}
state->PopContext();
return {};
-};
+}
// R1521 call-stmt -> CALL procedure-designator [( [actual-arg-spec-list] )]
template<> std::optional<CallStmt> Parser<CallStmt>::Parse(ParseState *state) {
}
}
return {};
-};
+}
// R1522 procedure-designator ->
// procedure-name | proc-component-ref | data-ref % binding-name
namespace Fortran {
namespace parser {
-std::ostream &operator<<(std::ostream &o, const Message &msg) {
- if (msg.context()) {
- o << *msg.context();
+void Message::Emit(std::ostream &o, const AllSources &sources) const {
+ if (context_) {
+ context_->Emit(o, sources);
}
- o << "at line " << msg.position().lineNumber();
- int column = msg.position().column();
- if (column > 0) {
- o << "(column " << column << ")";
- }
- o << ": " << msg.message() << '\n';
- return o;
+ sources.Identify(o, provenance_, "");
+ o << ": " << message_ << '\n';
}
-std::ostream &operator<<(std::ostream &o, const Messages &ms) {
- for (const auto &msg : ms) {
- o << msg;
+void Messages::Emit(std::ostream &o, const AllSources &sources) const {
+ for (const auto &msg : messages_) {
+ msg.Emit(o, sources);
}
- return o;
}
} // namespace parser
} // namespace Fortran
// Supports nested contextualization.
#include "idioms.h"
-#include "position.h"
+#include "provenance.h"
#include <forward_list>
#include <memory>
#include <optional>
Message(const Message &) = default;
Message(Message &&) = default;
- Message(Position pos, const std::string &msg, MessageContext ctx = nullptr)
- : position_{pos}, message_{msg}, context_{ctx} {}
- Message(Position pos, std::string &&msg, MessageContext ctx = nullptr)
- : position_{pos}, message_{std::move(msg)}, context_{ctx} {}
- Message(Position pos, const char *msg, MessageContext ctx = nullptr)
- : position_{pos}, message_{msg}, context_{ctx} {}
- Message(Position pos, char ch, MessageContext ctx = nullptr)
- : position_{pos}, message_{"expected '"s + ch + '\''}, context_{ctx} {}
+ Message(Provenance at, const std::string &msg, MessageContext ctx = nullptr)
+ : provenance_{at}, message_{msg}, context_{ctx} {}
+ Message(Provenance at, std::string &&msg, MessageContext ctx = nullptr)
+ : provenance_{at}, message_{std::move(msg)}, context_{ctx} {}
+ Message(Provenance at, const char *msg, MessageContext ctx = nullptr)
+ : provenance_{at}, message_{msg}, context_{ctx} {}
Message &operator=(const Message &that) = default;
Message &operator=(Message &&that) = default;
- Position position() const { return position_; }
+ bool operator<(const Message &that) const {
+ return provenance_ < that.provenance_;
+ }
+
+ Provenance provenance() const { return provenance_; }
std::string message() const { return message_; }
MessageContext context() const { return context_; }
- bool operator<(const Message &that) const {
- return position_ < that.position_;
- }
+ void Emit(std::ostream &, const AllSources &) const;
private:
- Position position_;
+ Provenance provenance_;
std::string message_;
MessageContext context_;
};
const_iterator cbegin() const { return messages_.cbegin(); }
const_iterator cend() const { return messages_.cend(); }
- void Add(Message &&m) {
+ void Put(Message &&m) {
if (messages_.empty()) {
messages_.emplace_front(std::move(m));
last_ = messages_.begin();
}
}
+ void Emit(std::ostream &, const AllSources &) const;
+
private:
list_type messages_;
iterator last_; // valid iff messages_ nonempty
};
-
-std::ostream &operator<<(std::ostream &, const Message &);
-std::ostream &operator<<(std::ostream &, const Messages &);
} // namespace parser
} // namespace Fortran
#endif // FORTRAN_MESSAGE_H_
#define FORTRAN_PARSE_STATE_H_
// Defines the ParseState type used as the argument for every parser's
-// Parse member or static function. Tracks position, context, accumulated
-// messages, and an arbitrary UserState instance for parsing attempts.
-// Must be efficient to duplicate and assign for backtracking and recovery
-// during parsing!
+// Parse member or static function. Tracks source provenance, context,
+// accumulated messages, and an arbitrary UserState instance for parsing
+// attempts. Must be efficient to duplicate and assign for backtracking
+// and recovery during parsing!
#include "idioms.h"
#include "message.h"
-#include "position.h"
+#include "provenance.h"
#include <cstring>
#include <list>
#include <memory>
class ParseState {
public:
- ParseState() {}
- ParseState(const char *str) : p_{str}, remaining_{std::strlen(str)} {}
- ParseState(const char *str, size_t bytes) : p_{str}, remaining_{bytes} {}
+ ParseState(const CookedSource &cooked)
+ : cooked_{cooked}, p_{&cooked[0]}, remaining_{cooked.size()} {}
ParseState(const ParseState &that)
- : p_{that.p_}, remaining_{that.remaining_}, position_{that.position_},
+ : cooked_{that.cooked_}, p_{that.p_},
+ remaining_{that.remaining_}, column_{that.column_},
userState_{that.userState_}, inCharLiteral_{that.inCharLiteral_},
inFortran_{that.inFortran_}, inFixedForm_{that.inFixedForm_},
enableOldDebugLines_{that.enableOldDebugLines_}, columns_{that.columns_},
anyErrorRecovery_{that.anyErrorRecovery_}, prescanned_{that.prescanned_} {
}
ParseState(ParseState &&that)
- : p_{that.p_}, remaining_{that.remaining_}, position_{that.position_},
+ : 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_},
std::memcpy(&that, buffer, bytes);
}
- Position position() const { return position_; }
-
bool anyErrorRecovery() const { return anyErrorRecovery_; }
void set_anyErrorRecovery() { anyErrorRecovery_ = true; }
UserState *userState() const { return userState_; }
void set_userState(UserState *u) { userState_ = u; }
+ int column() const { return column_; }
Messages *messages() { return &messages_; }
MessageContext context() const { return context_; }
return was;
}
- void PushContext(const std::string &str) {
- context_ = std::make_shared<Message>(position_, str, context_);
- }
- void PushContext(std::string &&str) {
- context_ = std::make_shared<Message>(position_, std::move(str), context_);
- }
- void PushContext(const char *str) {
- context_ = std::make_shared<Message>(position_, str, context_);
- }
-
- void PopContext() {
- if (context_) {
- context_ = context_->context();
- }
- }
-
bool inCharLiteral() const { return inCharLiteral_; }
bool set_inCharLiteral(bool yes) {
bool was{inCharLiteral_};
int skippedNewLines() const { return skippedNewLines_; }
void set_skippedNewLines(int n) { skippedNewLines_ = n; }
- bool prescanned() const { return prescanned_; }
- void set_prescanned(bool yes) { prescanned_ = yes; }
+ bool prescanned() const { return prescanned_; } // TODO: always true, remove
bool tabInCurrentLine() const { return tabInCurrentLine_; }
+ const AllSources &GetAllSources() const { return cooked_.sources(); }
+ const char *GetLocation() const { return p_; }
+ Provenance GetProvenance(const char *at) const {
+ return cooked_.GetProvenance(at).start;
+ }
+ Provenance GetProvenance() const { return GetProvenance(p_); }
+
+ void PushContext(const std::string &str) {
+ context_ = std::make_shared<Message>(GetProvenance(), str, context_);
+ }
+ void PushContext(std::string &&str) {
+ context_ =
+ std::make_shared<Message>(GetProvenance(), std::move(str), context_);
+ }
+ void PushContext(const char *str) {
+ context_ = std::make_shared<Message>(GetProvenance(), str, context_);
+ }
+
+ void PopContext() {
+ if (context_) {
+ context_ = context_->context();
+ }
+ }
+
+ void PutMessage(Provenance at, const std::string &msg) {
+ messages_.Put(Message{at, msg, context_});
+ }
+ void PutMessage(const char *at, const std::string &msg) {
+ PutMessage(GetProvenance(at), msg);
+ }
+ void PutMessage(const std::string &msg) { PutMessage(p_, msg); }
+ void PutMessage(Provenance at, std::string &&msg) {
+ messages_.Put(Message{at, std::move(msg), context_});
+ }
+ void PutMessage(const char *at, std::string &&msg) {
+ PutMessage(GetProvenance(at), std::move(msg));
+ }
+ void PutMessage(std::string &&msg) { PutMessage(p_, std::move(msg)); }
+ void PutMessage(Provenance at, const char *msg) {
+ PutMessage(at, std::string{msg});
+ }
+ void PutMessage(const char *at, const char *msg) {
+ PutMessage(GetProvenance(at), msg);
+ }
+ void PutMessage(const char *msg) { PutMessage(p_, msg); }
+
bool IsAtEnd() const { return remaining_ == 0; }
std::optional<char> GetNextRawChar() const {
CHECK(remaining_ > 0);
--remaining_;
if (*p_ == '\n') {
- position_.AdvanceLine();
+ column_ = 1;
tabInCurrentLine_ = false;
} else if (*p_ == '\t') {
- position_.TabAdvanceColumn();
+ column_ = ((column_ + 7) & -8) + 1;
tabInCurrentLine_ = true;
} else {
- position_.AdvanceColumn();
+ ++column_;
}
++p_;
}
- void AdvancePositionForPadding() { position_.AdvanceColumn(); }
+ void AdvanceColumnForPadding() { ++column_; }
private:
// Text remaining to be parsed
+ const CookedSource &cooked_;
const char *p_{nullptr};
size_t remaining_{0};
- Position position_;
+ int column_{1};
// Accumulated messages and current nested context.
Messages messages_;
int skippedNewLines_{0};
bool tabInCurrentLine_{false};
bool anyErrorRecovery_{false};
- bool prescanned_{false};
+ bool prescanned_{true};
// NOTE: Any additions or modifications to these data members must also be
// reflected in the copy and move constructors defined at the top of this
// class definition!
UNION_FORMATTER(EventWaitStmt::EventWaitSpec) // R1173
UNION_FORMATTER(FormTeamStmt::FormTeamSpec) // R1177
UNION_FORMATTER(LockStmt::LockStat) // R1179
-UNION_FORMATTER(IoUnit); // R1201, R1203
-UNION_FORMATTER(ConnectSpec); // R1205
+UNION_FORMATTER(IoUnit) // R1201, R1203
+UNION_FORMATTER(ConnectSpec) // R1205
UNION_FORMATTER(CloseStmt::CloseSpec) // R1209
UNION_FORMATTER(IoControlSpec) // R1213
UNION_FORMATTER(Format) // R1215
UNION_FORMATTER(RewindStmt) // R1226
UNION_FORMATTER(PositionOrFlushSpec) // R1227 & R1229
UNION_FORMATTER(FlushStmt) // R1228
-UNION_FORMATTER(InquireStmt); // R1230
+UNION_FORMATTER(InquireStmt) // R1230
UNION_FORMATTER(InquireSpec) // R1231
UNION_FORMATTER(ModuleSubprogram) // R1408
UNION_FORMATTER(Rename) // R1411
std::optional<Call> Designator::ConvertToCall() {
return std::visit(
visitors{[](ObjectName &n) -> std::optional<Call> {
- return {Call{ProcedureDesignator{std : move(n)},
+ return {Call{ProcedureDesignator{std::move(n)},
std::list<ActualArgSpec>{}}};
},
[this](DataReference &dr) -> std::optional<Call> {
#include "idioms.h"
#include "indirection.h"
#include "message.h"
-#include "position.h"
+#include "provenance.h"
#include <cinttypes>
#include <list>
#include <optional>
// Many classes below simply wrap a std::variant<> discriminated union,
// which is conventionally named "u".
#define UNION_CLASS_BOILERPLATE(classname) \
- BOILERPLATE(classname); \
- template<typename A> classname(A &&x) : u(std::move(x)) {}
+ template<typename A> classname(A &&x) : u(std::move(x)) {} \
+ BOILERPLATE(classname)
// Many other classes below simply wrap a std::tuple<> structure, which
// is conventionally named "t".
#define TUPLE_CLASS_BOILERPLATE(classname) \
- BOILERPLATE(classname); \
template<typename... Ts> \
- classname(Ts &&... args) : t(std::forward<Ts>(args)...) {}
+ classname(Ts &&... args) : t(std::forward<Ts>(args)...) {} \
+ BOILERPLATE(classname)
// Many other classes below simply wrap a single data member, which is
// conventionally named "v".
#define WRAPPER_CLASS_BOILERPLATE(classname, type) \
BOILERPLATE(classname); \
classname(type &&x) : v(std::move(x)) {} \
- type v;
+ type v
#define WRAPPER_CLASS(classname, type) \
struct classname { \
using Label = std::uint64_t; // validated later, must be in [1..99999]
// A wrapper for xzy-stmt productions that are statements, so that
-// source positions and labels have a uniform representation.
+// source provenances and labels have a uniform representation.
template<typename A> struct Statement {
- Statement(Position &&pos, std::optional<long> &&lab, bool &&accept, A &&s)
- : position(std::move(pos)),
- label(std::move(lab)), isLabelInAcceptableField{accept},
+ Statement(Provenance &&at, std::optional<long> &&lab, bool &&accept, A &&s)
+ : provenance(at), label(std::move(lab)), isLabelInAcceptableField{accept},
statement(std::move(s)) {}
- Position position;
+ Provenance provenance;
std::optional<Label> label;
bool isLabelInAcceptableField{true};
A statement;
std::ostream &operator<<(std::ostream &o, const Statement<A> &x) {
return o << "(Statement " << x.label << ' '
<< (x.isLabelInAcceptableField ? ""s : "!isLabelInAcceptableField "s)
- << x.position << ' ' << x.statement << ')';
+ << ' ' << x.statement << ')';
}
template<typename A>
+++ /dev/null
-#include "position.h"
-
-namespace Fortran {
-namespace parser {
-std::ostream &operator<<(std::ostream &o, const Position &x) {
- return o << "(at line " << x.lineNumber() << ", column " << x.column() << ')';
-}
-} // namespace parser
-} // namespace Fortran
+++ /dev/null
-#ifndef FORTRAN_POSITION_H_
-#define FORTRAN_POSITION_H_
-
-// Represents a position in a source file.
-// TODO: Support multiple source files for inclusion and support contextual
-// positions for macro expansions.
-
-#include <ostream>
-
-namespace Fortran {
-namespace parser {
-
-class Position {
-public:
- constexpr Position() {}
- constexpr Position(const Position &) = default;
- constexpr Position(Position &&) = default;
- constexpr Position(int ln, int col) : lineNumber_{ln}, column_{col} {}
- constexpr Position &operator=(const Position &) = default;
- constexpr Position &operator=(Position &&) = default;
-
- constexpr int lineNumber() const { return lineNumber_; }
- constexpr int column() const { return column_; }
- Position &set_lineNumber(int line) {
- lineNumber_ = line;
- return *this;
- }
- Position &set_column(int column) {
- column_ = column;
- return *this;
- }
-
- constexpr bool operator<(const Position &that) const {
- return lineNumber_ < that.lineNumber_ ||
- (lineNumber_ == that.lineNumber_ && column_ < that.column_);
- }
-
- constexpr bool operator<=(const Position &that) const {
- return lineNumber_ < that.lineNumber_ ||
- (lineNumber_ == that.lineNumber_ && column_ <= that.column_);
- }
-
- constexpr bool operator==(const Position &that) const {
- return lineNumber_ == that.lineNumber_ && column_ == that.column_;
- }
-
- constexpr bool operator!=(const Position &that) const {
- return !operator==(that);
- }
-
- constexpr bool operator>(const Position &that) const {
- return !operator<=(that);
- }
-
- constexpr bool operator>=(const Position &that) const {
- return !operator<(that);
- }
-
- void AdvanceColumn() { ++column_; }
-
- void TabAdvanceColumn() { column_ = ((column_ + 7) & -8) + 1; }
-
- void AdvanceLine() {
- ++lineNumber_;
- column_ = 1;
- }
-
-private:
- int lineNumber_{1};
- int column_{1};
-};
-
-std::ostream &operator<<(std::ostream &, const Position &);
-} // namespace parser
-} // namespace Fortran
-#endif // FORTRAN_POSITION_H_
#include "preprocessor.h"
-#include "char-buffer.h"
#include "idioms.h"
#include "prescan.h"
#include <algorithm>
return true;
}
-void TokenSequence::Append(const TokenSequence &that) {
+void TokenSequence::clear() {
+ start_.clear();
+ nextStart_ = 0;
+ char_.clear();
+}
+
+void TokenSequence::pop_back() {
+ size_t bytes{nextStart_ - start_.back()};
+ nextStart_ = start_.back();
+ start_.pop_back();
+ char_.resize(nextStart_);
+ provenances_.RemoveLastBytes(bytes);
+}
+
+void TokenSequence::shrink_to_fit() {
+ start_.shrink_to_fit();
+ char_.shrink_to_fit();
+ provenances_.shrink_to_fit();
+}
+
+void TokenSequence::Put(const TokenSequence &that) {
if (nextStart_ < char_.size()) {
start_.push_back(nextStart_);
}
}
char_.insert(char_.end(), that.char_.begin(), that.char_.end());
nextStart_ = char_.size();
+ provenances_.Put(that.provenances_);
+}
+
+void TokenSequence::Put(const TokenSequence &that, size_t at, size_t tokens) {
+ ProvenanceRange provenance;
+ size_t offset{0};
+ for (; tokens-- > 0; ++at) {
+ CharPointerWithLength tok{that[at]};
+ size_t tokBytes{tok.size()};
+ for (size_t j{0}; j < tokBytes; ++j) {
+ if (offset == provenance.bytes) {
+ offset = 0;
+ provenance = that.provenances_.Map(that.start_[at] + j);
+ }
+ PutNextTokenChar(tok[j], provenance.start + 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++);
+ }
+ CloseToken();
+}
+
+void TokenSequence::Put(const CharPointerWithLength &t, Provenance provenance) {
+ Put(&t[0], t.size(), provenance);
+}
+void TokenSequence::Put(const std::string &s, Provenance provenance) {
+ Put(s.data(), s.size(), provenance);
+}
+
+void TokenSequence::Put(const std::stringstream &ss, Provenance provenance) {
+ Put(ss.str(), provenance);
}
-void TokenSequence::EmitWithCaseConversion(CharBuffer *out) const {
+void TokenSequence::EmitWithCaseConversion(CookedSource *cooked) const {
size_t tokens{start_.size()};
size_t chars{char_.size()};
size_t atToken{0};
size_t nextStart{atToken + 1 < tokens ? start_[++atToken] : chars};
if (isalpha(char_[j])) {
for (; j < nextStart; ++j) {
- out->Put(tolower(char_[j]));
+ cooked->Put(tolower(char_[j]));
}
} else {
- out->Put(&char_[j], nextStart - j);
+ cooked->Put(&char_[j], nextStart - j);
j = nextStart;
}
}
+ cooked->Put(provenances_);
}
std::string TokenSequence::ToString() const {
return {&char_[0], char_.size()};
}
-void TokenSequence::clear() {
- start_.clear();
- nextStart_ = 0;
- char_.clear();
-}
-
-void TokenSequence::push_back(const char *s, size_t bytes) {
- for (size_t j{0}; j < bytes; ++j) {
- AddChar(s[j]);
- }
- EndToken();
-}
-
-void TokenSequence::push_back(const CharPointerWithLength &t) {
- push_back(&t[0], t.size());
-}
-void TokenSequence::push_back(const std::string &s) {
- push_back(s.data(), s.size());
-}
-
-void TokenSequence::push_back(const std::stringstream &ss) {
- push_back(ss.str());
-}
-
-void TokenSequence::pop_back() {
- nextStart_ = start_.back();
- start_.pop_back();
- char_.resize(nextStart_);
-}
-
-void TokenSequence::shrink_to_fit() {
- start_.shrink_to_fit();
- char_.shrink_to_fit();
+ProvenanceRange TokenSequence::GetProvenance(
+ size_t token, size_t offset) const {
+ ProvenanceRange range{provenances_.Map(start_[token] + offset)};
+ return {range.start, std::min(range.bytes, TokenBytes(token) - offset)};
}
Definition::Definition(
if (IsIdentifierFirstCharacter(tok)) {
auto it = args.find(tok.ToString());
if (it != args.end()) {
- result.push_back(it->second);
+ result.Put(it->second, 0);
continue;
}
}
- result.push_back(tok);
+ result.Put(token, j, 1);
}
return result;
}
if (index >= args.size()) {
continue;
}
- int lastNonBlank{static_cast<int>(result.size()) - 1};
- for (; lastNonBlank >= 0; --lastNonBlank) {
- if (!result[lastNonBlank].IsBlank()) {
+ size_t afterLastNonBlank{result.size()};
+ for (; afterLastNonBlank > 0; --afterLastNonBlank) {
+ if (!result[afterLastNonBlank - 1].IsBlank()) {
break;
}
}
size_t argTokens{args[index].size()};
- if (lastNonBlank >= 0 && result[lastNonBlank].ToString() == "#") {
- while (result.size() > static_cast<size_t>(lastNonBlank)) {
+ if (afterLastNonBlank > 0 &&
+ result[afterLastNonBlank - 1].ToString() == "#") {
+ while (result.size() >= afterLastNonBlank) {
result.pop_back();
}
- std::string strung{'"'};
+ result.PutNextTokenChar('"', 0); // TODO provenance
for (size_t k{0}; k < argTokens; ++k) {
const CharPointerWithLength &arg{args[index][k]};
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};
if (ch == '"' || ch == '\\') {
- strung += ch;
+ result.PutNextTokenChar(ch, from);
}
- strung += ch;
+ result.PutNextTokenChar(ch, from);
}
}
- result.push_back(strung + '"');
+ result.PutNextTokenChar('"', 0); // TODO provenance
+ result.CloseToken();
} else {
for (size_t k{0}; k < argTokens; ++k) {
- const CharPointerWithLength &argToken{args[index][k]};
- if (pasting && argToken.IsBlank()) {
- } else {
- result.push_back(argToken);
+ if (!pasting || !args[index][k].IsBlank()) {
+ result.Put(args[index], k);
pasting = false;
}
}
token.ToString() == "__VA_ARGS__") {
for (size_t k{argumentCount_}; k < args.size(); ++k) {
if (k > argumentCount_) {
- result.push_back(","s);
- }
- for (size_t n{0}; n < args[k].size(); ++n) {
- result.push_back(args[k][n]);
+ result.Put(","s, 0); // TODO provenance
}
+ result.Put(args[k]);
}
} else if (bytes == 10 && isVariadic_ && token.ToString() == "__VA_OPT__" &&
j + 2 < tokens && replacement_[j + 1].ToString() == "(" &&
continue;
}
}
- result.push_back(token);
+ result.Put(replacement_, j);
}
}
return result;
if (j == tokens) {
return false; // nothing appeared that could be replaced
}
-
- for (size_t k{0}; k < j; ++k) {
- result->push_back(input[k]);
- }
+ result->Put(input, 0, j);
for (; j < tokens; ++j) {
const CharPointerWithLength &token{input[j]};
if (token.IsBlank() || !IsIdentifierFirstCharacter(token[0])) {
- result->push_back(token);
+ result->Put(input, j);
continue;
}
auto it = definitions_.find(token);
if (it == definitions_.end()) {
- result->push_back(token);
+ result->Put(input, j);
continue;
}
Definition &def{it->second};
if (def.isDisabled()) {
- result->push_back(token);
+ result->Put(input, j);
continue;
}
if (!def.isFunctionLike()) {
if (def.isPredefined()) {
std::string name{def.replacement()[0].ToString()};
if (name == "__FILE__") {
- result->Append("\""s + prescanner_.sourceFile().path() + '"');
+ result->Put("\""s + prescanner_.GetCurrentPath() + '"');
continue;
}
if (name == "__LINE__") {
std::stringstream ss;
- ss << prescanner_.position().lineNumber();
- result->Append(ss.str());
+ ss << prescanner_.GetCurrentLineNumber();
+ result->Put(ss.str());
continue;
}
}
def.set_isDisabled(true);
- result->Append(ReplaceMacros(def.replacement()));
+ result->Put(ReplaceMacros(def.replacement()));
def.set_isDisabled(false);
continue;
}
}
}
if (!leftParen) {
- result->push_back(token);
+ result->Put(input, j);
continue;
}
std::vector<size_t> argStart{++k};
}
if (k >= tokens || argStart.size() < def.argumentCount() ||
(argStart.size() > def.argumentCount() && !def.isVariadic())) {
- result->push_back(token);
+ result->Put(input, j);
continue;
}
j = k; // advance to the terminal ')'
for (k = 0; k < argStart.size(); ++k) {
size_t at{argStart[k]};
size_t count{(k + 1 == argStart.size() ? j : argStart[k + 1] - 1) - at};
- TokenSequence actual;
- for (; count-- > 0; ++at) {
- actual.push_back(input[at]);
- }
- args.emplace_back(std::move(actual));
+ args.emplace_back(TokenSequence(input, at, count));
}
def.set_isDisabled(true);
- result->Append(ReplaceMacros(def.Apply(args)));
+ result->Put(ReplaceMacros(def.Apply(args)));
def.set_isDisabled(false);
}
return true;
TokenSequence noBlanks;
for (size_t j{SkipBlanks(token, first, tokens)}; j < tokens;
j = SkipBlanks(token, j + 1, tokens)) {
- noBlanks.push_back(token[j]);
+ noBlanks.Put(token, j);
}
return noBlanks;
}
}
void Preprocessor::Complain(const std::string &message) {
- prescanner_.messages().Add({prescanner_.position(), message});
+ prescanner_.messages().Put({prescanner_.GetCurrentProvenance(), message});
}
// Precedence level codes used here to accommodate mixed Fortran and C:
name = expr1[j++];
}
if (!name.empty()) {
- expr2.push_back(IsNameDefined(name) ? "1" : "0", 1);
+ expr2.Put(IsNameDefined(name) ? "1" : "0", 1, 0); // TODO provenance
continue;
}
}
- expr2.push_back(expr1[j]);
+ expr2.Put(expr1, j);
}
TokenSequence expr3{ReplaceMacros(expr2)};
TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
// extensions for preprocessing will not be necessary.
#include "idioms.h"
+#include "provenance.h"
#include <cctype>
#include <cstring>
#include <functional>
namespace Fortran {
namespace parser {
-class CharBuffer;
+class CookedSource;
class Prescanner;
// Just a const char pointer with an associated length; does not own the
namespace parser {
// Buffers a contiguous sequence of characters that has been partitioned into
-// a sequence of preprocessing tokens.
+// a sequence of preprocessing tokens with provenances.
class TokenSequence {
public:
TokenSequence() {}
- TokenSequence(const TokenSequence &that) { Append(that); }
+ TokenSequence(const TokenSequence &that) { Put(that); }
+ TokenSequence(const TokenSequence &that, size_t at, size_t count = 1) {
+ Put(that, at, count);
+ }
TokenSequence(TokenSequence &&that)
: start_{std::move(that.start_)},
nextStart_{that.nextStart_}, char_{std::move(that.char_)} {}
- TokenSequence(const std::string &s) { push_back(s); }
+ TokenSequence(const std::string &s) { Put(s, 0); } // TODO predefined prov.
TokenSequence &operator=(const TokenSequence &that) {
clear();
- Append(that);
+ Put(that);
return *this;
}
TokenSequence &operator=(TokenSequence &&that) {
}
CharPointerWithLength operator[](size_t token) const {
- return {&char_[start_[token]],
- (token + 1 >= start_.size() ? char_.size() : start_[token + 1]) -
- start_[token]};
+ return {&char_[start_[token]], TokenBytes(token)};
}
- void AddChar(char ch) { char_.emplace_back(ch); }
+ bool empty() const { return start_.empty(); }
+ size_t size() const { return start_.size(); }
+ const char *data() const { return &char_[0]; }
+ void clear();
+ void pop_back();
+ void shrink_to_fit();
+
+ void PutNextTokenChar(char ch, Provenance provenance) {
+ char_.emplace_back(ch);
+ provenances_.Put({provenance, 1});
+ }
- void EndToken() {
+ void CloseToken() {
// CHECK(char_.size() > nextStart_);
start_.emplace_back(nextStart_);
nextStart_ = char_.size();
start_.pop_back();
}
- void Append(const TokenSequence &);
- void EmitWithCaseConversion(CharBuffer *) const;
+ void Put(const TokenSequence &);
+ void Put(const TokenSequence &, size_t at, size_t tokens = 1);
+ void Put(const char *, size_t, Provenance);
+ void Put(const CharPointerWithLength &, Provenance);
+ void Put(const std::string &, Provenance);
+ void Put(const std::stringstream &, Provenance);
+ void EmitWithCaseConversion(CookedSource *) const;
std::string ToString() const;
-
- bool empty() const { return start_.empty(); }
- size_t size() const { return start_.size(); }
- const char *data() const { return &char_[0]; }
- void clear();
- void push_back(const char *, size_t);
- void push_back(const CharPointerWithLength &);
- void push_back(const std::string &);
- void push_back(const std::stringstream &);
- void pop_back();
- void shrink_to_fit();
+ ProvenanceRange GetProvenance(size_t token, size_t offset = 0) const;
private:
- std::vector<int> start_;
+ size_t TokenBytes(size_t token) const {
+ return (token + 1 >= start_.size() ? char_.size() : start_[token + 1]) -
+ start_[token];
+ }
+
+ std::vector<size_t> start_;
size_t nextStart_{0};
std::vector<char> char_;
+ OffsetToProvenanceMappings provenances_;
};
// Defines a macro
#include "prescan.h"
-#include "char-buffer.h"
#include "idioms.h"
#include "source.h"
#include <cctype>
namespace Fortran {
namespace parser {
-CharBuffer Prescanner::Prescan(const SourceFile &source) {
- sourceFile_ = &source;
- lineStart_ = source.content();
- limit_ = lineStart_ + source.bytes();
- CharBuffer out;
+CookedSource Prescanner::Prescan() {
+ startProvenance_ = 0;
+ start_ = &allSources_[0];
+ limit_ = start_ + allSources_.size();
+ lineStart_ = start_;
TokenSequence tokens, preprocessed;
+ CookedSource cooked{allSources_};
while (lineStart_ < limit_) {
if (CommentLinesAndPreprocessorDirectives() && lineStart_ >= limit_) {
break;
while (NextToken(&tokens)) {
}
if (preprocessor_.MacroReplacement(tokens, &preprocessed)) {
- preprocessed.AddChar('\n');
- preprocessed.EndToken();
+ EmitChar(&preprocessed, '\n');
+ preprocessed.CloseToken();
if (IsFixedFormCommentLine(preprocessed.data()) ||
IsFreeFormComment(preprocessed.data())) {
++newlineDebt_;
} else {
preprocessed.pop_back(); // clip the newline added above
- preprocessed.EmitWithCaseConversion(&out);
+ preprocessed.EmitWithCaseConversion(&cooked);
}
preprocessed.clear();
} else {
- tokens.EmitWithCaseConversion(&out);
+ tokens.EmitWithCaseConversion(&cooked);
}
tokens.clear();
- out.Put('\n');
- PayNewlineDebt(&out);
+ cooked.Put('\n', 0);
+ PayNewlineDebt(&cooked);
}
- PayNewlineDebt(&out);
- sourceFile_ = nullptr;
- return std::move(out);
+ PayNewlineDebt(&cooked);
+ cooked.Marshal();
+ return cooked;
}
std::optional<TokenSequence> Prescanner::NextTokenizedLine() {
}
bool wasInPreprocessorDirective{inPreprocessorDirective_};
auto saveAt = at_;
- auto saveAtPosition = atPosition_;
inPreprocessorDirective_ = true;
BeginSourceLineAndAdvance();
TokenSequence tokens;
}
inPreprocessorDirective_ = wasInPreprocessorDirective;
at_ = saveAt;
- atPosition_ = saveAtPosition;
return {std::move(tokens)};
}
+std::string Prescanner::GetCurrentPath() const {
+ return allSources_.GetPath(GetCurrentProvenance());
+}
+
+int Prescanner::GetCurrentLineNumber() const {
+ return allSources_.GetLineNumber(GetCurrentProvenance());
+}
+
void Prescanner::NextLine() {
void *vstart{static_cast<void *>(const_cast<char *>(lineStart_))};
void *v{std::memchr(vstart, '\n', limit_ - lineStart_)};
const char *nl{const_cast<const char *>(static_cast<char *>(v))};
lineStart_ = nl + 1;
}
- lineStartPosition_.AdvanceLine();
}
void Prescanner::LabelField(TokenSequence *token) {
int outCol{1};
- for (; *at_ != '\n' && atPosition_.column() <= 6; ++at_) {
+ for (; *at_ != '\n' && column_ <= 6; ++at_) {
if (*at_ == '\t') {
++at_;
- atPosition_.set_column(7);
+ column_ = 7;
break;
}
if (*at_ != ' ' &&
- (*at_ != '0' ||
- atPosition_.column() != 6)) { // '0' in column 6 becomes space
- token->AddChar(*at_);
+ (*at_ != '0' || column_ != 6)) { // '0' in column 6 becomes space
+ EmitChar(token, *at_);
++outCol;
}
- atPosition_.AdvanceColumn();
+ ++column_;
}
if (outCol > 1) {
- token->EndToken();
+ token->CloseToken();
}
if (outCol < 7) {
for (; outCol < 7; ++outCol) {
- token->AddChar(' ');
+ EmitChar(token, ' ');
}
- token->EndToken();
+ token->CloseToken();
}
}
void Prescanner::NextChar() {
// CHECK(*at_ != '\n');
++at_;
- atPosition_.AdvanceColumn();
+ ++column_;
if (inPreprocessorDirective_) {
while (*at_ == '/' && at_[1] == '*') {
char star{' '}, slash{' '};
at_ += 2;
- atPosition_.set_column(atPosition_.column() + 2);
+ column_ += 2;
while ((*at_ != '\n' || slash == '\\') && (star != '*' || slash != '/')) {
star = slash;
slash = *at_++;
- atPosition_.AdvanceColumn();
+ ++column_;
}
}
while (*at_ == '\\' && at_ + 2 < limit_ && at_[1] == '\n') {
++newlineDebt_;
}
} else {
- if ((inFixedForm_ && atPosition_.column() > fixedFormColumnLimit_ &&
+ if ((inFixedForm_ && column_ > fixedFormColumnLimit_ &&
!tabInCurrentLine_) ||
(*at_ == '!' && !inCharLiteral_)) {
while (*at_ != '\n') {
if (inFixedForm_) {
SkipSpaces();
} else if (*at_ == ' ' || *at_ == '\t') {
+ Provenance here{GetCurrentProvenance()};
NextChar();
SkipSpaces();
if (*at_ != '\n') {
- tokens->AddChar(' ');
- tokens->EndToken();
+ tokens->PutNextTokenChar(' ', here);
+ tokens->CloseToken();
return true;
}
}
inCharLiteral_ = true;
while (n-- > 0) {
if (PadOutCharacterLiteral()) {
- tokens->AddChar(' ');
+ EmitChar(tokens, ' ');
} else {
if (*at_ == '\n') {
break; // TODO error
EmitCharAndAdvance(tokens, nch);
}
}
- tokens->EndToken();
+ tokens->CloseToken();
return true;
}
do {
EmitCharAndAdvance(tokens, *at_);
while (PadOutCharacterLiteral()) {
- tokens->AddChar(' ');
+ EmitChar(tokens, ' ');
}
if (*at_ == '\\' && enableBackslashEscapesInCharLiterals_) {
EmitCharAndAdvance(tokens, '\\');
while (PadOutCharacterLiteral()) {
- tokens->AddChar(' ');
+ EmitChar(tokens, ' ');
}
} else if (*at_ == quote) {
// A doubled quote mark becomes a single instance of the quote character
bool Prescanner::PadOutCharacterLiteral() {
if (inFixedForm_ && !tabInCurrentLine_ && *at_ == '\n' &&
- atPosition_.column() < fixedFormColumnLimit_) {
- atPosition_.AdvanceColumn();
+ column_ < fixedFormColumnLimit_) {
+ ++column_;
return true;
}
return false;
return false;
}
BeginSourceLine(cont);
- atPosition_.set_column(7);
+ column_ = 7;
++newlineDebt_;
NextLine();
return true;
if (p >= limit_) {
return false;
}
- int column{1};
+ column_ = 1;
for (; *p == ' ' || *p == '\t'; ++p) {
- ++column;
+ ++column_;
}
if (*p == '&') {
++p;
- ++column;
+ ++column_;
} else if (ampersand || delimiterNesting_ > 0) {
if (p > lineStart_) {
--p;
- --column;
+ --column_;
}
} else {
return false; // not a continuation
}
at_ = p;
- (atPosition_ = lineStartPosition_).set_column(column);
tabInCurrentLine_ = false;
++newlineDebt_;
NextLine();
return true;
}
-void Prescanner::PayNewlineDebt(CharBuffer *out) {
+void Prescanner::PayNewlineDebt(CookedSource *cooked) {
for (; newlineDebt_ > 0; --newlineDebt_) {
- out->Put('\n');
+ cooked->Put('\n', 0);
}
}
} // namespace parser
// line continuation, comment removal, card image margins, padding out
// fixed form character literals on truncated card images, and drives the
// Fortran source preprocessor.
-//
-// It is possible to run the Fortran parser without running this prescan
-// phase, using only the parsers defined in cooked-chars.h, so long as
-// preprocessing and INCLUDE lines need not be handled.
-#include "char-buffer.h"
#include "message.h"
-#include "position.h"
#include "preprocessor.h"
+#include "provenance.h"
#include "source.h"
#include <optional>
+#include <string>
namespace Fortran {
namespace parser {
class Prescanner {
public:
- explicit Prescanner(Messages &messages)
- : messages_{messages}, preprocessor_{*this} {}
+ Prescanner(Messages &messages, AllSources &allSources)
+ : messages_{messages}, allSources_{allSources}, preprocessor_{*this} {}
Messages &messages() const { return messages_; }
- const SourceFile &sourceFile() const { return *sourceFile_; }
- Position position() const { return atPosition_; }
bool anyFatalErrors() const { return anyFatalErrors_; }
Prescanner &set_fixedForm(bool yes) {
return *this;
}
- CharBuffer Prescan(const SourceFile &source);
+ CookedSource Prescan();
std::optional<TokenSequence> NextTokenizedLine();
+ Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
+ std::string GetCurrentPath() const; // __FILE__
+ int GetCurrentLineNumber() const; // __LINE__
private:
void BeginSourceLine(const char *at) {
at_ = at;
- atPosition_ = lineStartPosition_;
tabInCurrentLine_ = false;
preventHollerith_ = false;
delimiterNesting_ = 0;
NextLine();
}
+ Provenance GetProvenance(const char *sourceChar) const {
+ return startProvenance_ + sourceChar - start_;
+ }
+
+ void EmitChar(TokenSequence *tokens, char ch) {
+ tokens->PutNextTokenChar(ch, GetCurrentProvenance());
+ }
+
char EmitCharAndAdvance(TokenSequence *tokens, char ch) {
- tokens->AddChar(ch);
+ EmitChar(tokens, ch);
NextChar();
return *at_;
}
const char *FixedFormContinuationLine();
bool FixedFormContinuation();
bool FreeFormContinuation();
- void PayNewlineDebt(CharBuffer *);
+ void PayNewlineDebt(CookedSource *);
Messages &messages_;
- bool anyFatalErrors_{false};
- const char *lineStart_{nullptr}; // next line to process; <= limit_
+ AllSources &allSources_;
+
+ Provenance startProvenance_;
+ const char *start_{nullptr}; // beginning of sourceFile_ content
+ const char *limit_{nullptr}; // first address after end of source
const char *at_{nullptr}; // next character to process; < lineStart_
int column_{1}; // card image column position of next character
- const char *limit_{nullptr}; // first address after end of source
+ const char *lineStart_{nullptr}; // next line to process; <= limit_
+ bool tabInCurrentLine_{false};
+ bool preventHollerith_{false};
+
+ bool anyFatalErrors_{false};
int newlineDebt_{0}; // newline characters consumed but not yet emitted
- const SourceFile *sourceFile_{nullptr};
- Position atPosition_, lineStartPosition_;
bool inCharLiteral_{false};
bool inPreprocessorDirective_{false};
bool inFixedForm_{true};
int fixedFormColumnLimit_{72};
- bool tabInCurrentLine_{false};
- bool preventHollerith_{false};
bool enableOldDebugLines_{false};
bool enableBackslashEscapesInCharLiterals_{true};
int delimiterNesting_{0};
#include "provenance.h"
#include "idioms.h"
-#include "position.h"
#include <utility>
namespace Fortran {
namespace parser {
-Origin::Origin(const SourceFile &source) : u_{Inclusion{source}} {}
-Origin::Origin(const SourceFile &included, ProvenanceRange from)
- : u_{Inclusion{included}}, replaces_{from} {}
-Origin::Origin(ProvenanceRange def, ProvenanceRange use,
- const std::string &expansion)
- : u_{Macro{def, expansion}}, replaces_{use} {}
+void OffsetToProvenanceMappings::Put(ProvenanceRange range) {
+ if (provenanceMap_.empty()) {
+ provenanceMap_.push_back({bytes_, 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});
+ }
+ }
+ bytes_ += range.bytes;
+}
-size_t Origin::size() const {
- return std::visit(
- visitors{[](const Inclusion &inc) { return inc.source.bytes(); },
- [](const Macro &mac) { return mac.expansion.size(); }},
- u_);
+void OffsetToProvenanceMappings::Put(const OffsetToProvenanceMappings &that) {
+ for (const auto &map : that.provenanceMap_) {
+ Put(map.range);
+ }
}
-const char &Origin::operator[](size_t n) const {
- return std::visit(
- visitors{[n](const Inclusion &inc) -> const char & {
- return inc.source.content()[n]; },
- [n](const Macro &mac) -> const char & { return mac.expansion[n]; }},
- u_);
+ProvenanceRange OffsetToProvenanceMappings::Map(size_t at) const {
+ CHECK(at < bytes_);
+ size_t low{0}, count{provenanceMap_.size()};
+ while (count > 1) {
+ size_t mid{low + (count >> 1)};
+ if (provenanceMap_[mid].start > at) {
+ count = mid - low;
+ } else {
+ count -= mid - low;
+ low = mid;
+ }
+ }
+ size_t offset{at - provenanceMap_[low].start};
+ return {provenanceMap_[low].start + offset,
+ provenanceMap_[low].range.bytes - offset};
+}
+
+void OffsetToProvenanceMappings::RemoveLastBytes(size_t bytes) {
+ for (; bytes > 0; provenanceMap_.pop_back()) {
+ if (provenanceMap_.empty()) {
+ break;
+ }
+ ContiguousProvenanceMapping &last{provenanceMap_.back()};
+ if (bytes < last.range.bytes) {
+ last.range.bytes -= bytes;
+ break;
+ }
+ bytes -= last.range.bytes;
+ }
+}
+
+AllSources::AllSources(const SourceFile &initialSourceFile) {
+ AddIncludedFile(initialSourceFile, ProvenanceRange{});
}
-void Origin::Identify(std::ostream &o, const AllOfTheSource &sources, size_t at,
- const std::string &prefix) const {
+const char &AllSources::operator[](Provenance at) const {
+ const Origin &origin{MapToOrigin(at)};
+ return origin[at - origin.start];
+}
+
+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};
+}
+
+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 AllSources::AddCompilerInsertion(const std::string &text) {
+ size_t start{bytes_}, bytes{text.size()};
+ bytes_ += bytes;
+ origin_.emplace_back(start, text);
+ return {start, bytes};
+}
+
+void AllSources::Identify(
+ std::ostream &o, Provenance at, const std::string &prefix) const {
static const std::string indented{prefix + " "};
+ const Origin &origin{MapToOrigin(at)};
std::visit(
- visitors{
- [&](const Inclusion &inc) {
- Position pos{inc.source.FindOffsetPosition(at)};
- o << prefix << "at line " << pos.lineNumber() << ", column " <<
- pos.column() << "in the file " << inc.source.path() << '\n';
- if (replaces_.bytes() > 0) {
- o << prefix << " that was included\n";
- sources.Identify(o, replaces_.start(), indented);
- }
- },
+ 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);
+ }
+ },
[&](const Macro &mac) {
- o << prefix << "in the expansion of a macro that was defined\n";
- sources.Identify(o, mac.definition.start(), indented);
- o << prefix << "... and called\n";
- sources.Identify(o, replaces_.start(), indented);
- o << prefix << "... and expanded to\n" <<
- indented << mac.expansion << '\n'; }},
- u_);
+ o << prefix << "in the expansion of a macro that was defined\n";
+ Identify(o, mac.definition.start, indented);
+ o << prefix << "... and called\n";
+ Identify(o, origin.replaces.start, indented);
+ o << prefix << "... and expanded to\n"
+ << indented << mac.expansion << '\n';
+ },
+ [&](const CompilerInsertion &ins) {
+ o << prefix << "in text '" << ins.text
+ << "' inserted by the compiler\n";
+ }},
+ origin.u);
}
-AllOfTheSource &AllOfTheSource::Add(Origin &&origin) {
- size_t start{bytes_};
- bytes_ += origin.size();
- chunk_.emplace_back(Chunk{std::move(origin), start});
- return *this;
+const SourceFile *AllSources::GetSourceFile(Provenance at) 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);
+ }},
+ origin.u);
}
-const char &AllOfTheSource::operator[](Provenance at) const {
- const Chunk &chunk{MapToChunk(at)};
- return chunk.origin[at - chunk.start];
+std::string AllSources::GetPath(Provenance at) const {
+ const SourceFile *source{GetSourceFile(at)};
+ return source ? source->path() : ""s;
}
-const AllOfTheSource::Chunk &AllOfTheSource::MapToChunk(Provenance at) const {
+int AllSources::GetLineNumber(Provenance at) const {
+ const SourceFile *source{GetSourceFile(at)};
+ return source ? source->FindOffsetLineAndColumn(at).first : 0;
+}
+
+AllSources::Origin::Origin(size_t s, const SourceFile &source)
+ : start{s}, u{Inclusion{source}} {}
+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);
+}
+
+const char &AllSources::Origin::operator[](size_t n) const {
+ return std::visit(
+ visitors{[n](const Inclusion &inc) -> const char & {
+ return inc.source.content()[n];
+ },
+ [n](const Macro &mac) -> const char & { return mac.expansion[n]; },
+ [n](const CompilerInsertion &ins) -> const char & {
+ return ins.text[n];
+ }},
+ u);
+}
+
+const AllSources::Origin &AllSources::MapToOrigin(Provenance at) const {
CHECK(at < bytes_);
- size_t low{0}, count{chunk_.size()};
+ size_t low{0}, count{origin_.size()};
while (count > 1) {
size_t mid{low + (count >> 1)};
- if (chunk_[mid].start > at) {
+ if (origin_[mid].start > at) {
count = mid - low;
} else {
count -= mid - low;
low = mid;
}
}
- return chunk_[low];
+ CHECK(at >= origin_[low].start);
+ CHECK(low + 1 == origin_.size() || at < origin_[low + 1].start);
+ return origin_[low];
}
-void AllOfTheSource::Identify(std::ostream &o, Provenance at,
- const std::string &prefix) const {
- const Chunk &chunk{MapToChunk(at)};
- return chunk.origin.Identify(o, *this, at - chunk.start, prefix);
+ProvenanceRange CookedSource::GetProvenance(const char *at) const {
+ return provenanceMap_.Map(at - &data_[0]);
+}
+
+void CookedSource::Marshal() {
+ data_.resize(buffer_.size());
+ char *p{&data_[0]};
+ for (char ch : buffer_) {
+ *p++ = ch;
+ }
+ buffer_.clear();
}
} // namespace parser
} // namespace Fortran
#ifndef FORTRAN_PROVENANCE_H_
#define FORTRAN_PROVENANCE_H_
+#include "char-buffer.h"
#include "source.h"
-#include <memory>
#include <ostream>
#include <string>
+#include <utility>
#include <variant>
+#include <vector>
namespace Fortran {
namespace parser {
+// Each character in the contiguous source stream built by the
+// prescanner corresponds to a particular character in a source file,
+// include file, macro expansion, or compiler-inserted padding.
+// 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
+// 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.
+
using Provenance = size_t;
-class ProvenanceRange {
-public:
+struct ProvenanceRange {
ProvenanceRange() {}
- bool empty() const { return bytes_ == 0; }
- Provenance start() const { return start_; }
- size_t bytes() const { return bytes_; }
+ ProvenanceRange(Provenance s, size_t n) : start{s}, bytes{n} {}
+ ProvenanceRange(const ProvenanceRange &) = default;
+ ProvenanceRange(ProvenanceRange &&) = default;
+ ProvenanceRange &operator=(const ProvenanceRange &) = default;
+ ProvenanceRange &operator=(ProvenanceRange &&) = default;
+ Provenance start{0};
+ size_t bytes{0};
+};
+
+class OffsetToProvenanceMappings {
+public:
+ OffsetToProvenanceMappings() {}
+ size_t size() const { return bytes_; }
+ 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);
+
private:
- Provenance start_{0};
+ struct ContiguousProvenanceMapping {
+ size_t start;
+ ProvenanceRange range;
+ };
+
size_t bytes_{0};
+ std::vector<ContiguousProvenanceMapping> provenanceMap_;
};
-class AllOfTheSource;
-
-class Origin {
+class AllSources {
public:
- explicit Origin(const SourceFile &); // initial source file
- Origin(const SourceFile &, ProvenanceRange); // included source file
- Origin(ProvenanceRange def, ProvenanceRange use, // macro call
- const std::string &expansion);
- size_t size() const;
- const char &operator[](size_t) const;
- void Identify(std::ostream &, const AllOfTheSource &, size_t,
- const std::string &indent) const;
+ explicit AllSources(const SourceFile &initialSourceFile);
+
+ size_t size() const { return bytes_; }
+ const char &operator[](Provenance) const;
+
+ ProvenanceRange AddIncludedFile(const SourceFile &, ProvenanceRange);
+ ProvenanceRange AddMacroCall(
+ ProvenanceRange def, ProvenanceRange use, const std::string &expansion);
+ ProvenanceRange AddCompilerInsertion(const std::string &);
+
+ void Identify(std::ostream &, Provenance, const std::string &prefix) const;
+ const SourceFile *GetSourceFile(Provenance) const;
+ std::string GetPath(Provenance) const; // __FILE__
+ int GetLineNumber(Provenance) const; // __LINE__
+
private:
struct Inclusion {
const SourceFile &source;
ProvenanceRange definition;
std::string expansion;
};
- std::variant<Inclusion, Macro> u_;
- ProvenanceRange replaces_;
-};
+ struct CompilerInsertion {
+ std::string text;
+ };
+
+ struct Origin {
+ Origin(size_t start, const SourceFile &);
+ Origin(size_t start, const SourceFile &, ProvenanceRange);
+ Origin(size_t start, ProvenanceRange def, ProvenanceRange use,
+ const std::string &expansion);
+ Origin(size_t start, const std::string &);
+
+ size_t size() const;
+ const char &operator[](size_t) const;
-class AllOfTheSource {
-public:
- AllOfTheSource() {}
- AllOfTheSource(AllOfTheSource &&) = default;
- AllOfTheSource &operator=(AllOfTheSource &&) = default;
- size_t size() const { return bytes_; }
- const char &operator[](Provenance) const;
- AllOfTheSource &Add(Origin &&);
- void Identify(std::ostream &, Provenance, const std::string &prefix) const;
-private:
- struct Chunk {
- Chunk(Origin &&origin, size_t at) : origin{std::move(origin)}, start{at} {}
- Origin origin;
size_t start;
+ std::variant<Inclusion, Macro, CompilerInsertion> u;
+ ProvenanceRange replaces;
};
- const Chunk &MapToChunk(Provenance) const;
- std::vector<Chunk> chunk_;
- size_t bytes_;
+
+ const Origin &MapToOrigin(Provenance) const;
+
+ std::vector<Origin> origin_;
+ size_t bytes_{0};
};
-class ProvenancedChar {
+class CookedSource {
public:
- using type = char;
- char character() const { return static_cast<char>(packed_); }
- Provenance provenance() const { return packed_ >> 8; }
-private:
- size_t packed_;
-};
+ explicit CookedSource(AllSources &sources) : sources_{sources} {}
-class ProvenancedString {
-private:
- class iterator {
- public:
- iterator(const AllOfTheSource &sources, Provenance at)
- : sources_{&sources}, at_{at} {}
- iterator(const iterator &that)
- : sources_{that.sources_}, at_{that.at_} {}
- iterator &operator=(const iterator &that) {
- sources_ = that.sources_;
- at_ = that.at_;
- return *this;
- }
- const char &operator*() const;
- iterator &operator++() {
- ++at_;
- return *this;
- }
- iterator operator++(int) {
- iterator result{*this};
- ++at_;
- return result;
- }
- bool operator<(const iterator &that) { return at_ < that.at_; }
- bool operator<=(const iterator &that) { return at_ <= that.at_; }
- bool operator==(const iterator &that) { return at_ == that.at_; }
- bool operator!=(const iterator &that) { return at_ != that.at_; }
- private:
- const AllOfTheSource *sources_;
- size_t at_;
- };
+ size_t size() const { return data_.size(); }
+ const char &operator[](size_t n) const { return data_[n]; }
+ const char &at(size_t n) const { return data_.at(n); }
- iterator begin(const AllOfTheSource &sources) const {
- return iterator(sources, start_);
- }
- iterator end(const AllOfTheSource &sources) const {
- return iterator(sources, start_ + bytes_);
+ AllSources &sources() const { return sources_; }
+
+ ProvenanceRange GetProvenance(const char *) const;
+ void Identify(std::ostream &, const char *) const;
+
+ void Put(const char *data, size_t bytes) { buffer_.Put(data, bytes); }
+ void Put(char ch) { buffer_.Put(&ch, 1); }
+ void Put(char ch, Provenance p) {
+ buffer_.Put(&ch, 1);
+ provenanceMap_.Put(ProvenanceRange{p, 1});
}
-public:
- size_t size() const { return bytes_; }
+ void Put(const OffsetToProvenanceMappings &pm) { provenanceMap_.Put(pm); }
+ void Marshal(); // marshalls all text into one contiguous block
+
private:
- Provenance start_;
- size_t bytes_;
+ AllSources &sources_;
+ CharBuffer buffer_; // before Marshal()
+ std::vector<char> data_; // all of it, prescanned and preprocessed
+ OffsetToProvenanceMappings provenanceMap_;
};
} // namespace parser
} // namespace Fortran
}
close(fileDescriptor_);
fileDescriptor_ = -1;
- bytes_ = buffer.bytes();
+ bytes_ = buffer.size();
if (bytes_ == 0) {
// empty file
content_ = nullptr;
path_.clear();
}
-Position SourceFile::FindOffsetPosition(size_t at) const {
+std::pair<int, int> SourceFile::FindOffsetLineAndColumn(size_t at) const {
CHECK(at < bytes_);
if (lineStart_.empty()) {
return {1, static_cast<int>(at + 1)};
low = mid;
}
}
- return {static_cast<int>(low + 1),
- static_cast<int>(at - lineStart_[low] + 1)};
-}
-
-size_t SourceFile::FindPositionOffset(int lineNumber, int column) const {
- return lineStart_.at(lineNumber - 1) + column - 1;
-}
-
-size_t SourceFile::FindPositionOffset(Position pos) const {
- return FindPositionOffset(pos.lineNumber(), pos.column());
+ return {
+ static_cast<int>(low + 1), static_cast<int>(at - lineStart_[low] + 1)};
}
} // namespace parser
} // namespace Fortran
// - Line ending markers are converted to single newline characters
// - A newline character is added to the last line of the file if one is needed
-#include "position.h"
#include <sstream>
#include <string>
+#include <utility>
#include <vector>
namespace Fortran {
const char *content() const { return content_; }
size_t bytes() const { return bytes_; }
size_t lines() const { return lineStart_.size(); }
- Position FindOffsetPosition(size_t) const;
- size_t FindPositionOffset(int lineNumber, int column) const;
- size_t FindPositionOffset(Position) const;
+ std::pair<int, int> FindOffsetLineAndColumn(size_t) const;
private:
std::string path_;
#include "../../lib/parser/message.h"
#include "../../lib/parser/parse-tree.h"
#include "../../lib/parser/prescan.h"
+#include "../../lib/parser/provenance.h"
#include "../../lib/parser/source.h"
#include "../../lib/parser/user-state.h"
#include <cerrno>
bool standard{false};
bool enableOldDebugLines{false};
int columns{72};
- bool prescan{true};
while (!args.empty()) {
if (args.front().empty()) {
columns = 132;
} else if (flag == "-fdebug-dump-cooked-chars") {
dumpCookedChars = true;
- } else if (flag == "-fno-prescan") {
- prescan = false;
} else if (flag == "-ed") {
enableOldDebugLines = true;
} else {
}
}
- Fortran::parser::SourceFile source;
+ Fortran::parser::SourceFile sourceFile;
std::stringstream error;
- if (!source.Open(path, &error)) {
+ if (!sourceFile.Open(path, &error)) {
std::cerr << error.str() << '\n';
return 1;
}
- const char *sourceContent{source.content()};
- size_t sourceBytes{source.bytes()};
- std::unique_ptr<char[]> prescanned;
- if (prescan) {
- Fortran::parser::Messages messages;
- Fortran::parser::Prescanner prescanner{messages};
- Fortran::parser::CharBuffer buffer{
- prescanner.set_fixedForm(fixedForm)
- .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
- .set_fixedFormColumnLimit(columns)
- .set_enableOldDebugLines(enableOldDebugLines)
- .Prescan(source)};
- std::cerr << messages;
- if (prescanner.anyFatalErrors()) {
- return 1;
- }
- sourceBytes = buffer.bytes();
- char *contig{new char[sourceBytes]};
- buffer.CopyToContiguous(contig);
- sourceContent = contig;
- prescanned.reset(contig);
- columns = std::numeric_limits<int>::max();
+ Fortran::parser::AllSources allSources{sourceFile};
+ Fortran::parser::Messages messages;
+ Fortran::parser::Prescanner prescanner{messages, allSources};
+ Fortran::parser::CookedSource cooked{
+ prescanner.set_fixedForm(fixedForm)
+ .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
+ .set_fixedFormColumnLimit(columns)
+ .set_enableOldDebugLines(enableOldDebugLines)
+ .Prescan()};
+ messages.Emit(std::cerr, allSources);
+ if (prescanner.anyFatalErrors()) {
+ return 1;
}
+ columns = std::numeric_limits<int>::max();
- Fortran::parser::ParseState state{sourceContent, sourceBytes};
- state.set_prescanned(prescan);
+ Fortran::parser::ParseState state{cooked};
state.set_inFixedForm(fixedForm);
state.set_enableBackslashEscapesInCharLiterals(backslashEscapes);
state.set_strictConformance(standard);
if (result.has_value() && !state.anyErrorRecovery()) {
std::cout << "demo PASS\n" << *result << '\n';
} else {
- std::cerr << "demo FAIL " << state.position() << '\n' << *state.messages();
+ std::cerr << "demo FAIL\n";
+ allSources.Identify(std::cerr, state.GetProvenance(), " ");
+ state.messages()->Emit(std::cerr, allSources);
return EXIT_FAILURE;
}
}