[flang] Clean out obsolete parsing code. Handle !cdir$ fixed and free in parser.
Work on scanning compiler directive lines.
Fix glitch in earlier change to parse-state.h.
Add ClassifyLine(), simplify some token sequence data lifetimes and interfaces.
Handle continued directives. Obey !DIR$ FIXED and FREE in prescanner.
Some refactoring of TokenSequence API, then support initial directives (FIXED, FREE, IGNORE_TKR).
Fix !DIR$ IGNORE_TKR syntax, manual was wrong.
Debugging directive scanning & parsing.
Profiling-directed speed-up - do not map cooked source locations to Provenance until a Message is emitted. Turn some non-nullable pointers into references.
Debugging.
Handle !DIR$ IVDEP too, it is in a test.
Accept directives in the execution part.
Original-commit: flang-compiler/f18@fb2ff367ec0609b7307771f927f22fd7bab5e180
Reviewed-on: https://github.com/flang-compiler/f18/pull/34
Tree-same-pre-rewrite: false
diff --git a/flang/lib/parser/basic-parsers.h b/flang/lib/parser/basic-parsers.h
index 72f41b0..b7b1f4d 100644
--- a/flang/lib/parser/basic-parsers.h
+++ b/flang/lib/parser/basic-parsers.h
@@ -1180,26 +1180,6 @@
constexpr FixedParser<true> ok;
constexpr FixedParser<false> cut;
-// guard(bool) returns a parser that succeeds iff its dynamic argument
-// value is true. The state is preserved.
-class GuardParser {
-public:
- using resultType = Success;
- constexpr GuardParser(const GuardParser &) = default;
- constexpr GuardParser(bool ok) : ok_{ok} {}
- constexpr std::optional<Success> Parse(ParseState *) const {
- if (ok_) {
- return {Success{}};
- }
- return {};
- }
-
-private:
- const bool ok_;
-};
-
-inline constexpr auto guard(bool truth) { return GuardParser(truth); }
-
// nextCh is a parser that succeeds if the parsing state is not
// at the end of its input, returning the next character location and
// advancing the parse when it does so.
@@ -1309,25 +1289,6 @@
return {state->userState()};
}
} getUserState;
-
-constexpr struct InFixedForm {
- using resultType = Success;
- constexpr InFixedForm() {}
- static std::optional<Success> Parse(ParseState *state) {
- if (state->inFixedForm()) {
- return {Success{}};
- }
- return {};
- }
-} inFixedForm;
-
-constexpr struct GetColumn {
- using resultType = int;
- constexpr GetColumn() {}
- static std::optional<int> Parse(ParseState *state) {
- return {state->column()};
- }
-} getColumn;
} // namespace parser
} // namespace Fortran
#endif // FORTRAN_PARSER_BASIC_PARSERS_H_
diff --git a/flang/lib/parser/grammar.h b/flang/lib/parser/grammar.h
index f32155d..919b4f7 100644
--- a/flang/lib/parser/grammar.h
+++ b/flang/lib/parser/grammar.h
@@ -148,6 +148,7 @@
constexpr Parser<EndSubroutineStmt> endSubroutineStmt; // R1537
constexpr Parser<EntryStmt> entryStmt; // R1541
constexpr Parser<ContainsStmt> containsStmt; // R1543
+constexpr Parser<CompilerDirective> compilerDirective;
// For a parser p, indirect(p) returns a parser that builds an indirect
// reference to p's return type.
@@ -166,23 +167,15 @@
// R611 label -> digit [digit]...
constexpr auto label = spaces >> digitString;
-static inline bool isColumnOkForFixedFormLabel(int &&column) {
- return column < 6;
-}
-
-constexpr auto isLabelOk = inFixedForm >>
- applyFunction(isColumnOkForFixedFormLabel, getColumn) ||
- pure(true);
-
template<typename PA>
using statementConstructor = construct<Statement<typename PA::resultType>>;
template<typename PA> inline constexpr auto unterminatedStatement(const PA &p) {
- return skipMany("\n"_tok) >>
- sourced(statementConstructor<PA>{}(maybe(label), isLabelOk, spaces >> p));
+ return skipEmptyLines >>
+ sourced(statementConstructor<PA>{}(maybe(label), spaces >> p));
}
-constexpr auto endOfLine = "\n"_ch / skipMany("\n"_tok) ||
+constexpr auto endOfLine = "\n"_ch / skipEmptyLines ||
fail<const char *>("expected end of line"_en_US);
constexpr auto endOfStmt = spaces >>
@@ -192,7 +185,7 @@
return unterminatedStatement(p) / endOfStmt;
}
-constexpr auto ignoredStatementPrefix = skipMany("\n"_tok) >>
+constexpr auto ignoredStatementPrefix = skipEmptyLines >>
maybe(label) >> spaces;
// Error recovery within statements: skip to the end of the line,
@@ -241,7 +234,8 @@
statement(Parser<OtherSpecificationStmt>{})) ||
construct<SpecificationConstruct>{}(
statement(indirect(typeDeclarationStmt))) ||
- construct<SpecificationConstruct>{}(indirect(Parser<StructureDef>{})))
+ construct<SpecificationConstruct>{}(indirect(Parser<StructureDef>{})) ||
+ construct<SpecificationConstruct>{}(indirect(compilerDirective)))
// R513 other-specification-stmt ->
// access-stmt | allocatable-stmt | asynchronous-stmt | bind-stmt |
@@ -393,7 +387,7 @@
TYPE_PARSER(
construct<Program>{}(
// statements consume only trailing noise; consume leading noise here.
- skipMany("\n"_tok) >>
+ skipEmptyLines >>
some(startNewSubprogram >> Parser<ProgramUnit>{} / endOfLine)) /
consumedAllInput)
@@ -555,7 +549,8 @@
construct<ExecutableConstruct>{}(indirect(Parser<SelectRankConstruct>{})) ||
construct<ExecutableConstruct>{}(indirect(Parser<SelectTypeConstruct>{})) ||
construct<ExecutableConstruct>{}(indirect(whereConstruct)) ||
- construct<ExecutableConstruct>{}(indirect(forallConstruct));
+ construct<ExecutableConstruct>{}(indirect(forallConstruct)) ||
+ construct<ExecutableConstruct>{}(indirect(compilerDirective));
// R510 execution-part-construct ->
// executable-construct | format-stmt | entry-stmt | data-stmt
@@ -3643,7 +3638,19 @@
TYPE_PARSER(construct<StmtFunctionStmt>{}(
name, parenthesized(optionalList(name)), "=" >> scalar(expr)))
-// Extension and deprecated statements
+// Directives, extensions, and deprecated statements
+// !DIR$ IVDEP
+// !DIR$ IGNORE_TKR [ [(tkr...)] name ]...
+constexpr auto beginDirective = skipEmptyLines >> spaces >> "!"_ch;
+constexpr auto endDirective = spaces >> endOfLine;
+constexpr auto ivdep = "DIR$ IVDEP" >> construct<CompilerDirective::IVDEP>{};
+constexpr auto ignore_tkr = "DIR$ IGNORE_TKR" >>
+ optionalList(construct<CompilerDirective::IgnoreTKR>{}(
+ defaulted(parenthesized(some("tkr"_ch))), name));
+TYPE_PARSER(beginDirective >> sourced(construct<CompilerDirective>{}(ivdep) ||
+ construct<CompilerDirective>{}(ignore_tkr)) /
+ endDirective)
+
TYPE_PARSER(
extension(construct<BasedPointerStmt>{}("POINTER (" >> objectName / ",",
objectName, maybe(Parser<ArraySpec>{}) / ")")))
diff --git a/flang/lib/parser/idioms.h b/flang/lib/parser/idioms.h
index 0048e6e..00805359 100644
--- a/flang/lib/parser/idioms.h
+++ b/flang/lib/parser/idioms.h
@@ -108,9 +108,10 @@
std::string EnumIndexToString(int index, const char *names);
#define ENUM_CLASS(NAME, ...) \
enum class NAME { __VA_ARGS__ }; \
- static inline std::string EnumToString(NAME e) \
- { return Fortran::parser::EnumIndexToString( \
- static_cast<int>(e), #__VA_ARGS__); }
+ static inline std::string EnumToString(NAME e) { \
+ return Fortran::parser::EnumIndexToString( \
+ static_cast<int>(e), #__VA_ARGS__); \
+ }
} // namespace parser
} // namespace Fortran
diff --git a/flang/lib/parser/message.cc b/flang/lib/parser/message.cc
index 0552753..cd2546d 100644
--- a/flang/lib/parser/message.cc
+++ b/flang/lib/parser/message.cc
@@ -50,9 +50,13 @@
}
Provenance Message::Emit(
- std::ostream &o, const AllSources &sources, bool echoSourceLine) const {
- if (!context_ || context_->Emit(o, sources, false) != provenance_) {
- sources.Identify(o, provenance_, "", echoSourceLine);
+ std::ostream &o, const CookedSource &cooked, bool echoSourceLine) const {
+ Provenance provenance{provenance_};
+ if (cookedSourceLocation_ != nullptr) {
+ provenance = cooked.GetProvenance(cookedSourceLocation_).start();
+ }
+ if (!context_ || context_->Emit(o, cooked, false) != provenance) {
+ cooked.allSources().Identify(o, provenance, "", echoSourceLine);
}
o << " ";
if (string_.empty()) {
@@ -71,10 +75,11 @@
o << string_;
}
o << '\n';
- return provenance_;
+ return provenance;
}
-void Messages::Emit(std::ostream &o, const char *prefix) const {
+void Messages::Emit(
+ std::ostream &o, const char *prefix, bool echoSourceLines) const {
for (const auto &msg : messages_) {
if (prefix) {
o << prefix;
@@ -82,7 +87,7 @@
if (msg.context()) {
o << "In the context ";
}
- msg.Emit(o, allSources_);
+ msg.Emit(o, cooked_, echoSourceLines);
}
}
} // namespace parser
diff --git a/flang/lib/parser/message.h b/flang/lib/parser/message.h
index 4a6fbd0..d55e7d5 100644
--- a/flang/lib/parser/message.h
+++ b/flang/lib/parser/message.h
@@ -73,6 +73,10 @@
public:
Message() {}
Message(const Message &) = default;
+ Message(Message &&) = default;
+ Message &operator=(const Message &that) = default;
+ Message &operator=(Message &&that) = default;
+
Message(Provenance p, MessageFixedText t, MessageContext c = nullptr)
: provenance_{p}, text_{t}, context_{c} {}
Message(Provenance p, MessageFormattedText &&s, MessageContext c = nullptr)
@@ -80,22 +84,35 @@
Message(Provenance p, MessageExpectedText t, MessageContext c = nullptr)
: provenance_{p}, text_{t.AsMessageFixedText()},
isExpectedText_{true}, context_{c} {}
- Message(Message &&) = default;
- Message &operator=(const Message &that) = default;
- Message &operator=(Message &&that) = default;
+
+ Message(const char *csl, MessageFixedText t, MessageContext c = nullptr)
+ : cookedSourceLocation_{csl}, text_{t}, context_{c} {}
+ Message(const char *csl, MessageFormattedText &&s, MessageContext c = nullptr)
+ : cookedSourceLocation_{csl}, string_{s.MoveString()}, context_{c} {}
+ Message(const char *csl, MessageExpectedText t, MessageContext c = nullptr)
+ : cookedSourceLocation_{csl}, text_{t.AsMessageFixedText()},
+ isExpectedText_{true}, context_{c} {}
bool operator<(const Message &that) const {
- return provenance_ < that.provenance_;
+ if (cookedSourceLocation_ != nullptr) {
+ return cookedSourceLocation_ < that.cookedSourceLocation_;
+ } else if (that.cookedSourceLocation_ != nullptr) {
+ return false;
+ } else {
+ return provenance_ < that.provenance_;
+ }
}
Provenance provenance() const { return provenance_; }
+ const char *cookedSourceLocation() const { return cookedSourceLocation_; }
MessageContext context() const { return context_; }
Provenance Emit(
- std::ostream &, const AllSources &, bool echoSourceLine = true) const;
+ std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
private:
Provenance provenance_;
+ const char *cookedSourceLocation_{nullptr};
MessageFixedText text_;
bool isExpectedText_{false}; // implies "expected '%s'"_en_US
std::string string_;
@@ -109,9 +126,9 @@
using iterator = list_type::iterator;
using const_iterator = list_type::const_iterator;
- explicit Messages(const AllSources &sources) : allSources_{sources} {}
+ explicit Messages(const CookedSource &cooked) : cooked_{cooked} {}
Messages(Messages &&that)
- : allSources_{that.allSources_}, messages_{std::move(that.messages_)},
+ : cooked_{that.cooked_}, messages_{std::move(that.messages_)},
last_{that.last_} {}
Messages &operator=(Messages &&that) {
swap(that);
@@ -130,10 +147,18 @@
const_iterator cbegin() const { return messages_.cbegin(); }
const_iterator cend() const { return messages_.cend(); }
- const AllSources &allSources() const { return allSources_; }
+ const CookedSource &cooked() const { return cooked_; }
+
+ bool IsValidLocation(const Message &m) {
+ if (auto p{m.cookedSourceLocation()}) {
+ return cooked_.IsValid(p);
+ } else {
+ return cooked_.IsValid(m.provenance());
+ }
+ }
Message &Put(Message &&m) {
- CHECK(allSources_.IsValid(m.provenance()));
+ CHECK(IsValidLocation(m));
if (messages_.empty()) {
messages_.emplace_front(std::move(m));
last_ = messages_.begin();
@@ -154,10 +179,11 @@
}
}
- void Emit(std::ostream &, const char *prefix = nullptr) const;
+ void Emit(std::ostream &, const char *prefix = nullptr,
+ bool echoSourceLines = true) const;
private:
- const AllSources &allSources_;
+ const CookedSource &cooked_;
list_type messages_;
iterator last_; // valid iff messages_ nonempty
};
diff --git a/flang/lib/parser/parse-state.h b/flang/lib/parser/parse-state.h
index a6e6915..63de742 100644
--- a/flang/lib/parser/parse-state.h
+++ b/flang/lib/parser/parse-state.h
@@ -28,22 +28,21 @@
// TODO: Add a constructor for parsing a normalized module file.
ParseState(const CookedSource &cooked)
: cooked_{cooked}, p_{&cooked[0]}, limit_{p_ + cooked.size()},
- messages_{*cooked.allSources()} {}
+ messages_{cooked} {}
ParseState(const ParseState &that)
: cooked_{that.cooked_}, p_{that.p_}, limit_{that.limit_},
- column_{that.column_}, messages_{*that.cooked_.allSources()},
- userState_{that.userState_}, inFixedForm_{that.inFixedForm_},
- encoding_{that.encoding_}, strictConformance_{that.strictConformance_},
+ messages_{that.cooked_}, userState_{that.userState_},
+ inFixedForm_{that.inFixedForm_}, encoding_{that.encoding_},
+ strictConformance_{that.strictConformance_},
warnOnNonstandardUsage_{that.warnOnNonstandardUsage_},
warnOnDeprecatedUsage_{that.warnOnDeprecatedUsage_},
anyErrorRecovery_{that.anyErrorRecovery_},
anyConformanceViolation_{that.anyConformanceViolation_} {}
ParseState(ParseState &&that)
: 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_},
- inFixedForm_{that.inFixedForm_}, encoding_{that.encoding_},
- strictConformance_{that.strictConformance_},
+ messages_{std::move(that.messages_)}, context_{std::move(that.context_)},
+ userState_{that.userState_}, inFixedForm_{that.inFixedForm_},
+ encoding_{that.encoding_}, strictConformance_{that.strictConformance_},
warnOnNonstandardUsage_{that.warnOnNonstandardUsage_},
warnOnDeprecatedUsage_{that.warnOnDeprecatedUsage_},
anyErrorRecovery_{that.anyErrorRecovery_},
@@ -62,7 +61,6 @@
}
const CookedSource &cooked() const { return cooked_; }
- int column() const { return column_; }
Messages *messages() { return &messages_; }
bool anyErrorRecovery() const { return anyErrorRecovery_; }
@@ -134,34 +132,23 @@
Message &PutMessage(MessageExpectedText &&t) {
return PutMessage(p_, std::move(t));
}
+
Message &PutMessage(const char *at, MessageFixedText t) {
- return PutMessage(GetProvenance(at), t);
- }
- Message &PutMessage(const char *at, MessageFormattedText &&t) {
- return PutMessage(GetProvenance(at), std::move(t));
- }
- Message &PutMessage(const char *at, MessageExpectedText &&t) {
- return PutMessage(GetProvenance(at), std::move(t));
- }
- Message &PutMessage(Provenance at, MessageFixedText t) {
return messages_.Put(Message{at, t, context_});
}
- Message &PutMessage(Provenance at, MessageFormattedText &&t) {
+ Message &PutMessage(const char *at, MessageFormattedText &&t) {
return messages_.Put(Message{at, std::move(t), context_});
}
- Message &PutMessage(Provenance at, MessageExpectedText &&t) {
+ Message &PutMessage(const char *at, MessageExpectedText &&t) {
return messages_.Put(Message{at, std::move(t), context_});
}
bool IsAtEnd() const { return p_ >= limit_; }
- char UncheckedAdvance() {
- ++column_;
- char ch{*p_++};
- if (ch == '\n') {
- column_ = 1;
- }
- return ch;
+ char UncheckedAdvance(std::size_t n = 1) {
+ char result{*p_};
+ p_ += n;
+ return result;
}
std::optional<char> GetNextChar() {
@@ -182,7 +169,6 @@
// Text remaining to be parsed
const CookedSource &cooked_;
const char *p_{nullptr}, *limit_{nullptr};
- int column_{1};
// Accumulated messages and current nested context.
Messages messages_;
diff --git a/flang/lib/parser/parse-tree-visitor.h b/flang/lib/parser/parse-tree-visitor.h
index 2760809..fbc2b6b 100644
--- a/flang/lib/parser/parse-tree-visitor.h
+++ b/flang/lib/parser/parse-tree-visitor.h
@@ -5,6 +5,7 @@
#include <cstddef>
#include <optional>
#include <tuple>
+#include <utility>
#include <variant>
/// Parse tree visitor
@@ -67,6 +68,13 @@
visitor.Post(x);
}
}
+template<typename A, typename B, typename V>
+void Walk(const std::pair<A, B> &x, V &visitor) {
+ if (visitor.Pre(x)) {
+ Walk(x.first, visitor);
+ Walk(x.second, visitor);
+ }
+}
// Trait-determined traversal of empty, tuple, union, and wrapper classes.
template<typename A, typename V>
diff --git a/flang/lib/parser/parse-tree.h b/flang/lib/parser/parse-tree.h
index c99f952..9b5e0e4 100644
--- a/flang/lib/parser/parse-tree.h
+++ b/flang/lib/parser/parse-tree.h
@@ -243,7 +243,8 @@
struct ReturnStmt; // R1542
struct StmtFunctionStmt; // R1544
-// Extension and deprecated statements
+// Directives, extensions, and deprecated statements
+struct CompilerDirective;
struct BasedPointerStmt;
struct StructureDef;
struct ArithmeticIfStmt;
@@ -314,12 +315,10 @@
// A wrapper for xzy-stmt productions that are statements, so that
// source provenances and labels have a uniform representation.
template<typename A> struct Statement {
- Statement(std::optional<long> &&lab, bool &&accept, A &&s)
- : label(std::move(lab)), isLabelInAcceptableField{accept},
- statement(std::move(s)) {}
+ Statement(std::optional<long> &&lab, A &&s)
+ : label(std::move(lab)), statement(std::move(s)) {}
CharBlock source;
std::optional<Label> label;
- bool isLabelInAcceptableField{true};
A statement;
};
@@ -361,7 +360,8 @@
Statement<Indirection<OldParameterStmt>>,
Statement<Indirection<ProcedureDeclarationStmt>>,
Statement<OtherSpecificationStmt>,
- Statement<Indirection<TypeDeclarationStmt>>, Indirection<StructureDef>>
+ Statement<Indirection<TypeDeclarationStmt>>, Indirection<StructureDef>,
+ Indirection<CompilerDirective>>
u;
};
@@ -470,7 +470,8 @@
Statement<Indirection<LabelDoStmt>>, Statement<Indirection<EndDoStmt>>,
Indirection<DoConstruct>, Indirection<IfConstruct>,
Indirection<SelectRankConstruct>, Indirection<SelectTypeConstruct>,
- Indirection<WhereConstruct>, Indirection<ForallConstruct>>
+ Indirection<WhereConstruct>, Indirection<ForallConstruct>,
+ Indirection<CompilerDirective>>
u;
};
@@ -3121,6 +3122,20 @@
std::tuple<Name, std::list<Name>, Scalar<Expr>> t;
};
+// Compiler directives
+// !DIR$ IVDEP
+// !DIR$ IGNORE_TKR [ [(tkr...)] name ]...
+struct CompilerDirective {
+ UNION_CLASS_BOILERPLATE(CompilerDirective);
+ struct IgnoreTKR {
+ TUPLE_CLASS_BOILERPLATE(IgnoreTKR);
+ std::tuple<std::list<const char *>, Name> t;
+ };
+ EMPTY_CLASS(IVDEP);
+ CharBlock source;
+ std::variant<std::list<IgnoreTKR>, IVDEP> u;
+};
+
// Legacy extensions
struct BasedPointerStmt {
TUPLE_CLASS_BOILERPLATE(BasedPointerStmt);
diff --git a/flang/lib/parser/parsing.cc b/flang/lib/parser/parsing.cc
index d62d3aa..530f55d 100644
--- a/flang/lib/parser/parsing.cc
+++ b/flang/lib/parser/parsing.cc
@@ -33,7 +33,7 @@
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);
@@ -41,13 +41,13 @@
preprocessor.Undefine(predef.first);
}
}
- Prescanner prescanner{&messages_, &cooked_, &preprocessor};
+ Prescanner prescanner{messages_, cooked_, preprocessor};
prescanner.set_fixedForm(options.isFixedForm)
.set_fixedFormColumnLimit(options.fixedFormColumns)
.set_encoding(options.encoding)
.set_enableBackslashEscapesInCharLiterals(options.enableBackslashEscapes)
- .set_enableOldDebugLines(options.enableOldDebugLines);
-// TODO in development .AddCompilerDirectiveSentinel("dir$");
+ .set_enableOldDebugLines(options.enableOldDebugLines)
+ .AddCompilerDirectiveSentinel("dir$");
ProvenanceRange range{
allSources_.AddIncludedFile(*sourceFile, ProvenanceRange{})};
anyFatalError_ = !prescanner.Prescan(range);
diff --git a/flang/lib/parser/parsing.h b/flang/lib/parser/parsing.h
index 8880c72..6214e7b 100644
--- a/flang/lib/parser/parsing.h
+++ b/flang/lib/parser/parsing.h
@@ -51,8 +51,8 @@
private:
Options options_;
AllSources allSources_;
- Messages messages_{allSources_};
- CookedSource cooked_{&allSources_};
+ CookedSource cooked_{allSources_};
+ Messages messages_{cooked_};
bool anyFatalError_{false};
bool consumedWholeFile_{false};
Provenance finalRestingPlace_;
diff --git a/flang/lib/parser/preprocessor.cc b/flang/lib/parser/preprocessor.cc
index ac23bca..30634be 100644
--- a/flang/lib/parser/preprocessor.cc
+++ b/flang/lib/parser/preprocessor.cc
@@ -28,10 +28,10 @@
argumentCount_(argNames.size()), isVariadic_{isVariadic},
replacement_{Tokenize(argNames, repl, firstToken, tokens)} {}
-Definition::Definition(const std::string &predefined, AllSources *sources)
- : isPredefined_{true},
- replacement_{
- predefined, sources->AddCompilerInsertion(predefined).start()} {}
+Definition::Definition(const std::string &predefined, AllSources &sources)
+ : isPredefined_{true}, replacement_{predefined,
+ sources.AddCompilerInsertion(predefined).start()} {
+}
bool Definition::set_isDisabled(bool disable) {
bool was{isDisabled_};
@@ -53,7 +53,7 @@
}
TokenSequence result;
for (std::size_t j{0}; j < tokens; ++j) {
- CharBlock tok{token[firstToken + j]};
+ CharBlock tok{token.TokenAt(firstToken + j)};
if (IsLegalIdentifierStart(tok)) {
auto it = args.find(tok.ToString());
if (it != args.end()) {
@@ -67,8 +67,8 @@
}
static std::size_t AfterLastNonBlank(const TokenSequence &tokens) {
- for (std::size_t j{tokens.size()}; j > 0; --j) {
- if (!tokens[j - 1].IsBlank()) {
+ for (std::size_t j{tokens.SizeInTokens()}; j > 0; --j) {
+ if (!tokens.TokenAt(j - 1).IsBlank()) {
return j;
}
}
@@ -76,12 +76,12 @@
}
static TokenSequence Stringify(
- const TokenSequence &tokens, AllSources *allSources) {
+ const TokenSequence &tokens, AllSources &allSources) {
TokenSequence result;
- Provenance quoteProvenance{allSources->CompilerInsertionProvenance('"')};
+ Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')};
result.PutNextTokenChar('"', quoteProvenance);
- for (std::size_t j{0}; j < tokens.size(); ++j) {
- const CharBlock &token{tokens[j]};
+ for (std::size_t j{0}; j < tokens.SizeInTokens(); ++j) {
+ const CharBlock &token{tokens.TokenAt(j)};
std::size_t bytes{token.size()};
for (std::size_t k{0}; k < bytes; ++k) {
char ch{token[k]};
@@ -98,14 +98,14 @@
}
TokenSequence Definition::Apply(
- const std::vector<TokenSequence> &args, AllSources *allSources) {
+ const std::vector<TokenSequence> &args, AllSources &allSources) {
TokenSequence result;
bool pasting{false};
bool skipping{false};
int parenthesesNesting{0};
- std::size_t tokens{replacement_.size()};
+ std::size_t tokens{replacement_.SizeInTokens()};
for (std::size_t j{0}; j < tokens; ++j) {
- const CharBlock &token{replacement_[j]};
+ const CharBlock &token{replacement_.TokenAt(j)};
std::size_t bytes{token.size()};
if (skipping) {
if (bytes == 1) {
@@ -124,16 +124,16 @@
}
std::size_t afterLastNonBlank{AfterLastNonBlank(result)};
if (afterLastNonBlank > 0 &&
- result[afterLastNonBlank - 1].ToString() == "#") {
+ result.TokenAt(afterLastNonBlank - 1).ToString() == "#") {
// stringifying
- while (result.size() >= afterLastNonBlank) {
+ while (result.SizeInTokens() >= afterLastNonBlank) {
result.pop_back();
}
result.Put(Stringify(args[index], allSources));
} else {
- std::size_t argTokens{args[index].size()};
+ std::size_t argTokens{args[index].SizeInTokens()};
for (std::size_t k{0}; k < argTokens; ++k) {
- if (!pasting || !args[index][k].IsBlank()) {
+ if (!pasting || !args[index].TokenAt(k).IsBlank()) {
result.Put(args[index], k);
pasting = false;
}
@@ -142,7 +142,8 @@
} else if (bytes == 2 && token[0] == '#' && token[1] == '#') {
// Token pasting operator in body (not expanded argument); discard any
// immediately preceding white space, then reopen the last token.
- while (!result.empty() && result[result.size() - 1].IsBlank()) {
+ while (!result.empty() &&
+ result.TokenAt(result.SizeInTokens() - 1).IsBlank()) {
result.pop_back();
}
if (!result.empty()) {
@@ -153,7 +154,7 @@
// Delete whitespace immediately following ## in the body.
} else if (bytes == 11 && isVariadic_ &&
token.ToString() == "__VA_ARGs__") {
- Provenance commaProvenance{allSources->CompilerInsertionProvenance(',')};
+ Provenance commaProvenance{allSources.CompilerInsertionProvenance(',')};
for (std::size_t k{argumentCount_}; k < args.size(); ++k) {
if (k > argumentCount_) {
result.Put(","s, commaProvenance);
@@ -161,7 +162,7 @@
result.Put(args[k]);
}
} else if (bytes == 10 && isVariadic_ && token.ToString() == "__VA_OPT__" &&
- j + 2 < tokens && replacement_[j + 1].ToString() == "(" &&
+ j + 2 < tokens && replacement_.TokenAt(j + 1).ToString() == "(" &&
parenthesesNesting == 0) {
parenthesesNesting = 1;
skipping = args.size() == argumentCount_;
@@ -187,7 +188,7 @@
std::strftime(buffer, sizeof buffer, format, std::localtime(&now))};
}
-Preprocessor::Preprocessor(AllSources *allSources) : allSources_{allSources} {
+Preprocessor::Preprocessor(AllSources &allSources) : allSources_{allSources} {
// Capture current local date & time once now to avoid having the values
// of __DATE__ or __TIME__ change during compilation.
std::time_t now;
@@ -209,55 +210,55 @@
void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); }
-bool Preprocessor::MacroReplacement(const TokenSequence &input,
- const Prescanner &prescanner, TokenSequence *result) {
+std::optional<TokenSequence> Preprocessor::MacroReplacement(
+ const TokenSequence &input, const Prescanner &prescanner) {
// Do quick scan for any use of a defined name.
- std::size_t tokens{input.size()};
+ std::size_t tokens{input.SizeInTokens()};
std::size_t j;
for (j = 0; j < tokens; ++j) {
- std::size_t bytes{input[j].size()};
- if (bytes > 0 && IsLegalIdentifierStart(input[j][0]) &&
- IsNameDefined(input[j])) {
+ CharBlock token{input.TokenAt(j)};
+ if (!token.empty() && IsLegalIdentifierStart(token[0]) &&
+ IsNameDefined(token)) {
break;
}
}
if (j == tokens) {
- return false; // contains nothing that would be replaced
+ return {}; // input contains nothing that would be replaced
}
- result->Put(input, 0, j);
+ TokenSequence result{input, 0, j};
for (; j < tokens; ++j) {
- const CharBlock &token{input[j]};
+ const CharBlock &token{input.TokenAt(j)};
if (token.IsBlank() || !IsLegalIdentifierStart(token[0])) {
- result->Put(input, j);
+ result.Put(input, j);
continue;
}
auto it = definitions_.find(token);
if (it == definitions_.end()) {
- result->Put(input, j);
+ result.Put(input, j);
continue;
}
Definition &def{it->second};
if (def.isDisabled()) {
- result->Put(input, j);
+ result.Put(input, j);
continue;
}
if (!def.isFunctionLike()) {
if (def.isPredefined()) {
- std::string name{def.replacement()[0].ToString()};
+ std::string name{def.replacement().TokenAt(0).ToString()};
std::string repl;
if (name == "__FILE__") {
repl = "\""s +
- allSources_->GetPath(prescanner.GetCurrentProvenance()) + '"';
+ allSources_.GetPath(prescanner.GetCurrentProvenance()) + '"';
} else if (name == "__LINE__") {
std::stringstream ss;
- ss << allSources_->GetLineNumber(prescanner.GetCurrentProvenance());
+ ss << allSources_.GetLineNumber(prescanner.GetCurrentProvenance());
repl = ss.str();
}
if (!repl.empty()) {
- ProvenanceRange insert{allSources_->AddCompilerInsertion(repl)};
- ProvenanceRange call{allSources_->AddMacroCall(
+ ProvenanceRange insert{allSources_.AddCompilerInsertion(repl)};
+ ProvenanceRange call{allSources_.AddMacroCall(
insert, input.GetTokenProvenanceRange(j), repl)};
- result->Put(repl, call.start());
+ result.Put(repl, call.start());
continue;
}
}
@@ -268,8 +269,8 @@
ProvenanceRange from{def.replacement().GetProvenanceRange()};
ProvenanceRange use{input.GetTokenProvenanceRange(j)};
ProvenanceRange newRange{
- allSources_->AddMacroCall(from, use, replaced.ToString())};
- result->Put(replaced, newRange);
+ allSources_.AddMacroCall(from, use, replaced.ToString())};
+ result.Put(replaced, newRange);
}
continue;
}
@@ -278,20 +279,21 @@
std::size_t k{j};
bool leftParen{false};
while (++k < tokens) {
- const CharBlock &lookAhead{input[k]};
+ const CharBlock &lookAhead{input.TokenAt(k)};
if (!lookAhead.IsBlank() && lookAhead[0] != '\n') {
leftParen = lookAhead[0] == '(' && lookAhead.size() == 1;
break;
}
}
if (!leftParen) {
- result->Put(input, j);
+ result.Put(input, j);
continue;
}
std::vector<std::size_t> argStart{++k};
for (int nesting{0}; k < tokens; ++k) {
- if (input[k].size() == 1) {
- char ch{input[k][0]};
+ CharBlock token{input.TokenAt(k)};
+ if (token.size() == 1) {
+ char ch{token[0]};
if (ch == '(') {
++nesting;
} else if (ch == ')') {
@@ -306,7 +308,7 @@
}
if (k >= tokens || argStart.size() < def.argumentCount() ||
(argStart.size() > def.argumentCount() && !def.isVariadic())) {
- result->Put(input, j);
+ result.Put(input, j);
continue;
}
std::vector<TokenSequence> args;
@@ -324,24 +326,26 @@
ProvenanceRange from{def.replacement().GetProvenanceRange()};
ProvenanceRange use{input.GetIntervalProvenanceRange(j, k - j)};
ProvenanceRange newRange{
- allSources_->AddMacroCall(from, use, replaced.ToString())};
- result->Put(replaced, newRange);
+ allSources_.AddMacroCall(from, use, replaced.ToString())};
+ result.Put(replaced, newRange);
}
j = k; // advance to the terminal ')'
}
- return true;
+ return {result};
}
TokenSequence Preprocessor::ReplaceMacros(
const TokenSequence &tokens, const Prescanner &prescanner) {
- TokenSequence repl;
- return MacroReplacement(tokens, prescanner, &repl) ? repl : tokens;
+ if (std::optional<TokenSequence> repl{MacroReplacement(tokens, prescanner)}) {
+ return std::move(*repl);
+ }
+ return tokens;
}
static std::size_t SkipBlanks(
const TokenSequence &tokens, std::size_t at, std::size_t lastToken) {
for (; at < lastToken; ++at) {
- if (!tokens[at].IsBlank()) {
+ if (!tokens.TokenAt(at).IsBlank()) {
break;
}
}
@@ -359,12 +363,12 @@
}
void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
- std::size_t tokens{dir.size()};
+ std::size_t tokens{dir.SizeInTokens()};
std::size_t j{SkipBlanks(dir, 0, tokens)};
if (j == tokens) {
return;
}
- if (dir[j].ToString() != "#") {
+ if (dir.TokenAt(j).ToString() != "#") {
prescanner->Error("missing '#'"_en_US, dir.GetTokenProvenance(j));
return;
}
@@ -372,15 +376,15 @@
if (j == tokens) {
return;
}
- if (IsDecimalDigit(dir[j][0]) || dir[j][0] == '"') {
+ if (IsDecimalDigit(dir.TokenAt(j)[0]) || dir.TokenAt(j)[0] == '"') {
return; // TODO: treat as #line
}
std::size_t dirOffset{j};
- std::string dirName{ToLowerCaseLetters(dir[dirOffset].ToString())};
+ std::string dirName{ToLowerCaseLetters(dir.TokenAt(dirOffset).ToString())};
j = SkipBlanks(dir, j + 1, tokens);
CharBlock nameToken;
- if (j < tokens && IsLegalIdentifierStart(dir[j][0])) {
- nameToken = dir[j];
+ if (j < tokens && IsLegalIdentifierStart(dir.TokenAt(j)[0])) {
+ nameToken = dir.TokenAt(j);
}
if (dirName == "line") {
// TODO: implement #line
@@ -392,13 +396,14 @@
}
nameToken = SaveTokenAsName(nameToken);
definitions_.erase(nameToken);
- if (++j < tokens && dir[j].size() == 1 && dir[j][0] == '(') {
+ if (++j < tokens && dir.TokenAt(j).size() == 1 &&
+ dir.TokenAt(j)[0] == '(') {
j = SkipBlanks(dir, j + 1, tokens);
std::vector<std::string> argName;
bool isVariadic{false};
- if (dir[j].ToString() != ")") {
+ if (dir.TokenAt(j).ToString() != ")") {
while (true) {
- std::string an{dir[j].ToString()};
+ std::string an{dir.TokenAt(j).ToString()};
if (an == "...") {
isVariadic = true;
} else {
@@ -416,7 +421,7 @@
dir.GetTokenProvenance(tokens - 1));
return;
}
- std::string punc{dir[j].ToString()};
+ std::string punc{dir.TokenAt(j).ToString()};
if (punc == ")") {
break;
}
@@ -542,8 +547,8 @@
return;
}
std::string include;
- if (dir[j].ToString() == "<") {
- if (dir[tokens - 1].ToString() != ">") {
+ if (dir.TokenAt(j).ToString() == "<") {
+ if (dir.TokenAt(tokens - 1).ToString() != ">") {
prescanner->Error("#include: expected '>' at end of directive"_en_US,
dir.GetTokenProvenance(tokens - 1));
return;
@@ -551,7 +556,7 @@
TokenSequence braced{dir, j + 1, tokens - j - 2};
include = ReplaceMacros(braced, *prescanner).ToString();
} else if (j + 1 == tokens &&
- (include = dir[j].ToString()).substr(0, 1) == "\"" &&
+ (include = dir.TokenAt(j).ToString()).substr(0, 1) == "\"" &&
include.substr(include.size() - 1, 1) == "\"") {
include = include.substr(1, include.size() - 2);
} else {
@@ -565,7 +570,7 @@
return;
}
std::stringstream error;
- const SourceFile *included{allSources_->Open(include, &error)};
+ const SourceFile *included{allSources_.Open(include, &error)};
if (included == nullptr) {
prescanner->Error(
MessageFormattedText("#include: %s"_en_US, error.str().data()),
@@ -576,7 +581,7 @@
return;
}
ProvenanceRange fileRange{
- allSources_->AddIncludedFile(*included, dir.GetProvenanceRange())};
+ allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
if (!Prescanner{*prescanner}.Prescan(fileRange)) {
prescanner->set_anyFatalErrors();
}
@@ -599,9 +604,9 @@
static std::string GetDirectiveName(
const TokenSequence &line, std::size_t *rest) {
- std::size_t tokens{line.size()};
+ std::size_t tokens{line.SizeInTokens()};
std::size_t j{SkipBlanks(line, 0, tokens)};
- if (j == tokens || line[j].ToString() != "#") {
+ if (j == tokens || line.TokenAt(j).ToString() != "#") {
*rest = tokens;
return "";
}
@@ -611,7 +616,7 @@
return "";
}
*rest = SkipBlanks(line, j + 1, tokens);
- return ToLowerCaseLetters(line[j].ToString());
+ return ToLowerCaseLetters(line.TokenAt(j).ToString());
}
void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
@@ -637,7 +642,8 @@
return;
}
if (dn == "elif" &&
- IsIfPredicateTrue(line, rest, line.size() - rest, prescanner)) {
+ IsIfPredicateTrue(
+ line, rest, line.SizeInTokens() - rest, prescanner)) {
ifStack_.push(CanDeadElseAppear::Yes);
return;
}
@@ -743,7 +749,7 @@
opNameMap[","] = COMMA;
}
- std::size_t tokens{token.size()};
+ std::size_t tokens{token.SizeInTokens()};
if (*atToken >= tokens) {
*error = Message{
token.GetTokenProvenance(tokens - 1), "incomplete expression"_en_US};
@@ -752,7 +758,7 @@
// Parse and evaluate a primary or a unary operator and its operand.
std::size_t opAt{*atToken};
- std::string t{token[opAt].ToString()};
+ std::string t{token.TokenAt(opAt).ToString()};
enum Operator op;
std::int64_t left{0};
if (t == "(") {
@@ -776,8 +782,8 @@
} else if (t == "-") {
op = UMINUS;
} else if (t == "." && *atToken + 2 < tokens &&
- ToLowerCaseLetters(token[*atToken + 1].ToString()) == "not" &&
- token[*atToken + 2].ToString() == ".") {
+ ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) == "not" &&
+ token.TokenAt(*atToken + 2).ToString() == ".") {
op = NOT;
*atToken += 2;
} else {
@@ -803,7 +809,7 @@
}
switch (op) {
case PARENS:
- if (*atToken < tokens && token[*atToken].ToString() == ")") {
+ if (*atToken < tokens && token.TokenAt(*atToken).ToString() == ")") {
++*atToken;
} else {
*error = Message{token.GetTokenProvenance(tokens - 1),
@@ -825,10 +831,10 @@
// Parse and evaluate a binary operator and its second operand, if present.
int advance{1};
- t = token[*atToken].ToString();
+ t = token.TokenAt(*atToken).ToString();
if (t == "." && *atToken + 2 < tokens &&
- token[*atToken + 2].ToString() == ".") {
- t += ToLowerCaseLetters(token[*atToken + 1].ToString()) + '.';
+ token.TokenAt(*atToken + 2).ToString() == ".") {
+ t += ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) + '.';
advance = 3;
}
auto it = opNameMap.find(t);
@@ -931,7 +937,7 @@
case EQV: return -(!left == !right);
case NEQV: return -(!left != !right);
case SELECT:
- if (*atToken >= tokens || token[*atToken].ToString() != ":") {
+ if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") {
*error = Message{token.GetTokenProvenance(opAt),
"':' required in selection expression"_en_US};
return left;
@@ -951,32 +957,34 @@
std::size_t first, std::size_t exprTokens, Prescanner *prescanner) {
TokenSequence expr1{StripBlanks(expr, first, first + exprTokens)};
TokenSequence expr2;
- for (std::size_t j{0}; j < expr1.size(); ++j) {
- if (ToLowerCaseLetters(expr1[j].ToString()) == "defined") {
+ for (std::size_t j{0}; j < expr1.SizeInTokens(); ++j) {
+ if (ToLowerCaseLetters(expr1.TokenAt(j).ToString()) == "defined") {
CharBlock name;
- if (j + 3 < expr1.size() && expr1[j + 1].ToString() == "(" &&
- expr1[j + 3].ToString() == ")") {
- name = expr1[j + 2];
+ if (j + 3 < expr1.SizeInTokens() &&
+ expr1.TokenAt(j + 1).ToString() == "(" &&
+ expr1.TokenAt(j + 3).ToString() == ")") {
+ name = expr1.TokenAt(j + 2);
j += 3;
- } else if (j + 1 < expr1.size() && IsLegalIdentifierStart(expr1[j + 1])) {
- name = expr1[j++];
+ } else if (j + 1 < expr1.SizeInTokens() &&
+ IsLegalIdentifierStart(expr1.TokenAt(j + 1))) {
+ name = expr1.TokenAt(j++);
}
if (!name.empty()) {
char truth{IsNameDefined(name) ? '1' : '0'};
- expr2.Put(&truth, 1, allSources_->CompilerInsertionProvenance(truth));
+ expr2.Put(&truth, 1, allSources_.CompilerInsertionProvenance(truth));
continue;
}
}
expr2.Put(expr1, j);
}
TokenSequence expr3{ReplaceMacros(expr2, *prescanner)};
- TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
+ TokenSequence expr4{StripBlanks(expr3, 0, expr3.SizeInTokens())};
std::size_t atToken{0};
std::optional<Message> error;
bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
if (error.has_value()) {
prescanner->Error(std::move(*error));
- } else if (atToken < expr4.size()) {
+ } else if (atToken < expr4.SizeInTokens()) {
prescanner->Error(atToken == 0 ? "could not parse any expression"_en_US
: "excess characters after expression"_en_US,
expr4.GetTokenProvenance(atToken));
diff --git a/flang/lib/parser/preprocessor.h b/flang/lib/parser/preprocessor.h
index 2a67944..e0a50c7 100644
--- a/flang/lib/parser/preprocessor.h
+++ b/flang/lib/parser/preprocessor.h
@@ -28,7 +28,7 @@
Definition(const TokenSequence &, std::size_t firstToken, std::size_t tokens);
Definition(const std::vector<std::string> &argNames, const TokenSequence &,
std::size_t firstToken, std::size_t tokens, bool isVariadic = false);
- Definition(const std::string &predefined, AllSources *);
+ Definition(const std::string &predefined, AllSources &);
bool isFunctionLike() const { return isFunctionLike_; }
std::size_t argumentCount() const { return argumentCount_; }
@@ -39,7 +39,7 @@
bool set_isDisabled(bool disable);
- TokenSequence Apply(const std::vector<TokenSequence> &args, AllSources *);
+ TokenSequence Apply(const std::vector<TokenSequence> &args, AllSources &);
private:
static TokenSequence Tokenize(const std::vector<std::string> &argNames,
@@ -56,17 +56,13 @@
// Preprocessing state
class Preprocessor {
public:
- explicit Preprocessor(AllSources *);
+ explicit Preprocessor(AllSources &);
void Define(std::string macro, std::string value);
void Undefine(std::string macro);
- // When the input contains macros to be replaced, the new token sequence
- // is appended to the output and the returned value is true. When
- // no macro replacement is necessary, the output is unmodified and the
- // return value is false.
- bool MacroReplacement(
- const TokenSequence &, const Prescanner &, TokenSequence *);
+ std::optional<TokenSequence> MacroReplacement(
+ const TokenSequence &, const Prescanner &);
// Implements a preprocessor directive.
void Directive(const TokenSequence &, Prescanner *);
@@ -83,7 +79,7 @@
bool IsIfPredicateTrue(const TokenSequence &expr, std::size_t first,
std::size_t exprTokens, Prescanner *);
- AllSources *allSources_;
+ AllSources &allSources_;
std::list<std::string> names_;
std::unordered_map<CharBlock, Definition> definitions_;
std::stack<CanDeadElseAppear> ifStack_;
diff --git a/flang/lib/parser/prescan.cc b/flang/lib/parser/prescan.cc
index 131e35d..8cd94fb 100644
--- a/flang/lib/parser/prescan.cc
+++ b/flang/lib/parser/prescan.cc
@@ -15,7 +15,7 @@
namespace parser {
Prescanner::Prescanner(
- Messages *messages, CookedSource *cooked, Preprocessor *preprocessor)
+ Messages &messages, CookedSource &cooked, Preprocessor &preprocessor)
: messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor} {}
Prescanner::Prescanner(const Prescanner &that)
@@ -28,53 +28,131 @@
compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_},
compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {}
+static void NormalizeCompilerDirectiveCommentMarker(TokenSequence *dir) {
+ char *p{dir->GetMutableCharData()};
+ char *limit{p + dir->SizeInChars()};
+ for (; p < limit; ++p) {
+ if (*p != ' ') {
+ CHECK(*p == '*' || *p == 'c' || *p == 'C' || *p == '!');
+ *p = '!';
+ return;
+ }
+ }
+ CHECK(!"compiler directive all blank");
+}
+
bool Prescanner::Prescan(ProvenanceRange range) {
- AllSources *allSources{cooked_->allSources()};
- ProvenanceRange around{allSources->GetContiguousRangeAround(range)};
+ AllSources &allSources{cooked_.allSources()};
+ ProvenanceRange around{allSources.GetContiguousRangeAround(range)};
startProvenance_ = range.start();
std::size_t offset{0};
- const SourceFile *source{
- allSources->GetSourceFile(startProvenance_, &offset)};
+ const SourceFile *source{allSources.GetSourceFile(startProvenance_, &offset)};
CHECK(source != nullptr);
start_ = source->content() + offset;
limit_ = start_ + range.size();
lineStart_ = start_;
- BeginSourceLine(lineStart_);
- TokenSequence tokens, preprocessed;
+ const bool beganInFixedForm{inFixedForm_};
while (lineStart_ < limit_) {
- char sentinel[8];
- if (CommentLinesAndPreprocessorDirectives(sentinel) &&
- lineStart_ >= limit_) {
- break;
+ Statement();
+ }
+ if (inFixedForm_ != beganInFixedForm) {
+ std::string dir{"!dir$ "};
+ if (beganInFixedForm) {
+ dir += "fixed";
+ } else {
+ dir += "free";
}
+ dir += '\n';
+ TokenSequence tokens{dir, allSources.AddCompilerInsertion(dir).start()};
+ tokens.Emit(&cooked_);
+ }
+ return !anyFatalErrors_;
+}
+
+void Prescanner::Statement() {
+ TokenSequence tokens;
+ LineClassification line{ClassifyLine(lineStart_)};
+ switch (line.kind) {
+ case LineClassification::Kind::Comment: NextLine(); return;
+ case LineClassification::Kind::Include:
+ FortranInclude(lineStart_ + line.payloadOffset);
+ NextLine();
+ return;
+ case LineClassification::Kind::PreprocessorDirective:
+ if (std::optional<TokenSequence> toks{TokenizePreprocessorDirective()}) {
+ preprocessor_.Directive(*toks, this);
+ }
+ return;
+ case LineClassification::Kind::CompilerDirective:
+ directiveSentinel_ = line.sentinel;
+ CHECK(directiveSentinel_ != nullptr);
+ BeginSourceLineAndAdvance();
+ if (inFixedForm_) {
+ CHECK(*at_ == '!' || *at_ == '*' || *at_ == 'c' || *at_ == 'C');
+ } else {
+ while (*at_ == ' ' || *at_ == '\t') {
+ ++at_;
+ }
+ CHECK(*at_ == '!');
+ }
+ tokens.PutNextTokenChar('!', GetCurrentProvenance());
+ ++at_, ++column_;
+ for (const char *sp{directiveSentinel_}; *sp != '\0';
+ ++sp, ++at_, ++column_) {
+ tokens.PutNextTokenChar(*sp, GetCurrentProvenance());
+ }
+ tokens.CloseToken();
+ break;
+ case LineClassification::Kind::Source:
BeginSourceLineAndAdvance();
if (inFixedForm_) {
LabelField(&tokens);
} else {
SkipSpaces();
}
- while (NextToken(&tokens)) {
- }
- Provenance newlineProvenance{GetCurrentProvenance()};
- if (preprocessor_->MacroReplacement(tokens, *this, &preprocessed)) {
- preprocessed.PutNextTokenChar('\n', newlineProvenance);
- preprocessed.CloseToken();
- const char *ppd{preprocessed.data()};
- if (IsFixedFormCompilerDirectiveLine(ppd, sentinel) ||
- IsFreeFormCompilerDirectiveLine(ppd, sentinel) ||
- !(IsFixedFormCommentLine(ppd) ||
- IsFreeFormComment(ppd))) {
- preprocessed.pop_back(); // clip the newline added above
- preprocessed.EmitLowerCase(cooked_);
- }
- preprocessed.clear();
- } else {
- tokens.EmitLowerCase(cooked_);
- }
- tokens.clear();
- cooked_->Put('\n', newlineProvenance);
+ break;
}
- return !anyFatalErrors_;
+
+ while (NextToken(&tokens)) {
+ }
+
+ Provenance newlineProvenance{GetCurrentProvenance()};
+ if (std::optional<TokenSequence> preprocessed{
+ preprocessor_.MacroReplacement(tokens, *this)}) {
+ // Reprocess the preprocessed line.
+ preprocessed->PutNextTokenChar('\n', newlineProvenance);
+ preprocessed->CloseToken();
+ const char *ppd{preprocessed->ToCharBlock().begin()};
+ LineClassification ppl{ClassifyLine(ppd)};
+ switch (ppl.kind) {
+ case LineClassification::Kind::Comment: break;
+ case LineClassification::Kind::Include:
+ FortranInclude(ppd + ppl.payloadOffset);
+ break;
+ case LineClassification::Kind::PreprocessorDirective:
+ Complain("preprocessed line looks like a preprocessor directive"_en_US,
+ preprocessed->GetProvenanceRange().start());
+ preprocessed->ToLowerCase().Emit(&cooked_);
+ break;
+ case LineClassification::Kind::CompilerDirective:
+ NormalizeCompilerDirectiveCommentMarker(&*preprocessed);
+ preprocessed->ToLowerCase();
+ SourceFormChange(preprocessed->ToString());
+ preprocessed->Emit(&cooked_);
+ break;
+ case LineClassification::Kind::Source:
+ preprocessed->ToLowerCase().Emit(&cooked_);
+ break;
+ }
+ } else {
+ tokens.ToLowerCase();
+ if (line.kind == LineClassification::Kind::CompilerDirective) {
+ SourceFormChange(tokens.ToString());
+ }
+ tokens.Emit(&cooked_);
+ cooked_.Put('\n', newlineProvenance);
+ }
+ directiveSentinel_ = nullptr;
}
TokenSequence Prescanner::TokenizePreprocessorDirective() {
@@ -92,29 +170,29 @@
Message &Prescanner::Error(Message &&message) {
anyFatalErrors_ = true;
- return messages_->Put(std::move(message));
+ return messages_.Put(std::move(message));
}
Message &Prescanner::Error(MessageFixedText text, Provenance p) {
anyFatalErrors_ = true;
- return messages_->Put({p, text});
+ return messages_.Put({p, text});
}
Message &Prescanner::Error(MessageFormattedText &&text, Provenance p) {
anyFatalErrors_ = true;
- return messages_->Put({p, std::move(text)});
+ return messages_.Put({p, std::move(text)});
}
Message &Prescanner::Complain(Message &&message) {
- return messages_->Put(std::move(message));
+ return messages_.Put(std::move(message));
}
Message &Prescanner::Complain(MessageFixedText text, Provenance p) {
- return messages_->Put({p, text});
+ return messages_.Put({p, text});
}
Message &Prescanner::Complain(MessageFormattedText &&text, Provenance p) {
- return messages_->Put({p, std::move(text)});
+ return messages_.Put({p, std::move(text)});
}
void Prescanner::NextLine() {
@@ -180,6 +258,8 @@
if ((inFixedForm_ && column_ > fixedFormColumnLimit_ &&
!tabInCurrentLine_) ||
(*at_ == '!' && !inCharLiteral_)) {
+ // Skip remainder of fixed form line due to '!' comment marker or
+ // hitting the right margin.
while (*at_ != '\n') {
++at_;
}
@@ -421,9 +501,6 @@
}
bool Prescanner::IsFixedFormCommentLine(const char *start) const {
- if (start >= limit_ || !inFixedForm_) {
- return false;
- }
const char *p{start};
char ch{*p};
if (ch == '*' || ch == 'C' || ch == 'c' ||
@@ -455,33 +532,35 @@
}
bool Prescanner::IsFreeFormComment(const char *p) const {
- if (p >= limit_ || inFixedForm_) {
- return false;
- }
while (*p == ' ' || *p == '\t') {
++p;
}
return *p == '!' || *p == '\n';
}
-bool Prescanner::IncludeLine(const char *p) {
- if (p >= limit_) {
- return false;
- }
- const char *start{p};
+std::optional<std::size_t> Prescanner::IsIncludeLine(const char *start) const {
+ const char *p{start};
while (*p == ' ' || *p == '\t') {
++p;
}
for (char ch : "include"s) {
if (ToLowerCaseLetter(*p++) != ch) {
- return false;
+ return {};
}
}
while (*p == ' ' || *p == '\t') {
++p;
}
- if (*p != '"' && *p != '\'') {
- return false;
+ if (*p == '"' || *p == '\'') {
+ return {p - start};
+ }
+ return {};
+}
+
+bool Prescanner::FortranInclude(const char *firstQuote) {
+ const char *p{firstQuote};
+ while (*p != '"' && *p != '\'') {
+ ++p;
}
char quote{*p};
std::string path;
@@ -504,15 +583,15 @@
Complain("excess characters after path name"_en_US, GetProvenance(p));
}
std::stringstream error;
- Provenance provenance{GetProvenance(start)};
- AllSources *allSources{cooked_->allSources()};
- const SourceFile *currentFile{allSources->GetSourceFile(provenance)};
+ Provenance provenance{GetProvenance(lineStart_)};
+ AllSources &allSources{cooked_.allSources()};
+ const SourceFile *currentFile{allSources.GetSourceFile(provenance)};
if (currentFile != nullptr) {
- allSources->PushSearchPathDirectory(DirectoryName(currentFile->path()));
+ allSources.PushSearchPathDirectory(DirectoryName(currentFile->path()));
}
- const SourceFile *included{allSources->Open(path, &error)};
+ const SourceFile *included{allSources.Open(path, &error)};
if (currentFile != nullptr) {
- allSources->PopSearchPathDirectory();
+ allSources.PopSearchPathDirectory();
}
if (included == nullptr) {
Error(MessageFormattedText("INCLUDE: %s"_en_US, error.str().data()),
@@ -523,18 +602,15 @@
return true;
}
ProvenanceRange includeLineRange{
- provenance, static_cast<std::size_t>(p - start)};
+ provenance, static_cast<std::size_t>(p - lineStart_)};
ProvenanceRange fileRange{
- allSources->AddIncludedFile(*included, includeLineRange)};
+ allSources.AddIncludedFile(*included, includeLineRange)};
anyFatalErrors_ |= !Prescanner{*this}.Prescan(fileRange);
return true;
}
bool Prescanner::IsPreprocessorDirectiveLine(const char *start) const {
const char *p{start};
- if (p >= limit_) {
- return false;
- }
for (; *p == ' '; ++p) {
}
if (*p == '#') {
@@ -549,44 +625,14 @@
return IsPreprocessorDirectiveLine(lineStart_);
}
-bool Prescanner::CommentLines() {
- bool any{false};
- char sentinel[8];
+void Prescanner::SkipCommentLines() {
while (lineStart_ < limit_) {
- if (IsFixedFormCompilerDirectiveLine(lineStart_, sentinel) ||
- IsFreeFormCompilerDirectiveLine(lineStart_, sentinel) ||
- !(IsFixedFormCommentLine(lineStart_) ||
- IsFreeFormComment(lineStart_))) {
+ LineClassification line{ClassifyLine(lineStart_)};
+ if (line.kind != LineClassification::Kind::Comment) {
break;
}
NextLine();
- any = true;
}
- return any;
-}
-
-bool Prescanner::CommentLinesAndPreprocessorDirectives(char *sentinel) {
- bool any{false};
- *sentinel = '\0';
- while (lineStart_ < limit_) {
- if (IsFixedFormCompilerDirectiveLine(lineStart_, sentinel) ||
- IsFreeFormCompilerDirectiveLine(lineStart_, sentinel)) {
- break;
- }
- if (IsFixedFormCommentLine(lineStart_) || IsFreeFormComment(lineStart_) ||
- IncludeLine(lineStart_)) {
- NextLine();
- } else if (IsPreprocessorDirectiveLine(lineStart_)) {
- if (std::optional<TokenSequence> tokens{
- TokenizePreprocessorDirective()}) {
- preprocessor_->Directive(*tokens, this);
- }
- } else {
- break;
- }
- any = true;
- }
- return any;
}
const char *Prescanner::FixedFormContinuationLine() {
@@ -595,6 +641,34 @@
return nullptr;
}
tabInCurrentLine_ = false;
+ char col1{*p};
+ if (directiveSentinel_ != nullptr) {
+ // Must be a continued compiler directive.
+ if (col1 != '!' && col1 != '*' && col1 != 'c' && col1 != 'C') {
+ return nullptr;
+ }
+ int j{1};
+ for (; j < 5; ++j) {
+ char ch{directiveSentinel_[j - 1]};
+ if (ch == '\0') {
+ break;
+ }
+ if (ch != ToLowerCaseLetter(p[j])) {
+ return nullptr;
+ }
+ }
+ for (; j < 5; ++j) {
+ if (p[j] != ' ') {
+ return nullptr;
+ }
+ }
+ char col6{p[5]};
+ if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') {
+ return p + 6;
+ }
+ return nullptr;
+ }
+ // Normal case: not in a compiler directive.
if (*p == '&') {
return p + 1; // extension; TODO: emit warning with -Mstandard
}
@@ -616,10 +690,10 @@
bool Prescanner::FixedFormContinuation() {
// N.B. We accept '&' as a continuation indicator (even) in fixed form.
- if (!inFixedForm_ || (*at_ == '&' && inCharLiteral_)) {
+ if (*at_ == '&' && inCharLiteral_) {
return false;
}
- CommentLines();
+ SkipCommentLines();
const char *cont{FixedFormContinuationLine()};
if (cont == nullptr) {
return false;
@@ -631,12 +705,6 @@
}
bool Prescanner::FreeFormContinuation() {
- if (inFixedForm_) {
- return false;
- }
- while (*at_ == ' ' || *at_ == '\t') {
- ++at_;
- }
const char *p{at_};
bool ampersand{*p == '&'};
if (ampersand) {
@@ -646,89 +714,107 @@
if (*p != '\n' && (inCharLiteral_ || *p != '!')) {
return false;
}
- CommentLines();
+ SkipCommentLines();
p = lineStart_;
if (p >= limit_) {
return false;
}
- int column{1};
for (; *p == ' ' || *p == '\t'; ++p) {
- ++column;
}
- if (*p == '&') {
- ++p;
- ++column;
- } else if (ampersand || delimiterNesting_ > 0) {
- if (p > lineStart_) {
- --p;
- --column;
+ if (directiveSentinel_ != nullptr) {
+ // Look for a continued compiler directive.
+ if (*p++ != '!') {
+ return false;
+ }
+ for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) {
+ if (*s != ToLowerCaseLetter(*p)) {
+ return false;
+ }
+ }
+ for (; *p == ' ' || *p == '\t'; ++p) {
+ }
+ if (*p == '&') {
+ ++p;
+ } else if (!ampersand) {
+ return false;
}
} else {
- return false; // not a continuation
+ // Normal case (not a compiler directive)
+ if (*p == '&') {
+ ++p;
+ } else if (ampersand || delimiterNesting_ > 0) {
+ if (p > lineStart_) {
+ --p;
+ }
+ } else {
+ return false; // not a continuation
+ }
}
at_ = p;
- column_ = column;
tabInCurrentLine_ = false;
NextLine();
return true;
}
-bool Prescanner::IsFixedFormCompilerDirectiveLine(const char *start,
- char *sentinel) const {
- *sentinel = '\0';
- if (start >= limit_ || !inFixedForm_) {
- return false;
- }
+std::optional<Prescanner::LineClassification>
+Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
const char *p{start};
- char c1{*p};
- if (!(c1 == '*' || c1 == 'C' || c1 == 'c' || c1 == '!')) {
- return false;
+ char col1{*p};
+ if (col1 != '*' && col1 != 'C' && col1 != 'c' && col1 != '!') {
+ return {};
}
- char *sp{sentinel};
- ++p;
+ char sentinel[5], *sp{sentinel};
for (int col{2}; col < 6; ++col) {
char ch{*++p};
if (ch == '\n' || ch == '\t') {
- return false;
+ return {};
}
if (ch != ' ') {
*sp++ = ToLowerCaseLetter(ch);
}
}
- if (*p != ' ' && *p != '0') {
- return false; // continuation card for directive
- }
*sp = '\0';
- return IsCompilerDirectiveSentinel(sentinel);
+ if (const char *sp{IsCompilerDirectiveSentinel(sentinel)}) {
+ return {
+ LineClassification{LineClassification::Kind::CompilerDirective, 6, sp}};
+ }
+ return {};
}
-bool Prescanner::IsFreeFormCompilerDirectiveLine(const char *start,
- char *sentinel) const {
- *sentinel = '\0';
- if (start >= limit_ || inFixedForm_) {
- return false;
- }
+std::optional<Prescanner::LineClassification>
+Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
+ char sentinel[8];
const char *p{start};
while (*p == ' ' || *p == '\t') {
++p;
}
if (*p++ != '!') {
- return false;
+ return {};
}
- for (int j{0}; j < 5; ++p, ++j) {
- if (*p == '\n' || *p == '&') {
+ for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) {
+ if (*p == '\n') {
break;
}
- if (*p == ' ' || *p == '\t') {
+ if (*p == ' ' || *p == '\t' || *p == '&') {
if (j == 0) {
break;
}
sentinel[j] = '\0';
- return IsCompilerDirectiveSentinel(sentinel);
+ for (++p; *p == ' ' || *p == '\t'; ++p) {
+ }
+ if (*p == '!') {
+ break;
+ }
+ if (const char *sp{IsCompilerDirectiveSentinel(sentinel)}) {
+ std::size_t offset = p - start;
+ return {LineClassification{
+ LineClassification::Kind::CompilerDirective, offset, sp}};
+ }
+ break;
}
sentinel[j] = ToLowerCaseLetter(*p);
}
- return false;
+ return {};
}
Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) {
@@ -742,16 +828,54 @@
return *this;
}
-bool Prescanner::IsCompilerDirectiveSentinel(const char *s) const {
+const char *Prescanner::IsCompilerDirectiveSentinel(const char *s) const {
std::uint64_t packed{0};
std::size_t n{0};
for (; s[n] != '\0'; ++n) {
packed = (packed << 8) | (s[n] & 0xff);
}
- return n > 0 && compilerDirectiveBloomFilter_.test(packed % prime1) &&
- compilerDirectiveBloomFilter_.test(packed % prime2) &&
- compilerDirectiveSentinels_.find(std::string(s, n)) !=
- compilerDirectiveSentinels_.end();
+ if (n == 0 || !compilerDirectiveBloomFilter_.test(packed % prime1) ||
+ !compilerDirectiveBloomFilter_.test(packed % prime2)) {
+ return nullptr;
+ }
+ const auto iter = compilerDirectiveSentinels_.find(std::string(s, n));
+ return iter == compilerDirectiveSentinels_.end() ? nullptr : iter->data();
+}
+
+Prescanner::LineClassification Prescanner::ClassifyLine(
+ const char *start) const {
+ if (inFixedForm_) {
+ if (std::optional<LineClassification> lc{
+ IsFixedFormCompilerDirectiveLine(start)}) {
+ return std::move(*lc);
+ }
+ if (IsFixedFormCommentLine(start)) {
+ return {LineClassification::Kind::Comment};
+ }
+ } else {
+ if (std::optional<LineClassification> lc{
+ IsFreeFormCompilerDirectiveLine(start)}) {
+ return std::move(*lc);
+ }
+ if (IsFreeFormComment(start)) {
+ return {LineClassification::Kind::Comment};
+ }
+ }
+ if (std::optional<std::size_t> quoteOffset{IsIncludeLine(start)}) {
+ return {LineClassification::Kind::Include, *quoteOffset};
+ }
+ if (IsPreprocessorDirectiveLine(start)) {
+ return {LineClassification::Kind::PreprocessorDirective};
+ }
+ return {LineClassification::Kind::Source};
+}
+
+void Prescanner::SourceFormChange(std::string &&dir) {
+ if (dir == "!dir$ free") {
+ inFixedForm_ = false;
+ } else if (dir == "!dir$ fixed") {
+ inFixedForm_ = true;
+ }
}
} // namespace parser
} // namespace Fortran
diff --git a/flang/lib/parser/prescan.h b/flang/lib/parser/prescan.h
index 2b3175d..ee978b1 100644
--- a/flang/lib/parser/prescan.h
+++ b/flang/lib/parser/prescan.h
@@ -25,12 +25,12 @@
class Prescanner {
public:
- Prescanner(Messages *, CookedSource *, Preprocessor *);
+ Prescanner(Messages &, CookedSource &, Preprocessor &);
Prescanner(const Prescanner &);
bool anyFatalErrors() const { return anyFatalErrors_; }
void set_anyFatalErrors() { anyFatalErrors_ = true; }
- Messages *messages() const { return messages_; }
+ Messages &messages() const { return messages_; }
Prescanner &set_fixedForm(bool yes) {
inFixedForm_ = yes;
@@ -56,6 +56,7 @@
Prescanner &AddCompilerDirectiveSentinel(const std::string &);
bool Prescan(ProvenanceRange);
+ void Statement();
void NextLine();
// Callbacks for use by Preprocessor.
@@ -72,6 +73,22 @@
Message &Complain(MessageFormattedText &&, Provenance);
private:
+ struct LineClassification {
+ enum class Kind {
+ Comment,
+ PreprocessorDirective,
+ Include,
+ CompilerDirective,
+ Source
+ };
+ LineClassification(Kind k, std::size_t po = 0, const char *s = nullptr)
+ : kind{k}, payloadOffset{po}, sentinel{s} {}
+ LineClassification(LineClassification &&) = default;
+ Kind kind;
+ std::size_t payloadOffset; // byte offset of content
+ const char *sentinel; // if it's a compiler directive
+ };
+
void BeginSourceLine(const char *at) {
at_ = at;
column_ = 1;
@@ -94,8 +111,7 @@
}
void EmitInsertedChar(TokenSequence *tokens, char ch) {
- Provenance provenance{
- cooked_->allSources()->CompilerInsertionProvenance(ch)};
+ Provenance provenance{cooked_.allSources().CompilerInsertionProvenance(ch)};
tokens->PutNextTokenChar(ch, provenance);
}
@@ -113,46 +129,55 @@
void QuotedCharacterLiteral(TokenSequence *);
void Hollerith(TokenSequence *, int);
bool PadOutCharacterLiteral(TokenSequence *);
- bool CommentLines();
- bool CommentLinesAndPreprocessorDirectives(char *sentinel);
+ void SkipCommentLines();
bool IsFixedFormCommentLine(const char *) const;
bool IsFreeFormComment(const char *) const;
- bool IncludeLine(const char *);
+ std::optional<std::size_t> IsIncludeLine(const char *) const;
+ bool FortranInclude(const char *quote);
bool IsPreprocessorDirectiveLine(const char *) const;
const char *FixedFormContinuationLine();
bool FixedFormContinuation();
bool FreeFormContinuation();
- bool IsFixedFormCompilerDirectiveLine(const char *, char *sentinel) const;
- bool IsFreeFormCompilerDirectiveLine(const char *, char *sentinel) const;
- bool IsCompilerDirectiveSentinel(const char *) const;
+ std::optional<LineClassification> IsFixedFormCompilerDirectiveLine(
+ const char *) const;
+ std::optional<LineClassification> IsFreeFormCompilerDirectiveLine(
+ const char *) const;
+ const char *IsCompilerDirectiveSentinel(const char *) const;
+ LineClassification ClassifyLine(const char *) const;
+ void SourceFormChange(std::string &&);
- Messages *messages_;
- CookedSource *cooked_;
- Preprocessor *preprocessor_;
-
- 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_
- int column_{1}; // card image column position of next character
- const char *lineStart_{nullptr}; // next line to process; <= limit_
- bool tabInCurrentLine_{false};
- bool preventHollerith_{false};
+ Messages &messages_;
+ CookedSource &cooked_;
+ Preprocessor &preprocessor_;
bool anyFatalErrors_{false};
- bool inCharLiteral_{false};
- bool inPreprocessorDirective_{false};
bool inFixedForm_{false};
int fixedFormColumnLimit_{72};
Encoding encoding_{Encoding::UTF8};
bool enableOldDebugLines_{false};
bool enableBackslashEscapesInCharLiterals_{true};
int delimiterNesting_{0};
- Provenance spaceProvenance_{
- cooked_->allSources()->CompilerInsertionProvenance(' ')};
- Provenance backslashProvenance_{
- cooked_->allSources()->CompilerInsertionProvenance('\\')};
- ProvenanceRange sixSpaceProvenance_{
- cooked_->allSources()->AddCompilerInsertion(" "s)};
+
+ 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 *lineStart_{nullptr}; // next line to process; <= limit_
+ const char *directiveSentinel_{nullptr}; // current compiler directive
+
+ // This data members are state for processing the source line containing
+ // "at_", which goes to up to the newline character before "lineStart_".
+ const char *at_{nullptr}; // next character to process; < lineStart_
+ int column_{1}; // card image column position of next character
+ bool tabInCurrentLine_{false};
+ bool preventHollerith_{false};
+ bool inCharLiteral_{false};
+ bool inPreprocessorDirective_{false};
+
+ const Provenance spaceProvenance_{
+ cooked_.allSources().CompilerInsertionProvenance(' ')};
+ const Provenance backslashProvenance_{
+ cooked_.allSources().CompilerInsertionProvenance('\\')};
+ const ProvenanceRange sixSpaceProvenance_{
+ cooked_.allSources().AddCompilerInsertion(" "s)};
// To avoid probing the set of active compiler directive sentinel strings
// on every comment line, they're checked first with a cheap Bloom filter.
diff --git a/flang/lib/parser/provenance.cc b/flang/lib/parser/provenance.cc
index 3f47eee..76aabee 100644
--- a/flang/lib/parser/provenance.cc
+++ b/flang/lib/parser/provenance.cc
@@ -33,7 +33,7 @@
}
ProvenanceRange OffsetToProvenanceMappings::Map(std::size_t at) const {
- CHECK(!provenanceMap_.empty());
+ // CHECK(!provenanceMap_.empty());
std::size_t low{0}, count{provenanceMap_.size()};
while (count > 1) {
std::size_t mid{low + (count >> 1)};
@@ -273,8 +273,7 @@
void CookedSource::Marshal() {
CHECK(provenanceMap_.size() == buffer_.size());
- provenanceMap_.Put(
- allSources_->AddCompilerInsertion("(after end of source)"));
+ provenanceMap_.Put(allSources_.AddCompilerInsertion("(after end of source)"));
data_.resize(buffer_.size());
char *p{&data_[0]};
for (char ch : buffer_) {
@@ -328,7 +327,7 @@
void CookedSource::Dump(std::ostream &o) const {
o << "CookedSource:\n";
- allSources_->Dump(o);
+ allSources_.Dump(o);
o << "CookedSource::provenanceMap_:\n";
provenanceMap_.Dump(o);
}
diff --git a/flang/lib/parser/provenance.h b/flang/lib/parser/provenance.h
index afbb97f..2b9e6de 100644
--- a/flang/lib/parser/provenance.h
+++ b/flang/lib/parser/provenance.h
@@ -166,13 +166,18 @@
class CookedSource {
public:
- explicit CookedSource(AllSources *sources) : allSources_{sources} {}
+ explicit CookedSource(AllSources &sources) : allSources_{sources} {}
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() const { return allSources_; }
+
+ bool IsValid(const char *p) const {
+ return p >= &data_.front() && p <= &data_.back() + 1;
+ }
+ bool IsValid(Provenance p) const { return allSources_.IsValid(p); }
ProvenanceRange GetProvenance(const char *) const;
void Identify(std::ostream &, const char *) const;
@@ -190,7 +195,7 @@
void Dump(std::ostream &) const;
private:
- AllSources *allSources_;
+ AllSources &allSources_;
CharBuffer buffer_; // before Marshal()
std::vector<char> data_; // all of it, prescanned and preprocessed
OffsetToProvenanceMappings provenanceMap_;
diff --git a/flang/lib/parser/token-parsers.h b/flang/lib/parser/token-parsers.h
index e1a6f0e..88ab79c 100644
--- a/flang/lib/parser/token-parsers.h
+++ b/flang/lib/parser/token-parsers.h
@@ -496,6 +496,27 @@
return "," >> nonemptyList(p) / "::" ||
("::"_tok || !","_tok) >> defaulted(cut >> nonemptyList(p));
}
+
+// Compiler directives can switch the parser between fixed and free form.
+constexpr struct FormDirectivesAndEmptyLines {
+ using resultType = Success;
+ static std::optional<Success> Parse(ParseState *state) {
+ while (!state->IsAtEnd()) {
+ const char *at{state->GetLocation()};
+ static const char fixed[] = "!dir$ fixed\n", free[] = "!dir$ free\n";
+ if (*at == '\n') {
+ state->UncheckedAdvance();
+ } else if (std::memcmp(at, fixed, sizeof fixed - 1) == 0) {
+ state->set_inFixedForm(true).UncheckedAdvance(sizeof fixed - 1);
+ } else if (std::memcmp(at, free, sizeof free - 1) == 0) {
+ state->set_inFixedForm(false).UncheckedAdvance(sizeof free - 1);
+ } else {
+ break;
+ }
+ }
+ return {Success{}};
+ }
+} skipEmptyLines;
} // namespace parser
} // namespace Fortran
#endif // FORTRAN_PARSER_TOKEN_PARSERS_H_
diff --git a/flang/lib/parser/token-sequence.cc b/flang/lib/parser/token-sequence.cc
index fec6f42..ae66166 100644
--- a/flang/lib/parser/token-sequence.cc
+++ b/flang/lib/parser/token-sequence.cc
@@ -40,8 +40,9 @@
void TokenSequence::Put(const TokenSequence &that, ProvenanceRange range) {
std::size_t offset{0};
- for (std::size_t j{0}; j < that.size(); ++j) {
- CharBlock tok{that[j]};
+ std::size_t tokens{that.SizeInTokens()};
+ for (std::size_t j{0}; j < tokens; ++j) {
+ CharBlock tok{that.TokenAt(j)};
Put(tok, range.OffsetMember(offset));
offset += tok.size();
}
@@ -53,12 +54,12 @@
ProvenanceRange provenance;
std::size_t offset{0};
for (; tokens-- > 0; ++at) {
- CharBlock tok{that[at]};
+ CharBlock tok{that.TokenAt(at)};
std::size_t tokBytes{tok.size()};
for (std::size_t j{0}; j < tokBytes; ++j) {
if (offset == provenance.size()) {
- offset = 0;
provenance = that.provenances_.Map(that.start_[at] + j);
+ offset = 0;
}
PutNextTokenChar(tok[j], provenance.OffsetMember(offset++));
}
@@ -86,55 +87,53 @@
Put(ss.str(), provenance);
}
-void TokenSequence::EmitLowerCase(CookedSource *cooked) const {
+TokenSequence &TokenSequence::ToLowerCase() {
std::size_t tokens{start_.size()};
std::size_t chars{char_.size()};
std::size_t atToken{0};
for (std::size_t j{0}; j < chars;) {
std::size_t nextStart{atToken + 1 < tokens ? start_[++atToken] : chars};
- const char *p{&char_[j]}, *limit{&char_[nextStart]};
+ char *p{&char_[j]}, *limit{&char_[nextStart]};
j = nextStart;
if (IsDecimalDigit(*p)) {
while (p < limit && IsDecimalDigit(*p)) {
- cooked->Put(*p++);
+ ++p;
}
if (p < limit && (*p == 'h' || *p == 'H')) {
// Hollerith
- cooked->Put('h');
- cooked->Put(p + 1, limit - (p + 1));
+ *p = 'h';
} else {
// exponent
- while (p < limit) {
- cooked->Put(ToLowerCaseLetter(*p++));
+ for (; p < limit; ++p) {
+ *p = ToLowerCaseLetter(*p);
}
}
} else if (limit[-1] == '\'' || limit[-1] == '"') {
if (*p == limit[-1]) {
// Character literal without prefix
- cooked->Put(p, limit - p);
} else if (p[1] == limit[-1]) {
// BOZX-prefixed constant
- while (p < limit) {
- cooked->Put(ToLowerCaseLetter(*p++));
+ for (; p < limit; ++p) {
+ *p = ToLowerCaseLetter(*p);
}
} else {
// Kanji NC'...' character literal or literal with kind-param prefix.
- while (*p != limit[-1]) {
- cooked->Put(ToLowerCaseLetter(*p++));
+ for (; *p != limit[-1]; ++p) {
+ *p = ToLowerCaseLetter(*p);
}
- cooked->Put(p, limit - p);
}
} else {
- while (p < limit) {
- cooked->Put(ToLowerCaseLetter(*p++));
+ for (; p < limit; ++p) {
+ *p = ToLowerCaseLetter(*p);
}
}
}
- cooked->PutProvenanceMappings(provenances_);
+ return *this;
}
-std::string TokenSequence::ToString() const {
- return {&char_[0], char_.size()};
+void TokenSequence::Emit(CookedSource *cooked) const {
+ cooked->Put(&char_[0], char_.size());
+ cooked->PutProvenanceMappings(provenances_);
}
Provenance TokenSequence::GetTokenProvenance(
diff --git a/flang/lib/parser/token-sequence.h b/flang/lib/parser/token-sequence.h
index 7053bf6..d336b38 100644
--- a/flang/lib/parser/token-sequence.h
+++ b/flang/lib/parser/token-sequence.h
@@ -2,7 +2,7 @@
#define FORTRAN_PARSER_TOKEN_SEQUENCE_H_
// A buffer class capable of holding a contiguous sequence of characters
-// that has been partitioned into preprocessing tokens, along with their
+// and a partitioning thereof into preprocessing tokens, along with their
// associated provenances.
#include "char-block.h"
@@ -44,17 +44,23 @@
return *this;
}
- CharBlock operator[](std::size_t token) const {
- return {&char_[start_[token]], TokenBytes(token)};
- }
-
bool empty() const { return start_.empty(); }
- std::size_t size() const { return start_.size(); }
- const char *data() const { return &char_[0]; }
void clear();
void pop_back();
void shrink_to_fit();
+ std::size_t SizeInTokens() const { return start_.size(); }
+ std::size_t SizeInChars() const { return char_.size(); }
+
+ CharBlock ToCharBlock() const { return {&char_[0], char_.size()}; }
+ std::string ToString() const { return ToCharBlock().ToString(); }
+
+ CharBlock TokenAt(std::size_t token) const {
+ return {&char_[start_.at(token)], TokenBytes(token)};
+ }
+
+ char CharAt(std::size_t j) const { return char_.at(j); }
+
void PutNextTokenChar(char ch, Provenance provenance) {
char_.emplace_back(ch);
provenances_.Put({provenance, 1});
@@ -77,7 +83,6 @@
void Put(const CharBlock &, Provenance);
void Put(const std::string &, Provenance);
void Put(const std::stringstream &, Provenance);
- std::string ToString() const;
Provenance GetTokenProvenance(
std::size_t token, std::size_t offset = 0) const;
ProvenanceRange GetTokenProvenanceRange(
@@ -86,7 +91,9 @@
std::size_t token, std::size_t tokens = 1) const;
ProvenanceRange GetProvenanceRange() const;
- void EmitLowerCase(CookedSource *) const;
+ char *GetMutableCharData() { return &char_[0]; }
+ TokenSequence &ToLowerCase();
+ void Emit(CookedSource *) const;
private:
std::size_t TokenBytes(std::size_t token) const {
diff --git a/flang/lib/parser/unparse.cc b/flang/lib/parser/unparse.cc
index 0d98419..2243d2e 100644
--- a/flang/lib/parser/unparse.cc
+++ b/flang/lib/parser/unparse.cc
@@ -1941,7 +1941,30 @@
return false;
}
- // Extensions and deprecated constructs
+ // Directives, extensions, and deprecated constructs
+ bool Pre(const CompilerDirective &x) {
+ std::visit(
+ visitors{[&](const std::list<CompilerDirective::IgnoreTKR> &tkr) {
+ Word("!DIR$ IGNORE_TKR");
+ Walk(" ", tkr, ", ");
+ },
+ [&](const CompilerDirective::IVDEP &) { Word("!DIR$ IVDEP\n"); }},
+ x.u);
+ Put('\n');
+ return false;
+ }
+ bool Pre(const CompilerDirective::IgnoreTKR &x) {
+ const auto &list = std::get<std::list<const char *>>(x.t);
+ if (!list.empty()) {
+ Put("(");
+ for (const char *tkr : list) {
+ Put(*tkr);
+ }
+ Put(") ");
+ }
+ Walk(std::get<Name>(x.t));
+ return false;
+ }
bool Pre(const BasedPointerStmt &x) {
Word("POINTER ("), Walk(std::get<0>(x.t)), Put(", ");
Walk(std::get<1>(x.t));
@@ -2150,9 +2173,7 @@
}
}
-void UnparseVisitor::Word(const std::string &str) {
- Word(str.c_str());
-}
+void UnparseVisitor::Word(const std::string &str) { Word(str.c_str()); }
void Unparse(std::ostream &out, const Program &program, Encoding encoding,
bool capitalizeKeywords) {