TokenSequence tokens;
LineClassification line{ClassifyLine(lineStart_)};
switch (line.kind) {
- case LineClassification::Kind::Comment: NextLine(); return;
+ case LineClassification::Kind::Comment:
+ lineStart_ += line.payloadOffset; // advance to '!' or newline
+ NextLine();
+ return;
case LineClassification::Kind::IncludeLine:
FortranInclude(lineStart_ + line.payloadOffset);
NextLine();
CHECK(*at_ != '\n');
++at_, ++column_;
if (inPreprocessorDirective_) {
- while (*at_ == '/' && at_[1] == '*') {
- char star{' '}, slash{' '};
- at_ += 2;
- column_ += 2;
- while ((*at_ != '\n' || slash == '\\') && (star != '*' || slash != '/')) {
- star = slash;
- slash = *at_++;
- ++column_;
- }
- }
- while (*at_ == '\\' && at_ + 2 < limit_ && at_[1] == '\n') {
- BeginSourceLineAndAdvance();
- }
+ SkipCComments();
} else {
bool rightMarginClip{
inFixedForm_ && column_ > fixedFormColumnLimit_ && !tabInCurrentLine_};
}
}
+void Prescanner::SkipCComments() {
+ while (true) {
+ if (IsCComment(at_)) {
+ if (const char *after{SkipCComment(at_)}) {
+ column_ += after - at_;
+ // May have skipped over one or more newlines; relocate the start of
+ // the next line.
+ lineStart_ = at_ = after;
+ NextLine();
+ } else {
+ Say(GetProvenance(at_), "unclosed C-style comment"_err_en_US);
+ break;
+ }
+ } else if (inPreprocessorDirective_ && at_[0] == '\\' && at_ + 2 < limit_ &&
+ at_[1] == '\n' && lineStart_ < limit_) {
+ BeginSourceLineAndAdvance();
+ } else {
+ break;
+ }
+ }
+}
+
void Prescanner::SkipSpaces() {
while (*at_ == ' ' || *at_ == '\t') {
NextChar();
return p;
}
+const char *Prescanner::SkipWhiteSpaceAndCComments(const char *p) const {
+ while (true) {
+ if (*p == ' ' || *p == '\t') {
+ ++p;
+ } else if (IsCComment(p)) {
+ if (const char *after{SkipCComment(p)}) {
+ p = after;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return p;
+}
+
+const char *Prescanner::SkipCComment(const char *p) const {
+ char star{' '}, slash{' '};
+ p += 2;
+ while (star != '*' || slash != '/') {
+ if (p >= limit_) {
+ return nullptr; // signifies an unterminated comment
+ }
+ star = slash;
+ slash = *p++;
+ }
+ return p;
+}
+
bool Prescanner::NextToken(TokenSequence &tokens) {
CHECK(at_ >= start_ && at_ < limit_);
if (InFixedFormSource()) {
SkipSpaces();
- } else if (*at_ == ' ' || *at_ == '\t') {
- // Compress white space into a single space character.
- // Discard white space at the end of a line.
- const auto theSpace{at_};
- NextChar();
- SkipSpaces();
- if (*at_ != '\n') {
- tokens.PutNextTokenChar(' ', GetProvenance(theSpace));
- tokens.CloseToken();
- return true;
+ } else {
+ if (*at_ == '/' && IsCComment(at_)) {
+ // Recognize and skip over classic C style /*comments*/ when
+ // outside a character literal.
+ if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) {
+ Say(GetProvenance(at_), "nonstandard usage: C-style comment"_en_US);
+ }
+ SkipCComments();
+ }
+ if (*at_ == ' ' || *at_ == '\t') {
+ // Compress free-form white space into a single space character.
+ // Discard white space at the end of a line.
+ const auto theSpace{at_};
+ NextChar();
+ SkipSpaces();
+ if (*at_ != '\n') {
+ tokens.PutNextTokenChar(' ', GetProvenance(theSpace));
+ tokens.CloseToken();
+ return true;
+ }
}
}
if (insertASpace_) {
return *p == '\n';
}
-bool Prescanner::IsFreeFormComment(const char *p) const {
- p = SkipWhiteSpace(p);
- return *p == '!' || *p == '\n';
+const char *Prescanner::IsFreeFormComment(const char *p) const {
+ p = SkipWhiteSpaceAndCComments(p);
+ if (*p == '!' || *p == '\n') {
+ return p;
+ } else {
+ return nullptr;
+ }
}
std::optional<std::size_t> Prescanner::IsIncludeLine(const char *start) const {
IsFreeFormCompilerDirectiveLine(start)}) {
return std::move(*lc);
}
- if (IsFreeFormComment(start)) {
- return {LineClassification::Kind::Comment};
+ if (const char *bang{IsFreeFormComment(start)}) {
+ return {LineClassification::Kind::Comment,
+ static_cast<std::size_t>(bang - start)};
}
}
if (std::optional<std::size_t> quoteOffset{IsIncludeLine(start)}) {
return inFixedForm_ && !inPreprocessorDirective_ && !InCompilerDirective();
}
+ bool IsCComment(const char *p) const {
+ return p[0] == '/' && p[1] == '*' &&
+ (inPreprocessorDirective_ ||
+ (!inCharLiteral_ &&
+ features_.IsEnabled(LanguageFeature::ClassicCComments)));
+ }
+
void LabelField(TokenSequence &, int outCol = 1);
void SkipToEndOfLine();
void NextChar();
+ void SkipCComments();
void SkipSpaces();
static const char *SkipWhiteSpace(const char *);
+ const char *SkipWhiteSpaceAndCComments(const char *) const;
+ const char *SkipCComment(const char *) const;
bool NextToken(TokenSequence &);
bool ExponentAndKind(TokenSequence &);
void QuotedCharacterLiteral(TokenSequence &);
bool PadOutCharacterLiteral(TokenSequence &);
bool SkipCommentLine(bool afterAmpersand);
bool IsFixedFormCommentLine(const char *) const;
- bool IsFreeFormComment(const char *) const;
+ const char *IsFreeFormComment(const char *) const;
std::optional<std::size_t> IsIncludeLine(const char *) const;
void FortranInclude(const char *quote);
const char *IsPreprocessorDirectiveLine(const char *) const;