}
// R711 digit-string -> digit [digit]...
-// N.B. not a token -- no spaces are skipped
+// N.B. not a token -- no space is skipped
constexpr auto digitString = DigitString{};
// statement(p) parses Statement<P> for some statement type P that is the
// end-of-statement markers.
// R611 label -> digit [digit]...
-constexpr auto label = spaces >> digitString / spaceCheck;
+constexpr auto label = space >> digitString / spaceCheck;
template<typename PA>
using statementConstructor = construct<Statement<typename PA::resultType>>;
template<typename PA> inline constexpr auto unterminatedStatement(const PA &p) {
return skipEmptyLines >>
- sourced(statementConstructor<PA>{}(maybe(label), spaces >> p));
+ sourced(statementConstructor<PA>{}(maybe(label), space >> p));
}
constexpr auto endOfLine = "\n"_ch / skipEmptyLines ||
fail<const char *>("expected end of line"_en_US);
-constexpr auto endOfStmt = spaces >>
+constexpr auto endOfStmt = space >>
(";"_ch / skipMany(";"_tok) / maybe(endOfLine) || endOfLine);
template<typename PA> inline constexpr auto statement(const PA &p) {
}
constexpr auto ignoredStatementPrefix = skipEmptyLines >>
- maybe(label) >> spaces;
+ (label >> ok || space);
// Error recovery within statements: skip to the end of the line,
// but not over an END or CONTAINS statement.
construct<ActionStmt>{}(indirect(Parser<EventPostStmt>{})) ||
construct<ActionStmt>{}(indirect(Parser<EventWaitStmt>{})) ||
construct<ActionStmt>{}(indirect(Parser<ExitStmt>{})) ||
- "FAIL IMAGE" >> construct<ActionStmt>{}(construct<FailImageStmt>{}) ||
+ "FAIL~IMAGE" >> construct<ActionStmt>{}(construct<FailImageStmt>{}) ||
construct<ActionStmt>{}(indirect(Parser<FlushStmt>{})) ||
construct<ActionStmt>{}(indirect(Parser<FormTeamStmt>{})) ||
construct<ActionStmt>{}(indirect(Parser<GotoStmt>{})) ||
constexpr auto otherIdChar = underscore / !"'\""_ch || extension("$@"_ch);
constexpr auto nonDigitIdChar = letter || otherIdChar;
constexpr auto rawName = nonDigitIdChar >> many(nonDigitIdChar || digit);
-TYPE_PARSER(spaces >> sourced(attempt(rawName) >> construct<Name>{}))
+TYPE_PARSER(space >> sourced(attempt(rawName) >> construct<Name>{}))
constexpr auto keyword = construct<Keyword>{}(name);
// R605 literal-constant ->
// complex-literal-constant | logical-literal-constant |
// char-literal-constant | boz-literal-constant
TYPE_PARSER(construct<LiteralConstant>{}(Parser<HollerithLiteralConstant>{}) ||
- construct<LiteralConstant>{}(spaces >> realLiteralConstant) ||
+ construct<LiteralConstant>{}(space >> realLiteralConstant) ||
construct<LiteralConstant>{}(intLiteralConstant) ||
construct<LiteralConstant>{}(Parser<ComplexLiteralConstant>{}) ||
construct<LiteralConstant>{}(Parser<BOZLiteralConstant>{}) ||
TYPE_PARSER(construct<KindSelector>{}(
parenthesized(maybe("KIND ="_tok) >> scalarIntConstantExpr)) ||
extension(construct<KindSelector>{}(
- construct<KindSelector::StarSize>{}("*" >> digitString))))
+ construct<KindSelector::StarSize>{}("*" >> digitString / spaceCheck))))
// R710 signed-digit-string -> [sign] digit-string
-// N.B. Not a complete token -- no spaces are skipped.
+// N.B. Not a complete token -- no space is skipped.
static inline std::int64_t negate(std::uint64_t &&n) {
return -n; // TODO: check for overflow
}
maybe("+"_ch) >> applyFunction(castToSigned, digitString);
// R707 signed-int-literal-constant -> [sign] int-literal-constant
-TYPE_PARSER(spaces >> sourced(construct<SignedIntLiteralConstant>{}(
- signedDigitString, maybe(underscore >> kindParam))))
+TYPE_PARSER(space >> sourced(construct<SignedIntLiteralConstant>{}(
+ signedDigitString, maybe(underscore >> kindParam))) /
+ spaceCheck)
// R708 int-literal-constant -> digit-string [_ kind-param]
TYPE_PARSER(construct<IntLiteralConstant>{}(
- spaces >> digitString, maybe(underscore >> kindParam)))
+ space >> digitString, maybe(underscore >> kindParam)) /
+ spaceCheck)
// R709 kind-param -> digit-string | scalar-int-constant-name
-TYPE_PARSER(construct<KindParam>{}(digitString) ||
+TYPE_PARSER(construct<KindParam>{}(digitString / spaceCheck) ||
construct<KindParam>{}(scalar(integer(constant(name)))))
// R712 sign -> + | -
"-"_ch >> pure(Sign::Negative);
// R713 signed-real-literal-constant -> [sign] real-literal-constant
-constexpr auto signedRealLiteralConstant = spaces >>
+constexpr auto signedRealLiteralConstant = space >>
construct<SignedRealLiteralConstant>{}(maybe(sign), realLiteralConstant);
// R714 real-literal-constant ->
// R716 exponent-letter -> E | D
// Extension: Q
// R717 exponent -> signed-digit-string
-// N.B. Preceding spaces are not skipped.
+// N.B. Preceding space are not skipped.
constexpr auto exponentPart =
("ed"_ch || extension("q"_ch)) >> signedDigitString;
"."_ch >> digitString >> maybe(exponentPart) >> ok ||
digitString >> exponentPart >> ok) >>
construct<RealLiteralConstant::Real>{}),
- maybe(underscore >> kindParam)))
+ maybe(underscore >> kindParam)) /
+ spaceCheck)
// R718 complex-literal-constant -> ( real-part , imag-part )
TYPE_CONTEXT_PARSER("COMPLEX literal constant"_en_US,
// PGI/Intel extension: signed complex literal constant
TYPE_PARSER(construct<SignedComplexLiteralConstant>{}(
- spaces >> sign, Parser<ComplexLiteralConstant>{}))
+ space >> sign, Parser<ComplexLiteralConstant>{}))
// R719 real-part ->
// signed-int-literal-constant | signed-real-literal-constant |
// R723 char-length -> ( type-param-value ) | digit-string
TYPE_PARSER(construct<CharLength>{}(parenthesized(typeParamValue)) ||
- construct<CharLength>{}(spaces >> digitString))
+ construct<CharLength>{}(space >> digitString / spaceCheck))
// R724 char-literal-constant ->
// [kind-param _] ' [rep-char]... ' |
// "rep-char" is any non-control character. Doubled interior quotes are
// combined. Backslash escapes can be enabled.
// PGI extension: nc'...' is Kanji.
-// N.B. charLiteralConstantWithoutKind does not skip preceding spaces.
+// N.B. charLiteralConstantWithoutKind does not skip preceding space.
// N.B. the parsing of "name" takes care to not consume the '_'.
constexpr auto charLiteralConstantWithoutKind =
"'"_ch >> CharLiteral<'\''>{} || "\""_ch >> CharLiteral<'"'>{};
construct<CharLiteralConstant>{}(
kindParam / underscore, charLiteralConstantWithoutKind) ||
construct<CharLiteralConstant>{}(construct<std::optional<KindParam>>{},
- spaces >> charLiteralConstantWithoutKind) ||
+ space >> charLiteralConstantWithoutKind) ||
construct<CharLiteralConstant>{}(
"NC" >> construct<std::optional<KindParam>>{}(
construct<KindParam>{}(construct<KindParam::Kanji>{})),
parenthesized(nonemptyList(Parser<LetterSpec>{}))))
// R865 letter-spec -> letter [- letter]
-TYPE_PARSER(spaces >> (construct<LetterSpec>{}(letter, maybe("-" >> letter)) ||
- construct<LetterSpec>{}(otherIdChar,
- construct<std::optional<const char *>>{})))
+TYPE_PARSER(space >> (construct<LetterSpec>{}(letter, maybe("-" >> letter)) ||
+ construct<LetterSpec>{}(otherIdChar,
+ construct<std::optional<const char *>>{})))
// R867 import-stmt ->
// IMPORT [[::] import-name-list] |
// R1414 local-defined-operator -> defined-unary-op | defined-binary-op
// R1415 use-defined-operator -> defined-unary-op | defined-binary-op
// N.B. The name of the operator is captured without the periods around it.
-TYPE_PARSER(spaces >> "."_ch >>
+TYPE_PARSER(space >> "."_ch >>
construct<DefinedOpName>{}(sourced(some(letter) >> construct<Name>{})) /
"."_ch)
// R1047 masked-elsewhere-stmt ->
// ELSEWHERE ( mask-expr ) [where-construct-name]
TYPE_CONTEXT_PARSER("masked ELSEWHERE statement"_en_US,
- "ELSEWHERE" >> construct<MaskedElsewhereStmt>{}(
- parenthesized(logicalExpr), maybe(name)))
+ "ELSE WHERE" >> construct<MaskedElsewhereStmt>{}(
+ parenthesized(logicalExpr), maybe(name)))
// R1048 elsewhere-stmt -> ELSEWHERE [where-construct-name]
TYPE_CONTEXT_PARSER("ELSEWHERE statement"_en_US,
- "ELSEWHERE" >> construct<ElsewhereStmt>{}(maybe(name)))
+ "ELSE WHERE" >> construct<ElsewhereStmt>{}(maybe(name)))
// R1049 end-where-stmt -> ENDWHERE [where-construct-name]
TYPE_CONTEXT_PARSER("END WHERE statement"_en_US,
// ( team-variable [, coarray-association-list] [, sync-stat-list] )
TYPE_CONTEXT_PARSER("CHANGE TEAM statement"_en_US,
construct<ChangeTeamStmt>{}(maybe(name / ":"),
- "CHANGE TEAM (" >> teamVariable,
+ "CHANGE~TEAM (" >> teamVariable,
defaulted("," >> nonemptyList(Parser<CoarrayAssociation>{})),
defaulted("," >> nonemptyList(statOrErrmsg))) /
")")
// R1114 end-change-team-stmt ->
// END TEAM [( [sync-stat-list] )] [team-construct-name]
-TYPE_CONTEXT_PARSER("END CHANGE TEAM statement"_en_US,
+TYPE_CONTEXT_PARSER("END TEAM statement"_en_US,
"END TEAM" >>
construct<EndChangeTeamStmt>{}(
defaulted(parenthesized(optionalList(statOrErrmsg))), maybe(name)))
TYPE_PARSER(
"LOCAL" >> construct<LocalitySpec>{}(construct<LocalitySpec::Local>{}(
parenthesized(nonemptyList(name)))) ||
- "LOCAL INIT" >>
+ "LOCAL~INIT" >>
construct<LocalitySpec>{}(construct<LocalitySpec::LocalInit>{}(
parenthesized(nonemptyList(name)))) ||
"SHARED" >> construct<LocalitySpec>{}(construct<LocalitySpec::Shared>{}(
// ( [associate-name =>] selector )
TYPE_CONTEXT_PARSER("SELECT RANK statement"_en_US,
construct<SelectRankStmt>{}(maybe(name / ":"),
- "SELECT RANK (" >> maybe(name / "=>"), selector / ")"))
+ "SELECT~RANK (" >> maybe(name / "=>"), selector / ")"))
// R1150 select-rank-case-stmt ->
// RANK ( scalar-int-constant-expr ) [select-construct-name] |
// CLASS IS ( derived-type-spec ) [select-construct-name] |
// CLASS DEFAULT [select-construct-name]
TYPE_CONTEXT_PARSER("type guard statement"_en_US,
- construct<TypeGuardStmt>{}("TYPE IS" >>
+ construct<TypeGuardStmt>{}("TYPE~IS" >>
parenthesized(construct<TypeGuardStmt::Guard>{}(typeSpec)) ||
- "CLASS IS" >> parenthesized(construct<TypeGuardStmt::Guard>{}(
+ "CLASS~IS" >> parenthesized(construct<TypeGuardStmt::Guard>{}(
derivedTypeSpec)) ||
"CLASS" >> construct<TypeGuardStmt::Guard>{}(defaultKeyword),
maybe(name)))
// ERROR STOP [stop-code] [, QUIET = scalar-logical-expr]
TYPE_CONTEXT_PARSER("STOP statement"_en_US,
construct<StopStmt>{}("STOP" >> pure(StopStmt::Kind::Stop) ||
- "ERROR STOP" >> pure(StopStmt::Kind::ErrorStop),
+ "ERROR~STOP" >> pure(StopStmt::Kind::ErrorStop),
maybe(Parser<StopCode>{}), maybe(", QUIET =" >> scalarLogicalExpr)))
// R1162 stop-code -> scalar-default-char-expr | scalar-int-expr
// R1164 sync-all-stmt -> SYNC ALL [( [sync-stat-list] )]
TYPE_CONTEXT_PARSER("SYNC ALL statement"_en_US,
- "SYNC ALL" >> construct<SyncAllStmt>{}(
+ "SYNC~ALL" >> construct<SyncAllStmt>{}(
defaulted(parenthesized(optionalList(statOrErrmsg)))))
// R1166 sync-images-stmt -> SYNC IMAGES ( image-set [, sync-stat-list] )
// R1167 image-set -> int-expr | *
TYPE_CONTEXT_PARSER("SYNC IMAGES statement"_en_US,
- "SYNC IMAGES" >> parenthesized(construct<SyncImagesStmt>{}(
+ "SYNC~IMAGES" >> parenthesized(construct<SyncImagesStmt>{}(
construct<SyncImagesStmt::ImageSet>{}(intExpr) ||
construct<SyncImagesStmt::ImageSet>{}(star),
defaulted("," >> nonemptyList(statOrErrmsg)))))
// R1168 sync-memory-stmt -> SYNC MEMORY [( [sync-stat-list] )]
TYPE_CONTEXT_PARSER("SYNC MEMORY statement"_en_US,
- "SYNC MEMORY" >> construct<SyncMemoryStmt>{}(
+ "SYNC~MEMORY" >> construct<SyncMemoryStmt>{}(
defaulted(parenthesized(optionalList(statOrErrmsg)))))
// R1169 sync-team-stmt -> SYNC TEAM ( team-variable [, sync-stat-list] )
TYPE_CONTEXT_PARSER("SYNC TEAM statement"_en_US,
- "SYNC TEAM" >> parenthesized(construct<SyncTeamStmt>{}(teamVariable,
+ "SYNC~TEAM" >> parenthesized(construct<SyncTeamStmt>{}(teamVariable,
defaulted("," >> nonemptyList(statOrErrmsg)))))
// R1170 event-post-stmt -> EVENT POST ( event-variable [, sync-stat-list] )
// R1171 event-variable -> scalar-variable
TYPE_CONTEXT_PARSER("EVENT POST statement"_en_US,
- "EVENT POST" >> parenthesized(construct<EventPostStmt>{}(scalar(variable),
+ "EVENT~POST" >> parenthesized(construct<EventPostStmt>{}(scalar(variable),
defaulted("," >> nonemptyList(statOrErrmsg)))))
// R1172 event-wait-stmt ->
// EVENT WAIT ( event-variable [, event-wait-spec-list] )
TYPE_CONTEXT_PARSER("EVENT WAIT statement"_en_US,
- "EVENT WAIT" >>
+ "EVENT~WAIT" >>
parenthesized(construct<EventWaitStmt>{}(scalar(variable),
defaulted(
"," >> nonemptyList(Parser<EventWaitStmt::EventWaitSpec>{})))))
// FORM TEAM ( team-number , team-variable [, form-team-spec-list] )
// R1176 team-number -> scalar-int-expr
TYPE_CONTEXT_PARSER("FORM TEAM statement"_en_US,
- "FORM TEAM" >>
+ "FORM~TEAM" >>
parenthesized(construct<FormTeamStmt>{}(scalarIntExpr,
"," >> teamVariable,
defaulted(
// R1321 char-string-edit-desc
// N.B. C1313 disallows any kind parameter on the character literal.
-constexpr auto charStringEditDesc = spaces >>
+constexpr auto charStringEditDesc = space >>
(charLiteralConstantWithoutKind || rawHollerithLiteral);
// R1303 format-items -> format-item [[,] format-item]...
return n; // TODO: check for overflow
}
-constexpr auto repeat = spaces >> applyFunction(castU64ToInt, digitString);
+constexpr auto repeat = space >> applyFunction(castU64ToInt, digitString);
// R1304 format-item ->
// [r] data-edit-desc | control-edit-desc | char-string-edit-desc |
// R1312 v -> [sign] digit-string
TYPE_PARSER("DT" >>
construct<format::DerivedTypeDataEditDesc>{}(
- spaces >> defaulted(charLiteralConstantWithoutKind),
- defaulted(parenthesized(nonemptyList(spaces >> signedDigitString)))))
+ space >> defaulted(charLiteralConstantWithoutKind),
+ defaulted(parenthesized(nonemptyList(space >> signedDigitString)))))
// R1314 k -> [sign] digit-string
static inline int castS64ToInt(std::int64_t &&n) {
return n; // TODO: check for overflow
}
-constexpr auto scaleFactor = spaces >>
+constexpr auto scaleFactor = space >>
applyFunction(castS64ToInt, signedDigitString);
// R1313 control-edit-desc ->
"PROGRAM" >> name / maybe(extension(parenthesized(ok)))))
// R1403 end-program-stmt -> END [PROGRAM [program-name]]
+constexpr auto bareEnd = "END" >> defaulted(cut >> maybe(name));
TYPE_CONTEXT_PARSER("END PROGRAM statement"_en_US,
- construct<EndProgramStmt>{}("END" >> defaulted("PROGRAM" >> maybe(name))))
+ construct<EndProgramStmt>{}("END PROGRAM" >> maybe(name) || bareEnd))
// R1404 module ->
// module-stmt [specification-part] [module-subprogram-part]
// R1406 end-module-stmt -> END [MODULE [module-name]]
TYPE_CONTEXT_PARSER("END MODULE statement"_en_US,
- "END" >> construct<EndModuleStmt>{}(defaulted("MODULE" >> maybe(name))))
+ construct<EndModuleStmt>{}("END MODULE" >> maybe(name) || bareEnd))
// R1407 module-subprogram-part -> contains-stmt [module-subprogram]...
TYPE_CONTEXT_PARSER("module subprogram part"_en_US,
// R1419 end-submodule-stmt -> END [SUBMODULE [submodule-name]]
TYPE_CONTEXT_PARSER("END SUBMODULE statement"_en_US,
- "END" >>
- construct<EndSubmoduleStmt>{}(defaulted("SUBMODULE" >> maybe(name))))
+ construct<EndSubmoduleStmt>{}("END SUBMODULE" >> maybe(name) || bareEnd))
// R1420 block-data -> block-data-stmt [specification-part] end-block-data-stmt
TYPE_CONTEXT_PARSER("BLOCK DATA subprogram"_en_US,
// R1422 end-block-data-stmt -> END [BLOCK DATA [block-data-name]]
TYPE_CONTEXT_PARSER("END BLOCK DATA statement"_en_US,
- "END" >>
- construct<EndBlockDataStmt>{}(defaulted("BLOCK DATA" >> maybe(name))))
+ construct<EndBlockDataStmt>{}("END BLOCK DATA" >> maybe(name) || bareEnd))
// R1501 interface-block ->
// interface-stmt [interface-specification]... end-interface-stmt
// R1503 interface-stmt -> INTERFACE [generic-spec] | ABSTRACT INTERFACE
TYPE_PARSER("INTERFACE" >> construct<InterfaceStmt>{}(maybe(genericSpec)) ||
- "ABSTRACT INTERFACE" >> construct<InterfaceStmt>{}(construct<Abstract>{}))
+ "ABSTRACT~INTERFACE" >> construct<InterfaceStmt>{}(construct<Abstract>{}))
// R1504 end-interface-stmt -> END INTERFACE [generic-spec]
TYPE_PARSER(
constexpr auto specificProcedure = name;
// R1506 procedure-stmt -> [MODULE] PROCEDURE [::] specific-procedure-list
-TYPE_PARSER(construct<ProcedureStmt>{}("MODULE PROCEDURE" >>
+TYPE_PARSER(construct<ProcedureStmt>{}("MODULE~PROCEDURE" >>
pure(ProcedureStmt::Kind::ModuleProcedure),
maybe("::"_tok) >> nonemptyList(specificProcedure)) ||
construct<ProcedureStmt>{}(
// R1533 end-function-stmt -> END [FUNCTION [function-name]]
TYPE_PARSER(
- "END" >> construct<EndFunctionStmt>{}(defaulted("FUNCTION" >> maybe(name))))
+ construct<EndFunctionStmt>{}("END FUNCTION" >> maybe(name) || bareEnd))
// R1534 subroutine-subprogram ->
// subroutine-stmt [specification-part] [execution-part]
TYPE_PARSER(construct<DummyArg>{}(name) || construct<DummyArg>{}(star))
// R1537 end-subroutine-stmt -> END [SUBROUTINE [subroutine-name]]
-TYPE_PARSER("END" >>
- construct<EndSubroutineStmt>{}(defaulted("SUBROUTINE" >> maybe(name))))
+TYPE_PARSER(
+ construct<EndSubroutineStmt>{}("END SUBROUTINE" >> maybe(name) || bareEnd))
// R1538 separate-module-subprogram ->
// mp-subprogram-stmt [specification-part] [execution-part]
// R1539 mp-subprogram-stmt -> MODULE PROCEDURE procedure-name
TYPE_CONTEXT_PARSER("MODULE PROCEDURE statement"_en_US,
- construct<MpSubprogramStmt>{}("MODULE PROCEDURE" >> name))
+ construct<MpSubprogramStmt>{}("MODULE~PROCEDURE" >> name))
// R1540 end-mp-subprogram-stmt -> END [PROCEDURE [procedure-name]]
TYPE_CONTEXT_PARSER("END PROCEDURE statement"_en_US,
- "END" >>
- construct<EndMpSubprogramStmt>{}(defaulted("PROCEDURE" >> maybe(name))))
+ construct<EndMpSubprogramStmt>{}("END PROCEDURE" >> maybe(name) || bareEnd))
// R1541 entry-stmt -> ENTRY entry-name [( [dummy-arg-list] ) [suffix]]
TYPE_PARSER("ENTRY" >>
// 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 beginDirective = skipEmptyLines >> space >> "!"_ch;
+constexpr auto endDirective = space >> endOfLine;
constexpr auto ivdep = "DIR$ IVDEP" >> construct<CompilerDirective::IVDEP>{};
constexpr auto ignore_tkr = "DIR$ IGNORE_TKR" >>
optionalList(construct<CompilerDirective::IgnoreTKR>{}(
CharPredicateGuard{IsDecimalDigit, "expected digit"_en_US};
// "x"_ch matches one instance of the character 'x' without skipping any
-// spaces before or after. The parser returns the location of the character
+// space before or after. The parser returns the location of the character
// on success.
class AnyOfChar {
public:
return AnyOfChar{str, n};
}
-// Skips over spaces. Always succeeds.
-constexpr struct Spaces {
+// Skips over optional spaces. Always succeeds.
+constexpr struct Space {
using resultType = Success;
- constexpr Spaces() {}
+ constexpr Space() {}
static std::optional<Success> Parse(ParseState *state) {
while (std::optional<char> ch{state->PeekAtNextChar()}) {
if (*ch != ' ') {
}
return {Success{}};
}
-} spaces;
+} space;
+
+// Skips a space that in free from requires a warning if it precedes a
+// character that could begin an identifier or keyword. Always succeeds.
+static inline void MissingSpace(ParseState *state) {
+ if (!state->inFixedForm()) {
+ state->set_anyConformanceViolation();
+ if (state->warnOnNonstandardUsage()) {
+ state->PutMessage("expected space"_en_US);
+ }
+ }
+}
-// Warn about a missing space that must be present in free form.
-// Always succeeds.
constexpr struct SpaceCheck {
using resultType = Success;
constexpr SpaceCheck() {}
static std::optional<Success> Parse(ParseState *state) {
- if (!state->inFixedForm()) {
- if (std::optional<char> ch{state->PeekAtNextChar()}) {
- if (IsLegalInIdentifier(*ch)) {
- state->PutMessage("expected space"_en_US);
- }
+ if (std::optional<char> ch{state->PeekAtNextChar()}) {
+ if (*ch == ' ') {
+ state->UncheckedAdvance();
+ return space.Parse(state);
+ }
+ if (IsLegalInIdentifier(*ch)) {
+ MissingSpace(state);
}
}
return {Success{}};
}
} spaceCheck;
+// Matches a token string. Spaces in the token string denote where
+// an optional space may appear in the source; the character '~' in
+// a token string denotes a space that, if missing in free form,
+// elicits a warning. Spaces before and after the token are also
+// skipped.
+//
+// Token strings appear in the grammar as C++ user-defined literals
+// like "BIND ( C )"_tok. The _tok suffix is not required before
+// the sequencing operator >> or after the sequencing operator /.
class TokenStringMatch {
public:
using resultType = Success;
: str_{str}, bytes_{n} {}
constexpr TokenStringMatch(const char *str) : str_{str} {}
std::optional<Success> Parse(ParseState *state) const {
- spaces.Parse(state);
+ space.Parse(state);
const char *start{state->GetLocation()};
const char *p{str_};
std::optional<const char *> at; // initially empty
for (std::size_t j{0}; j < bytes_ && *p != '\0'; ++j, ++p) {
- const auto spaceSkipping{*p == ' '};
+ const auto spaceSkipping{*p == ' ' || *p == '~'};
if (spaceSkipping) {
- if (j + 1 == bytes_ || p[1] == ' ' || p[1] == '\0') {
+ if (j + 1 == bytes_ || p[1] == ' ' || p[1] == '~' || p[1] == '\0') {
continue; // redundant; ignore
}
}
}
}
if (spaceSkipping) {
- // medial space: space accepted, none required
- // TODO: designate and enforce free-form mandatory white space
if (**at == ' ') {
at = nextCh.Parse(state);
if (!at.has_value()) {
return {};
}
+ } else if (*p == '~') {
+ // This space is notionally required in free form.
+ MissingSpace(state);
}
// 'at' remains full for next iteration
} else if (**at == ToLowerCaseLetter(*p)) {
return {};
}
}
- return spaces.Parse(state);
+ if (IsLegalInIdentifier(p[-1])) {
+ return spaceCheck.Parse(state);
+ }
+ return space.Parse(state);
}
private:
}
};
- spaces.Parse(state);
+ space.Parse(state);
const char *start{state->GetLocation()};
std::optional<const char *> at{nextCh.Parse(state)};
if (!at.has_value()) {
!baseChar(**at)) {
return {};
}
+ spaceCheck.Parse(state);
}
if (content.empty()) {
struct HollerithLiteral {
using resultType = std::string;
static std::optional<std::string> Parse(ParseState *state) {
- spaces.Parse(state);
+ space.Parse(state);
const char *start{state->GetLocation()};
std::optional<std::uint64_t> charCount{DigitString{}.Parse(state)};
if (!charCount || *charCount < 1) {