constexpr NonstandardParser(const PA &parser) : parser_{parser} {}
std::optional<resultType> Parse(ParseState &state) const {
if (UserState * ustate{state.userState()}) {
- if (!ustate->IsEnabled(LF)) {
+ if (!ustate->features().IsEnabled(LF)) {
return {};
}
}
constexpr DeprecatedParser(const PA &parser) : parser_{parser} {}
std::optional<resultType> Parse(ParseState &state) const {
if (UserState * ustate{state.userState()}) {
- if (!ustate->IsEnabled(LF)) {
+ if (!ustate->features().IsEnabled(LF)) {
return {};
}
}
namespace Fortran::parser {
-ENUM_CLASS(LanguageFeature, BackslashEscapes, LogicalAbbreviations, XOROperator,
- PunctuationInNames, OptionalFreeFormSpace, BOZExtensions, EmptyStatement,
- OldDebugLines, OpenMP, Extension, Deprecation)
+ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
+ FixedFormContinuationWithColumn1Ampersand, LogicalAbbreviations,
+ XOROperator, PunctuationInNames, OptionalFreeFormSpace, BOZExtensions,
+ EmptyStatement, OpenMP, Extension, Deprecation)
using LanguageFeatures = common::EnumSet<LanguageFeature, 32>;
+class LanguageFeatureControl {
+public:
+ LanguageFeatureControl() {
+ disable_.set(LanguageFeature::OldDebugLines);
+ // These features, if enabled, conflict with valid standard usage.
+ disable_.set(LanguageFeature::LogicalAbbreviations);
+ disable_.set(LanguageFeature::XOROperator);
+ }
+ LanguageFeatureControl(const LanguageFeatureControl &) = default;
+ void Enable(LanguageFeature f, bool yes = true) { disable_.set(f, !yes); }
+ void EnableWarning(LanguageFeature f, bool yes = true) { warn_.set(f, yes); }
+ void WarnOnAllNonstandard(bool yes = true) { warnAll_ = yes; }
+ bool IsEnabled(LanguageFeature f) const { return !disable_.test(f); }
+ bool ShouldWarn(LanguageFeature f) const {
+ return (warnAll_ && f != LanguageFeature::OpenMP) || warn_.test(f);
+ }
+
+private:
+ LanguageFeatures disable_;
+ LanguageFeatures warn_;
+ bool warnAll_{false};
+};
+
} // namespace Fortran::parser
#endif // FORTRAN_PARSER_FEATURES_H_
void Nonstandard(
CharBlock range, LanguageFeature lf, const MessageFixedText &msg) {
anyConformanceViolation_ = true;
- if (userState_ != nullptr && userState_->Warn(lf)) {
+ if (userState_ != nullptr && userState_->features().ShouldWarn(lf)) {
Say(range, msg);
}
}
+ bool IsNonstandardOk(LanguageFeature lf, const MessageFixedText &msg) {
+ if (userState_ != nullptr && !userState_->features().IsEnabled(lf)) {
+ return false;
+ }
+ Nonstandard(lf, msg);
+ return true;
+ }
bool IsAtEnd() const { return p_ >= limit_; }
preprocessor.Undefine(predef.first);
}
}
- Prescanner prescanner{messages_, cooked_, preprocessor};
+ Prescanner prescanner{messages_, cooked_, preprocessor, options.features};
prescanner.set_fixedForm(options.isFixedForm)
.set_fixedFormColumnLimit(options.fixedFormColumns)
.set_encoding(options.encoding)
- .set_enableBackslashEscapesInCharLiterals( // TODO pmk
- options.enabled.test(LanguageFeature::BackslashEscapes))
- .set_enableOldDebugLines(
- options.enabled.test(LanguageFeature::OldDebugLines))
- .set_warnOnNonstandardUsage(options_.isStrictlyStandard)
.AddCompilerDirectiveSentinel("dir$");
- if (options.enabled.test(LanguageFeature::OpenMP)) {
+ if (options.features.IsEnabled(LanguageFeature::OpenMP)) {
prescanner.AddCompilerDirectiveSentinel("$omp");
}
ProvenanceRange range{
}
void Parsing::DumpCookedChars(std::ostream &out) const {
- UserState userState{cooked_};
+ UserState userState{cooked_, LanguageFeatureControl{}};
ParseState parseState{cooked_};
parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
while (std::optional<const char *> p{parseState.GetNextChar()}) {
}
void Parsing::Parse(std::ostream *out) {
- UserState userState{cooked_};
+ UserState userState{cooked_, options_.features};
userState.set_debugOutput(out)
.set_instrumentedParse(options_.instrumentedParse)
.set_log(&log_);
- userState.Enable(options_.enabled);
- userState.EnableWarnings(options_.warning);
ParseState parseState{cooked_};
parseState.set_inFixedForm(options_.isFixedForm)
.set_encoding(options_.encoding)
bool isFixedForm{false};
int fixedFormColumns{72};
bool isStrictlyStandard{false};
- LanguageFeatures enabled, warning;
+ LanguageFeatureControl features;
Encoding encoding{Encoding::UTF8};
std::vector<std::string> searchDirectories;
std::vector<Predefinition> predefinitions;
static constexpr int maxPrescannerNesting{100};
-Prescanner::Prescanner(
- Messages &messages, CookedSource &cooked, Preprocessor &preprocessor)
- : messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor} {}
+Prescanner::Prescanner(Messages &messages, CookedSource &cooked,
+ Preprocessor &preprocessor, LanguageFeatureControl lfc)
+ : messages_{messages}, cooked_{cooked},
+ preprocessor_{preprocessor}, features_{lfc} {}
Prescanner::Prescanner(const Prescanner &that)
: messages_{that.messages_}, cooked_{that.cooked_},
- preprocessor_{that.preprocessor_}, inFixedForm_{that.inFixedForm_},
+ preprocessor_{that.preprocessor_}, features_{that.features_},
+ inFixedForm_{that.inFixedForm_},
fixedFormColumnLimit_{that.fixedFormColumnLimit_},
- encoding_{that.encoding_}, enableOldDebugLines_{that.enableOldDebugLines_},
- enableBackslashEscapesInCharLiterals_{
- that.enableBackslashEscapesInCharLiterals_},
- warnOnNonstandardUsage_{that.warnOnNonstandardUsage_},
- prescannerNesting_{that.prescannerNesting_ + 1},
+ encoding_{that.encoding_}, prescannerNesting_{that.prescannerNesting_ + 1},
compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_},
compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {}
const auto emit{[&](char ch) { EmitChar(tokens, ch); }};
const auto insert{[&](char ch) { EmitInsertedChar(tokens, ch); }};
bool escape{false};
+ bool escapesEnabled{features_.IsEnabled(LanguageFeature::BackslashEscapes)};
while (true) {
char ch{*at_};
- escape = !escape && ch == '\\' && enableBackslashEscapesInCharLiterals_;
- EmitQuotedChar(
- ch, emit, insert, false, !enableBackslashEscapesInCharLiterals_);
+ escape = !escape && ch == '\\' && escapesEnabled;
+ EmitQuotedChar(ch, emit, insert, false, !escapesEnabled);
while (PadOutCharacterLiteral(tokens)) {
}
if (*at_ == '\n') {
char ch{*p};
if (ch == '*' || ch == 'C' || ch == 'c' ||
ch == '%' || // VAX %list, %eject, &c.
- ((ch == 'D' || ch == 'd') && !enableOldDebugLines_)) {
+ ((ch == 'D' || ch == 'd') &&
+ !features_.IsEnabled(LanguageFeature::OldDebugLines))) {
return true;
}
bool anyTabs{false};
return nullptr;
} else {
// Normal case: not in a compiler directive.
- if (col1 == '&') {
+ if (col1 == '&' &&
+ features_.IsEnabled(
+ LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) {
// Extension: '&' as continuation marker
- if (warnOnNonstandardUsage_) {
+ if (features_.ShouldWarn(
+ LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) {
Say("nonstandard usage"_en_US, GetProvenance(lineStart_));
}
return lineStart_ + 1;
// inclusion, and driving the Fortran source preprocessor.
#include "characters.h"
+#include "features.h"
#include "message.h"
#include "provenance.h"
#include "token-sequence.h"
class Prescanner {
public:
- Prescanner(Messages &, CookedSource &, Preprocessor &);
+ Prescanner(
+ Messages &, CookedSource &, Preprocessor &, LanguageFeatureControl);
Prescanner(const Prescanner &);
Messages &messages() const { return messages_; }
encoding_ = code;
return *this;
}
- Prescanner &set_enableOldDebugLines(bool yes) {
- enableOldDebugLines_ = yes;
- return *this;
- }
- Prescanner &set_enableBackslashEscapesInCharLiterals(bool yes) {
- enableBackslashEscapesInCharLiterals_ = yes;
- return *this;
- }
Prescanner &set_fixedFormColumnLimit(int limit) {
fixedFormColumnLimit_ = limit;
return *this;
}
- Prescanner &set_warnOnNonstandardUsage(bool yes) {
- warnOnNonstandardUsage_ = yes;
- return *this;
- }
Prescanner &AddCompilerDirectiveSentinel(const std::string &);
Messages &messages_;
CookedSource &cooked_;
Preprocessor &preprocessor_;
+ LanguageFeatureControl features_;
bool inFixedForm_{false};
int fixedFormColumnLimit_{72};
Encoding encoding_{Encoding::UTF8};
- bool enableOldDebugLines_{false};
- bool enableBackslashEscapesInCharLiterals_{false};
- bool warnOnNonstandardUsage_{false};
int delimiterNesting_{0};
int prescannerNesting_{0};
}
};
-static bool IsNonstandardBOZOk(ParseState &state) {
- if (UserState * ustate{state.userState()}) {
- if (!ustate->IsEnabled(LanguageFeature::BOZExtensions)) {
- return false;
- }
- }
- state.Nonstandard(
- LanguageFeature::BOZExtensions, "nonstandard BOZ literal"_en_US);
- return true;
-}
-
// Parse "BOZ" binary literal quoted constants.
// As extensions, support X as an alternate hexadecimal marker, and allow
// BOZX markers to appear as suffixes.
if (!at.has_value()) {
return {};
}
- if (**at == 'x' && !IsNonstandardBOZOk(state)) {
+ if (**at == 'x' &&
+ !state.IsNonstandardOk(
+ LanguageFeature::BOZExtensions, "nonstandard BOZ literal"_en_US)) {
return {};
}
if (baseChar(**at)) {
if (!shift.has_value()) {
// extension: base allowed to appear as suffix, too
- if (!IsNonstandardBOZOk(state) ||
- !(at = nextCh.Parse(state)).has_value() || !baseChar(**at)) {
+ if (!(at = nextCh.Parse(state)).has_value() || !baseChar(**at) ||
+ !state.IsNonstandardOk(LanguageFeature::BOZExtensions,
+ "nonstandard BOZ literal"_en_US)) {
return {};
}
spaceCheck.Parse(state);
} else {
break;
}
- } else if (**at == ';') {
- state.Nonstandard(
- LanguageFeature::EmptyStatement, "empty statement"_en_US);
+ } else if (**at == ';' &&
+ state.IsNonstandardOk(
+ LanguageFeature::EmptyStatement, "empty statement"_en_US)) {
state.UncheckedAdvance();
} else {
break;
class UserState {
public:
- explicit UserState(const CookedSource &cooked) : cooked_{cooked} {}
+ UserState(const CookedSource &cooked, LanguageFeatureControl features)
+ : cooked_{cooked}, features_{features} {}
const CookedSource &cooked() const { return cooked_; }
+ const LanguageFeatureControl &features() const { return features_; }
std::ostream *debugOutput() const { return debugOutput_; }
UserState &set_debugOutput(std::ostream *out) {
return oldStructureComponents_.find(name) != oldStructureComponents_.end();
}
- UserState &Enable(LanguageFeature f) {
- enabled_.set(f);
- return *this;
- }
- UserState &Enable(LanguageFeatures fs) {
- enabled_ |= fs;
- return *this;
- }
- UserState &Disable(LanguageFeature f) {
- enabled_.reset(f);
- return *this;
- }
- UserState &Disable(LanguageFeatures fs) {
- enabled_ &= ~fs;
- return *this;
- }
- bool IsEnabled(LanguageFeature f) { return enabled_.test(f); }
- UserState &EnableWarning(LanguageFeature f) {
- warning_.set(f);
- return *this;
- }
- UserState &EnableWarnings(LanguageFeatures fs) {
- warning_ |= fs;
- return *this;
- }
- UserState &DisableWarning(LanguageFeature f) {
- warning_.reset(f);
- return *this;
- }
- UserState &DisableWarnings(LanguageFeatures fs) {
- warning_ &= ~fs;
- return *this;
- }
- bool Warn(LanguageFeature f) { return warning_.test(f); }
-
private:
const CookedSource &cooked_;
std::set<CharBlock> oldStructureComponents_;
- LanguageFeatures enabled_, warning_;
+ LanguageFeatureControl features_;
};
// Definitions of parser classes that manipulate the UserState.
options.predefinitions.emplace_back("__F18_MINOR__", "1");
options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1");
- options.enabled.set(Fortran::parser::LanguageFeature::PunctuationInNames);
- options.enabled.set(Fortran::parser::LanguageFeature::OptionalFreeFormSpace);
- options.enabled.set(Fortran::parser::LanguageFeature::BOZExtensions);
- options.enabled.set(Fortran::parser::LanguageFeature::EmptyStatement);
- options.enabled.set(Fortran::parser::LanguageFeature::Extension); // pmk
- options.enabled.set(Fortran::parser::LanguageFeature::Deprecation);
-
std::vector<std::string> fortranSources, otherSources, relocatables;
bool anyFiles{false};
} else if (arg == "-Mextend") {
options.fixedFormColumns = 132;
} else if (arg == "-Mbackslash") {
- options.enabled.reset(Fortran::parser::LanguageFeature::BackslashEscapes);
+ options.features.Enable(
+ Fortran::parser::LanguageFeature::BackslashEscapes, false);
} else if (arg == "-Mnobackslash") {
- options.enabled.set(Fortran::parser::LanguageFeature::BackslashEscapes);
+ options.features.Enable(
+ Fortran::parser::LanguageFeature::BackslashEscapes);
} else if (arg == "-Mstandard") {
- options.isStrictlyStandard = true;
+ options.features.WarnOnAllNonstandard();
} else if (arg == "-fopenmp") {
- options.enabled.set(Fortran::parser::LanguageFeature::OpenMP);
+ options.features.Enable(Fortran::parser::LanguageFeature::OpenMP);
} else if (arg == "-Werror") {
driver.warningsAreErrors = true;
} else if (arg == "-ed") {
- options.enabled.set(Fortran::parser::LanguageFeature::OldDebugLines);
+ options.features.Enable(Fortran::parser::LanguageFeature::OldDebugLines);
} else if (arg == "-E") {
driver.dumpCookedChars = true;
} else if (arg == "-fbackslash") {
- options.enabled.set(Fortran::parser::LanguageFeature::BackslashEscapes);
+ options.features.Enable(
+ Fortran::parser::LanguageFeature::BackslashEscapes);
} else if (arg == "-fno-backslash") {
- options.enabled.reset(Fortran::parser::LanguageFeature::BackslashEscapes);
+ options.features.Enable(
+ Fortran::parser::LanguageFeature::BackslashEscapes, false);
} else if (arg == "-fdebug-dump-provenance") {
driver.dumpProvenance = true;
} else if (arg == "-fdebug-dump-parse-tree") {
driver.encoding = options.encoding;
if (options.isStrictlyStandard) {
- options.warning |= options.enabled;
- options.warning.reset(Fortran::parser::LanguageFeature::OpenMP);
+ options.features.WarnOnAllNonstandard();
}
if (!anyFiles) {