diff --git a/flang/cooked-chars.h b/flang/cooked-chars.h new file mode 100644 index 000000000000..0c0b91f43cb9 --- /dev/null +++ b/flang/cooked-chars.h @@ -0,0 +1,236 @@ +#ifndef FORTRAN_COOKED_CHARS_H_ +#define FORTRAN_COOKED_CHARS_H_ + +// Defines the parser cookedNextChar, which supplies all of the input to +// the next stage of parsing, viz. the tokenization parsers in cooked-tokens.h. +// It consumes the stream of raw characters and removes Fortran comments, +// continuation line markers, and characters that appear in the right margin +// of fixed form source after the column limit. It inserts spaces to +// pad out source card images to fixed form's right margin when necessary. +// These parsers are largely bypassed when the prescanner is used, but still +// serve as the definition of correct character cooking, apart from +// preprocessing and file inclusion, which are not supported here. + +#include "basic-parsers.h" +#include "char-parsers.h" +#include "idioms.h" +#include "parse-state.h" +#include + +namespace Fortran { + +constexpr struct FixedFormPadding { + using resultType = char; + static std::optional Parse(ParseState *state) { + if (state->inCharLiteral() && + state->inFortran() && + state->inFixedForm() && + state->position().column() <= state->columns()) { + if (std::optional ch{state->GetNextRawChar()}) { + if (*ch == '\n') { + state->AdvancePositionForPadding(); + return {' '}; + } + } + } + return {}; + } +} fixedFormPadding; + +static inline void IncrementSkippedNewLines(ParseState *state) { + state->set_skippedNewLines(state->skippedNewLines() + 1); +} + +constexpr StateUpdateParser noteSkippedNewLine{IncrementSkippedNewLines}; + +static inline bool InRightMargin(const ParseState &state) { + if (state.inFortran() && + state.inFixedForm() && + state.position().column() > state.columns() && + !state.tabInCurrentLine()) { + if (std::optional ch{state.GetNextRawChar()}) { + return *ch != '\n'; + } + } + return false; +} + +constexpr StatePredicateGuardParser inRightMargin{InRightMargin}; + +template +struct AtFixedFormColumn { + using resultType = Success; + constexpr AtFixedFormColumn() {} + constexpr AtFixedFormColumn(const AtFixedFormColumn &) {} + static std::optional Parse(ParseState *state) { + if (state->inFortran() && + state->inFixedForm() && + !state->IsAtEnd() && + state->position().column() == col) { + return {Success{}}; + } + return {}; + } +}; + +template +struct AtColumn { + using resultType = Success; + constexpr AtColumn() {} + constexpr AtColumn(const AtColumn &) {} + static std::optional Parse(ParseState *state) { + if (!state->IsAtEnd() && + state->position().column() == col) { + return {Success{}}; + } + return {}; + } +}; + +static inline bool AtOldDebugLineMarker(const ParseState &state) { + if (state.inFortran() && + state.inFixedForm() && + state.position().column() == 1) { + if (std::optional ch{state.GetNextRawChar()}) { + return toupper(*ch) == 'D'; + } + } + return false; +} + +static inline bool AtDisabledOldDebugLine(const ParseState &state) { + return AtOldDebugLineMarker(state) && !state.enableOldDebugLines(); +} + +static inline bool AtEnabledOldDebugLine(const ParseState &state) { + return AtOldDebugLineMarker(state) && state.enableOldDebugLines(); +} + +static constexpr StatePredicateGuardParser + atDisabledOldDebugLine{AtDisabledOldDebugLine}, + atEnabledOldDebugLine{AtEnabledOldDebugLine}; + +constexpr auto skipPastNewLine = SkipPast<'\n'>{} / noteSkippedNewLine; + +// constexpr auto rawSpace = +// (ExactRaw<' '>{} || ExactRaw<'\t'>{} || +// atEnabledOldDebugLine >> rawNextChar) >> ok; +constexpr struct FastRawSpaceParser { + using resultType = Success; + constexpr FastRawSpaceParser() {} + constexpr FastRawSpaceParser(const FastRawSpaceParser &) {} + static std::optional Parse(ParseState *state) { + if (std::optional ch{state->GetNextRawChar()}) { + if (*ch == ' ' || *ch == '\t' || + (toupper(*ch) == 'D' && + state->position().column() == 1 && + state->enableOldDebugLines() && + state->inFortran() && + state->inFixedForm())) { + state->Advance(); + return {Success{}}; + } + } + return {}; + } +} rawSpace; + +constexpr auto skipAnyRawSpaces = skipManyFast(rawSpace); + +constexpr auto commentBang = + !inCharLiteral >> !AtFixedFormColumn<6>{} >> ExactRaw<'!'>{} >> ok; + +constexpr auto fixedComment = + AtFixedFormColumn<1>{} >> + ((ExactRaw<'*'>{} || ExactRaw<'C'>{} || ExactRaw<'c'>{}) >> ok || + atDisabledOldDebugLine || + extension(ExactRaw<'%'>{} /* VAX %list, %eject, &c. */) >> ok); + +constexpr auto comment = + (skipAnyRawSpaces >> (commentBang || inRightMargin) || fixedComment) >> + skipPastNewLine; + +constexpr auto blankLine = skipAnyRawSpaces >> eoln >> ok; + +inline bool InFortran(const ParseState &state) { + return state.inFortran(); +} + +constexpr StatePredicateGuardParser inFortran{InFortran}; + +inline bool FixedFormFortran(const ParseState &state) { + return state.inFortran() && state.inFixedForm(); +} + +constexpr StatePredicateGuardParser fixedFormFortran{FixedFormFortran}; + +inline bool FreeFormFortran(const ParseState &state) { + return state.inFortran() && !state.inFixedForm(); +} + +constexpr StatePredicateGuardParser freeFormFortran{FreeFormFortran}; + +constexpr auto lineEnd = comment || blankLine; +constexpr auto skippedLineEnd = lineEnd / noteSkippedNewLine; +constexpr auto someSkippedLineEnds = skippedLineEnd >> skipMany(skippedLineEnd); + +constexpr auto fixedFormContinuation = + fixedFormFortran >> + someSkippedLineEnds >> + (extension(AtColumn<1>{} >> + (ExactRaw<'&'>{} || // extension: & in column 1 + (ExactRaw<'\t'>{} >> // VAX Fortran: tab and then 1-9 + ExactRawRange<'1', '9'>{}))) || + (skipAnyRawSpaces >> AtColumn<6>{} >> AnyCharExcept<'0'>{})) >> ok; + +constexpr auto freeFormContinuation = + freeFormFortran >> + ((ExactRaw<'&'>{} >> blankLine >> + skipMany(skippedLineEnd) >> + skipAnyRawSpaces >> ExactRaw<'&'>{} >> ok) || + (ExactRaw<'&'>{} >> !inCharLiteral >> + someSkippedLineEnds >> + maybe(skipAnyRawSpaces >> ExactRaw<'&'>{}) >> ok) || + // PGI-only extension: don't need '&' on initial line if it's on later one + extension(eoln >> skipMany(skippedLineEnd) >> + skipAnyRawSpaces >> ExactRaw<'&'>{} >> ok)); + +constexpr auto skippable = + freeFormContinuation || + fixedFormFortran >> + (fixedFormContinuation || + !inCharLiteral >> rawSpace || + AtColumn<6>{} >> ExactRaw<'0'>{} >> ok); + +char toLower(char &&ch) { return tolower(ch); } + +// TODO: skip \\ \n in C mode, increment skipped newline count; +// drain skipped newlines. + +constexpr auto slowCookedNextChar = + fixedFormPadding || + skipMany(skippable) >> + (inCharLiteral >> rawNextChar || + lineEnd >> pure('\n') || + rawSpace >> skipAnyRawSpaces >> pure(' ') || + // TODO: detect and report non-digit in fixed form label field + inFortran >> applyFunction(toLower, rawNextChar) || + rawNextChar); + +constexpr struct CookedChar { + using resultType = char; + static std::optional Parse(ParseState *state) { + if (state->prescanned()) { + return rawNextChar.Parse(state); + } + return slowCookedNextChar.Parse(state); + } +} cookedNextChar; + +static inline bool ConsumedAllInput(const ParseState &state) { + return state.IsAtEnd(); +} + +constexpr StatePredicateGuardParser consumedAllInput{ConsumedAllInput}; +} // namespace Fortran +#endif // FORTRAN_COOKED_CHARS_H_