2018-05-01 21:50:34 +02:00
|
|
|
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2018-02-16 20:42:17 +01:00
|
|
|
#ifndef FORTRAN_PARSER_TOKEN_PARSERS_H_
|
|
|
|
#define FORTRAN_PARSER_TOKEN_PARSERS_H_
|
2018-01-30 20:50:36 +01:00
|
|
|
|
|
|
|
// These parsers are driven by the Fortran grammar (grammar.h) to consume
|
2018-02-16 19:41:16 +01:00
|
|
|
// the prescanned character stream and recognize context-sensitive tokens.
|
2018-01-30 20:50:36 +01:00
|
|
|
|
|
|
|
#include "basic-parsers.h"
|
2018-04-18 20:26:17 +02:00
|
|
|
#include "char-set.h"
|
2018-02-27 23:02:10 +01:00
|
|
|
#include "characters.h"
|
2018-01-30 20:50:36 +01:00
|
|
|
#include "idioms.h"
|
2018-04-21 00:19:20 +02:00
|
|
|
#include "instrumented-parser.h"
|
2018-02-09 23:04:11 +01:00
|
|
|
#include "provenance.h"
|
2018-04-21 00:19:20 +02:00
|
|
|
#include "type-parsers.h"
|
2018-03-20 18:59:07 +01:00
|
|
|
#include <cstddef>
|
2018-01-30 20:50:36 +01:00
|
|
|
#include <cstring>
|
|
|
|
#include <functional>
|
|
|
|
#include <limits>
|
|
|
|
#include <list>
|
|
|
|
#include <optional>
|
|
|
|
#include <string>
|
|
|
|
|
2018-05-02 22:48:12 +02:00
|
|
|
namespace Fortran::parser {
|
2018-01-30 20:50:36 +01:00
|
|
|
|
2018-03-30 01:34:03 +02:00
|
|
|
// "xyz"_ch matches one instance of the characters x, y, or z without skipping
|
|
|
|
// any spaces before or after. The parser returns the location of the character
|
2018-03-20 18:59:07 +01:00
|
|
|
// on success.
|
2018-04-18 20:26:17 +02:00
|
|
|
class AnyOfChars {
|
2018-02-05 21:54:36 +01:00
|
|
|
public:
|
2018-03-20 18:59:07 +01:00
|
|
|
using resultType = const char *;
|
2018-04-18 20:26:17 +02:00
|
|
|
constexpr AnyOfChars(const AnyOfChars &) = default;
|
|
|
|
constexpr AnyOfChars(SetOfChars set) : set_{set} {}
|
2018-04-20 20:23:51 +02:00
|
|
|
std::optional<const char *> Parse(ParseState &state) const {
|
|
|
|
if (std::optional<const char *> at{state.PeekAtNextChar()}) {
|
2018-04-19 02:05:07 +02:00
|
|
|
if (set_.Has(**at)) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
2018-04-18 20:26:17 +02:00
|
|
|
return at;
|
2018-03-20 18:59:07 +01:00
|
|
|
}
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(MessageExpectedText{set_});
|
2018-03-20 18:59:07 +01:00
|
|
|
return {};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
|
|
|
|
private:
|
2018-04-19 01:28:29 +02:00
|
|
|
const SetOfChars set_;
|
2018-01-30 20:50:36 +01:00
|
|
|
};
|
|
|
|
|
2018-04-18 20:26:17 +02:00
|
|
|
constexpr AnyOfChars operator""_ch(const char str[], std::size_t n) {
|
2018-04-19 01:39:28 +02:00
|
|
|
return AnyOfChars{SetOfChars(str, n)};
|
2018-03-20 18:59:07 +01:00
|
|
|
}
|
|
|
|
|
2018-04-18 20:26:17 +02:00
|
|
|
constexpr auto letter = "abcdefghijklmnopqrstuvwxyz"_ch;
|
|
|
|
constexpr auto digit = "0123456789"_ch;
|
|
|
|
|
2018-03-30 01:06:31 +02:00
|
|
|
// Skips over optional spaces. Always succeeds.
|
|
|
|
constexpr struct Space {
|
2018-01-30 20:50:36 +01:00
|
|
|
using resultType = Success;
|
2018-03-30 01:06:31 +02:00
|
|
|
constexpr Space() {}
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Success> Parse(ParseState &state) {
|
|
|
|
while (std::optional<const char *> p{state.PeekAtNextChar()}) {
|
2018-04-03 01:33:10 +02:00
|
|
|
if (**p != ' ') {
|
2018-03-01 01:56:10 +01:00
|
|
|
break;
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-03-01 01:56:10 +01:00
|
|
|
return {Success{}};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-03-30 01:06:31 +02:00
|
|
|
} space;
|
|
|
|
|
2018-03-30 02:07:19 +02:00
|
|
|
// Skips a space that in free form requires a warning if it precedes a
|
2018-03-30 01:06:31 +02:00
|
|
|
// character that could begin an identifier or keyword. Always succeeds.
|
2018-04-20 20:23:51 +02:00
|
|
|
inline void MissingSpace(ParseState &state) {
|
|
|
|
if (!state.inFixedForm()) {
|
|
|
|
state.set_anyConformanceViolation();
|
|
|
|
if (state.warnOnNonstandardUsage()) {
|
|
|
|
state.Say("expected space"_err_en_US);
|
2018-03-30 01:06:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-30 20:50:36 +01:00
|
|
|
|
2018-03-29 21:16:10 +02:00
|
|
|
constexpr struct SpaceCheck {
|
|
|
|
using resultType = Success;
|
|
|
|
constexpr SpaceCheck() {}
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Success> Parse(ParseState &state) {
|
|
|
|
if (std::optional<const char *> p{state.PeekAtNextChar()}) {
|
2018-04-03 01:33:10 +02:00
|
|
|
char ch{**p};
|
|
|
|
if (ch == ' ') {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
2018-03-30 01:06:31 +02:00
|
|
|
return space.Parse(state);
|
|
|
|
}
|
2018-04-03 01:33:10 +02:00
|
|
|
if (IsLegalInIdentifier(ch)) {
|
2018-03-30 01:06:31 +02:00
|
|
|
MissingSpace(state);
|
2018-03-29 21:16:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return {Success{}};
|
|
|
|
}
|
|
|
|
} spaceCheck;
|
|
|
|
|
2018-03-30 01:06:31 +02:00
|
|
|
// Matches a token string. Spaces in the token string denote where
|
2018-03-30 20:11:48 +02:00
|
|
|
// spaces may appear in the source; they can be made mandatory for
|
|
|
|
// some free form keyword sequences. Missing mandatory spaces in free
|
|
|
|
// form elicit a warning; they are not necessary for recognition.
|
|
|
|
// Spaces before and after the token are also skipped.
|
2018-03-30 01:06:31 +02:00
|
|
|
//
|
|
|
|
// Token strings appear in the grammar as C++ user-defined literals
|
2018-03-30 20:11:48 +02:00
|
|
|
// like "BIND ( C )"_tok and "SYNC ALL"_sptok. The _tok suffix is implied
|
|
|
|
// when a string literal appears before the sequencing operator >> or
|
|
|
|
// after the sequencing operator /.
|
2018-01-30 20:50:36 +01:00
|
|
|
class TokenStringMatch {
|
2018-02-05 21:54:36 +01:00
|
|
|
public:
|
2018-01-30 20:50:36 +01:00
|
|
|
using resultType = Success;
|
|
|
|
constexpr TokenStringMatch(const TokenStringMatch &) = default;
|
2018-03-30 20:11:48 +02:00
|
|
|
constexpr TokenStringMatch(const char *str, std::size_t n, bool mandatory)
|
|
|
|
: str_{str}, bytes_{n}, mandatoryFreeFormSpace_{mandatory} {}
|
|
|
|
constexpr TokenStringMatch(const char *str, bool mandatory)
|
|
|
|
: str_{str}, mandatoryFreeFormSpace_{mandatory} {}
|
2018-04-20 20:23:51 +02:00
|
|
|
std::optional<Success> Parse(ParseState &state) const {
|
2018-03-30 01:06:31 +02:00
|
|
|
space.Parse(state);
|
2018-04-20 20:23:51 +02:00
|
|
|
const char *start{state.GetLocation()};
|
2018-01-30 20:50:36 +01:00
|
|
|
const char *p{str_};
|
2018-03-20 18:59:07 +01:00
|
|
|
std::optional<const char *> at; // initially empty
|
|
|
|
for (std::size_t j{0}; j < bytes_ && *p != '\0'; ++j, ++p) {
|
2018-03-30 20:11:48 +02:00
|
|
|
const auto spaceSkipping{*p == ' '};
|
2018-01-30 20:50:36 +01:00
|
|
|
if (spaceSkipping) {
|
2018-03-30 20:11:48 +02:00
|
|
|
if (j + 1 == bytes_ || p[1] == ' ' || p[1] == '\0') {
|
2018-01-30 20:50:36 +01:00
|
|
|
continue; // redundant; ignore
|
|
|
|
}
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (!at.has_value()) {
|
|
|
|
at = nextCh.Parse(state);
|
|
|
|
if (!at.has_value()) {
|
|
|
|
return {};
|
|
|
|
}
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
if (spaceSkipping) {
|
2018-03-20 18:59:07 +01:00
|
|
|
if (**at == ' ') {
|
|
|
|
at = nextCh.Parse(state);
|
|
|
|
if (!at.has_value()) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-30 20:11:48 +02:00
|
|
|
} else if (mandatoryFreeFormSpace_) {
|
2018-03-30 01:06:31 +02:00
|
|
|
MissingSpace(state);
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
// 'at' remains full for next iteration
|
|
|
|
} else if (**at == ToLowerCaseLetter(*p)) {
|
|
|
|
at.reset();
|
2018-01-30 20:50:36 +01:00
|
|
|
} else {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(start, MessageExpectedText{str_, bytes_});
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
2018-03-30 01:06:31 +02:00
|
|
|
if (IsLegalInIdentifier(p[-1])) {
|
|
|
|
return spaceCheck.Parse(state);
|
2018-03-30 20:11:48 +02:00
|
|
|
} else {
|
|
|
|
return space.Parse(state);
|
2018-03-30 01:06:31 +02:00
|
|
|
}
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-02-05 23:29:26 +01:00
|
|
|
|
2018-02-05 21:54:36 +01:00
|
|
|
private:
|
2018-01-30 20:50:36 +01:00
|
|
|
const char *const str_;
|
2018-04-19 22:51:25 +02:00
|
|
|
const std::size_t bytes_{std::string::npos};
|
2018-03-30 20:11:48 +02:00
|
|
|
const bool mandatoryFreeFormSpace_;
|
2018-01-30 20:50:36 +01:00
|
|
|
};
|
|
|
|
|
2018-03-20 18:59:07 +01:00
|
|
|
constexpr TokenStringMatch operator""_tok(const char str[], std::size_t n) {
|
2018-03-30 20:11:48 +02:00
|
|
|
return TokenStringMatch{str, n, false};
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr TokenStringMatch operator""_sptok(const char str[], std::size_t n) {
|
|
|
|
return TokenStringMatch{str, n, true};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class PA, std::enable_if_t<std::is_class<PA>::value, int> = 0>
|
2018-02-05 23:29:26 +01:00
|
|
|
inline constexpr SequenceParser<TokenStringMatch, PA> operator>>(
|
|
|
|
const char *str, const PA &p) {
|
2018-03-30 20:11:48 +02:00
|
|
|
return SequenceParser<TokenStringMatch, PA>{TokenStringMatch{str, false}, p};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class PA, std::enable_if_t<std::is_class<PA>::value, int> = 0>
|
2018-02-05 23:29:26 +01:00
|
|
|
inline constexpr InvertedSequenceParser<PA, TokenStringMatch> operator/(
|
|
|
|
const PA &p, const char *str) {
|
2018-03-30 20:11:48 +02:00
|
|
|
return InvertedSequenceParser<PA, TokenStringMatch>{
|
|
|
|
p, TokenStringMatch{str, false}};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class PA>
|
|
|
|
inline constexpr SequenceParser<TokenStringMatch,
|
2018-02-05 23:29:26 +01:00
|
|
|
InvertedSequenceParser<PA, TokenStringMatch>>
|
2018-01-30 20:50:36 +01:00
|
|
|
parenthesized(const PA &p) {
|
|
|
|
return "(" >> p / ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class PA>
|
|
|
|
inline constexpr SequenceParser<TokenStringMatch,
|
2018-02-05 23:29:26 +01:00
|
|
|
InvertedSequenceParser<PA, TokenStringMatch>>
|
2018-01-30 20:50:36 +01:00
|
|
|
bracketed(const PA &p) {
|
|
|
|
return "[" >> p / "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Quoted character literal constants.
|
|
|
|
struct CharLiteralChar {
|
|
|
|
struct Result {
|
|
|
|
Result(char c, bool esc) : ch{c}, wasEscaped{esc} {}
|
|
|
|
static Result Bare(char c) { return Result{c, false}; }
|
|
|
|
static Result Escaped(char c) { return Result{c, true}; }
|
|
|
|
char ch;
|
|
|
|
bool wasEscaped;
|
|
|
|
};
|
|
|
|
using resultType = Result;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Result> Parse(ParseState &state) {
|
|
|
|
auto at = state.GetLocation();
|
2018-03-20 18:59:07 +01:00
|
|
|
std::optional<const char *> och{nextCh.Parse(state)};
|
2018-01-30 20:50:36 +01:00
|
|
|
if (!och.has_value()) {
|
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
char ch{**och};
|
2018-01-30 20:50:36 +01:00
|
|
|
if (ch == '\n') {
|
2018-04-23 21:17:11 +02:00
|
|
|
state.Say(CharBlock{at, state.GetLocation()},
|
|
|
|
"unclosed character constant"_err_en_US);
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-02-16 20:14:11 +01:00
|
|
|
if (ch != '\\') {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {Result::Bare(ch)};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (!(och = nextCh.Parse(state)).has_value()) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
ch = **och;
|
2018-02-27 23:02:10 +01:00
|
|
|
if (ch == '\n') {
|
2018-04-23 21:17:11 +02:00
|
|
|
state.Say(CharBlock{at, state.GetLocation()},
|
|
|
|
"unclosed character constant"_err_en_US);
|
2018-02-17 01:57:40 +01:00
|
|
|
return {};
|
2018-02-27 23:02:10 +01:00
|
|
|
}
|
|
|
|
if (std::optional<char> escChar{BackslashEscapeValue(ch)}) {
|
|
|
|
return {Result::Escaped(*escChar)};
|
|
|
|
}
|
|
|
|
if (IsOctalDigit(ch)) {
|
|
|
|
ch -= '0';
|
|
|
|
for (int j = (ch > 3 ? 1 : 2); j-- > 0;) {
|
2018-05-07 21:57:08 +02:00
|
|
|
static constexpr auto octalDigit = attempt("01234567"_ch);
|
2018-03-14 01:11:26 +01:00
|
|
|
och = octalDigit.Parse(state);
|
|
|
|
if (och.has_value()) {
|
2018-03-20 18:59:07 +01:00
|
|
|
ch = 8 * ch + **och - '0';
|
|
|
|
} else {
|
|
|
|
break;
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-02-27 23:02:10 +01:00
|
|
|
}
|
|
|
|
} else if (ch == 'x' || ch == 'X') {
|
|
|
|
ch = 0;
|
2018-05-07 21:57:08 +02:00
|
|
|
static constexpr auto hexDigit = "0123456789abcdefABCDEF"_ch;
|
|
|
|
och = hexDigit.Parse(state);
|
|
|
|
if (och.has_value()) {
|
|
|
|
ch = HexadecimalDigitValue(**och);
|
|
|
|
static constexpr auto hexDigit2 = attempt("0123456789abcdefABCDEF"_ch);
|
|
|
|
och = hexDigit2.Parse(state);
|
2018-03-14 01:11:26 +01:00
|
|
|
if (och.has_value()) {
|
2018-03-20 18:59:07 +01:00
|
|
|
ch = 16 * ch + HexadecimalDigitValue(**och);
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-05-07 21:57:08 +02:00
|
|
|
} else {
|
|
|
|
return {};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-02-27 23:02:10 +01:00
|
|
|
} else {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(at, "bad escaped character"_en_US);
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-02-27 23:02:10 +01:00
|
|
|
return {Result::Escaped(ch)};
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-02-05 23:29:26 +01:00
|
|
|
template<char quote> struct CharLiteral {
|
2018-01-30 20:50:36 +01:00
|
|
|
using resultType = std::string;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<std::string> Parse(ParseState &state) {
|
2018-01-30 20:50:36 +01:00
|
|
|
std::string str;
|
|
|
|
static constexpr auto nextch = attempt(CharLiteralChar{});
|
|
|
|
while (std::optional<CharLiteralChar::Result> ch{nextch.Parse(state)}) {
|
|
|
|
if (ch->ch == quote && !ch->wasEscaped) {
|
2018-04-19 01:39:28 +02:00
|
|
|
static constexpr auto doubled = attempt(AnyOfChars{SetOfChars{quote}});
|
2018-01-30 20:50:36 +01:00
|
|
|
if (!doubled.Parse(state).has_value()) {
|
|
|
|
return {str};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str += ch->ch;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-20 20:23:51 +02:00
|
|
|
static bool IsNonstandardUsageOk(ParseState &state) {
|
|
|
|
if (state.strictConformance()) {
|
2018-03-01 01:56:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
state.set_anyConformanceViolation();
|
|
|
|
if (state.warnOnNonstandardUsage()) {
|
|
|
|
state.Say("nonstandard usage"_en_US);
|
2018-03-01 01:56:10 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-01-30 20:50:36 +01:00
|
|
|
// Parse "BOZ" binary literal quoted constants.
|
|
|
|
// As extensions, support X as an alternate hexadecimal marker, and allow
|
2018-03-01 01:56:10 +01:00
|
|
|
// BOZX markers to appear as suffixes.
|
2018-01-30 20:50:36 +01:00
|
|
|
struct BOZLiteral {
|
|
|
|
using resultType = std::uint64_t;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<std::uint64_t> Parse(ParseState &state) {
|
2018-01-30 20:50:36 +01:00
|
|
|
std::optional<int> shift;
|
|
|
|
auto baseChar = [&shift](char ch) -> bool {
|
2018-03-20 18:59:07 +01:00
|
|
|
switch (ch) {
|
|
|
|
case 'b': shift = 1; return true;
|
|
|
|
case 'o': shift = 3; return true;
|
|
|
|
case 'z': shift = 4; return true;
|
|
|
|
case 'x': shift = 4; return true;
|
2018-01-30 20:50:36 +01:00
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-30 01:06:31 +02:00
|
|
|
space.Parse(state);
|
2018-04-20 20:23:51 +02:00
|
|
|
const char *start{state.GetLocation()};
|
2018-03-20 18:59:07 +01:00
|
|
|
std::optional<const char *> at{nextCh.Parse(state)};
|
|
|
|
if (!at.has_value()) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (**at == 'x' && !IsNonstandardUsageOk(state)) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (baseChar(**at)) {
|
|
|
|
at = nextCh.Parse(state);
|
|
|
|
if (!at.has_value()) {
|
|
|
|
return {};
|
|
|
|
}
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
|
2018-03-20 18:59:07 +01:00
|
|
|
char quote = **at;
|
2018-01-30 20:50:36 +01:00
|
|
|
if (quote != '\'' && quote != '"') {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string content;
|
|
|
|
while (true) {
|
2018-03-20 18:59:07 +01:00
|
|
|
at = nextCh.Parse(state);
|
|
|
|
if (!at.has_value()) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (**at == quote) {
|
2018-01-30 20:50:36 +01:00
|
|
|
break;
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (**at == ' ') {
|
2018-03-01 01:56:10 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
if (!IsHexadecimalDigit(**at)) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
content += **at;
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
|
2018-03-01 01:56:10 +01:00
|
|
|
if (!shift) {
|
|
|
|
// extension: base allowed to appear as suffix, too
|
2018-03-20 18:59:07 +01:00
|
|
|
if (!IsNonstandardUsageOk(state) || !(at = nextCh.Parse(state)) ||
|
|
|
|
!baseChar(**at)) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-30 01:06:31 +02:00
|
|
|
spaceCheck.Parse(state);
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (content.empty()) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(start, "no digit in BOZ literal"_err_en_US);
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::uint64_t value{0};
|
|
|
|
for (auto digit : content) {
|
|
|
|
digit = HexadecimalDigitValue(digit);
|
|
|
|
if ((digit >> *shift) > 0) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(start, "bad digit in BOZ literal"_err_en_US);
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
std::uint64_t was{value};
|
|
|
|
value <<= *shift;
|
|
|
|
if ((value >> *shift) != was) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(start, "excessive digits in BOZ literal"_err_en_US);
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
value |= digit;
|
|
|
|
}
|
|
|
|
return {value};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-05-03 00:16:53 +02:00
|
|
|
// R711 digit-string -> digit [digit]...
|
|
|
|
// N.B. not a token -- no space is skipped
|
|
|
|
constexpr struct DigitString {
|
2018-01-30 20:50:36 +01:00
|
|
|
using resultType = std::uint64_t;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<std::uint64_t> Parse(ParseState &state) {
|
2018-05-03 00:16:53 +02:00
|
|
|
std::optional<const char *> firstDigit{digit.Parse(state)};
|
2018-03-20 18:59:07 +01:00
|
|
|
if (!firstDigit.has_value()) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
std::uint64_t value = **firstDigit - '0';
|
2018-01-30 20:50:36 +01:00
|
|
|
bool overflow{false};
|
2018-05-03 00:16:53 +02:00
|
|
|
static constexpr auto getDigit = attempt(digit);
|
2018-01-30 20:50:36 +01:00
|
|
|
while (auto nextDigit{getDigit.Parse(state)}) {
|
|
|
|
if (value > std::numeric_limits<std::uint64_t>::max() / 10) {
|
|
|
|
overflow = true;
|
|
|
|
}
|
|
|
|
value *= 10;
|
2018-03-20 18:59:07 +01:00
|
|
|
int digitValue = **nextDigit - '0';
|
2018-01-30 20:50:36 +01:00
|
|
|
if (value > std::numeric_limits<std::uint64_t>::max() - digitValue) {
|
|
|
|
overflow = true;
|
|
|
|
}
|
|
|
|
value += digitValue;
|
|
|
|
}
|
|
|
|
if (overflow) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(*firstDigit, "overflow in decimal literal"_err_en_US);
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
return {value};
|
|
|
|
}
|
2018-05-03 00:16:53 +02:00
|
|
|
} digitString;
|
2018-01-30 20:50:36 +01:00
|
|
|
|
2018-04-03 01:33:10 +02:00
|
|
|
constexpr struct SkipDigitString {
|
|
|
|
using resultType = Success;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Success> Parse(ParseState &state) {
|
|
|
|
if (std::optional<const char *> ch1{state.PeekAtNextChar()}) {
|
2018-04-03 01:33:10 +02:00
|
|
|
if (IsDecimalDigit(**ch1)) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
|
|
|
while (std::optional<const char *> p{state.PeekAtNextChar()}) {
|
2018-04-03 01:33:10 +02:00
|
|
|
if (!IsDecimalDigit(**p)) {
|
|
|
|
break;
|
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
2018-04-03 01:33:10 +02:00
|
|
|
}
|
|
|
|
return {Success{}};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
} skipDigitString;
|
|
|
|
|
2018-05-16 01:37:47 +02:00
|
|
|
// R707 signed-int-literal-constant -> [sign] int-literal-constant
|
|
|
|
// N.B. Spaces are consumed before and after the sign, since the sign
|
|
|
|
// and the int-literal-constant are distinct tokens. Does not
|
|
|
|
// handle a trailing kind parameter.
|
2018-05-16 01:40:22 +02:00
|
|
|
static std::optional<std::int64_t> SignedInteger(
|
2018-05-16 01:37:47 +02:00
|
|
|
const std::optional<std::uint64_t> &x, Location at, bool negate,
|
|
|
|
ParseState &state) {
|
2018-05-16 01:40:22 +02:00
|
|
|
std::optional<std::int64_t> result;
|
2018-05-16 01:37:47 +02:00
|
|
|
if (!x.has_value()) {
|
2018-05-16 01:40:22 +02:00
|
|
|
return result;
|
2018-05-16 01:37:47 +02:00
|
|
|
}
|
|
|
|
std::uint64_t limit{std::numeric_limits<std::int64_t>::max()};
|
|
|
|
if (negate) {
|
|
|
|
limit = -(limit + 1);
|
|
|
|
}
|
|
|
|
if (*x > limit) {
|
|
|
|
state.Say(at, "overflow in signed decimal literal"_err_en_US);
|
|
|
|
}
|
|
|
|
std::int64_t value = *x;
|
2018-05-16 01:40:22 +02:00
|
|
|
result = negate ? -value : value;
|
|
|
|
return result;
|
2018-05-16 01:37:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct SignedIntLiteralConstantWithoutKind {
|
|
|
|
using resultType = std::int64_t;
|
|
|
|
static std::optional<std::int64_t> Parse(ParseState &state) {
|
|
|
|
Location at{state.GetLocation()};
|
|
|
|
static constexpr auto minus = attempt("-"_tok);
|
|
|
|
static constexpr auto plus = maybe("+"_tok);
|
|
|
|
bool negate{false};
|
|
|
|
if (minus.Parse(state)) {
|
|
|
|
negate = true;
|
|
|
|
} else if (!plus.Parse(state)) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return SignedInteger(digitString.Parse(state), at, negate, state);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-05-03 00:16:53 +02:00
|
|
|
// R710 signed-digit-string -> [sign] digit-string
|
|
|
|
// N.B. Not a complete token -- no space is skipped.
|
2018-05-16 01:37:47 +02:00
|
|
|
// Used only in the exponent parts of real literal constants.
|
|
|
|
struct SignedDigitString {
|
2018-05-03 00:16:53 +02:00
|
|
|
using resultType = std::int64_t;
|
|
|
|
static std::optional<std::int64_t> Parse(ParseState &state) {
|
|
|
|
std::optional<const char *> sign{state.PeekAtNextChar()};
|
|
|
|
if (!sign.has_value()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
bool negate{**sign == '-'};
|
|
|
|
if (negate || **sign == '+') {
|
|
|
|
state.UncheckedAdvance();
|
|
|
|
}
|
2018-05-16 01:37:47 +02:00
|
|
|
return SignedInteger(digitString.Parse(state), *sign, negate, state);
|
2018-05-03 00:16:53 +02:00
|
|
|
}
|
2018-05-16 01:37:47 +02:00
|
|
|
};
|
2018-05-03 00:16:53 +02:00
|
|
|
|
|
|
|
// Variants of the above for use in FORMAT specifications, where spaces
|
|
|
|
// must be ignored.
|
|
|
|
struct DigitStringIgnoreSpaces {
|
|
|
|
using resultType = std::uint64_t;
|
|
|
|
static std::optional<std::uint64_t> Parse(ParseState &state) {
|
|
|
|
static constexpr auto getFirstDigit = space >> digit;
|
|
|
|
std::optional<const char *> firstDigit{getFirstDigit.Parse(state)};
|
|
|
|
if (!firstDigit.has_value()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
std::uint64_t value = **firstDigit - '0';
|
|
|
|
bool overflow{false};
|
|
|
|
static constexpr auto getDigit = space >> attempt(digit);
|
|
|
|
while (auto nextDigit{getDigit.Parse(state)}) {
|
|
|
|
if (value > std::numeric_limits<std::uint64_t>::max() / 10) {
|
|
|
|
overflow = true;
|
|
|
|
}
|
|
|
|
value *= 10;
|
|
|
|
int digitValue = **nextDigit - '0';
|
|
|
|
if (value > std::numeric_limits<std::uint64_t>::max() - digitValue) {
|
|
|
|
overflow = true;
|
|
|
|
}
|
|
|
|
value += digitValue;
|
|
|
|
}
|
|
|
|
if (overflow) {
|
|
|
|
state.Say(*firstDigit, "overflow in decimal literal"_err_en_US);
|
|
|
|
}
|
|
|
|
return {value};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PositiveDigitStringIgnoreSpaces {
|
2018-04-03 23:14:39 +02:00
|
|
|
using resultType = std::int64_t;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<std::int64_t> Parse(ParseState &state) {
|
|
|
|
Location at{state.GetLocation()};
|
2018-05-16 01:37:47 +02:00
|
|
|
return SignedInteger(
|
|
|
|
DigitStringIgnoreSpaces{}.Parse(state), at, false /*positive*/, state);
|
2018-04-03 23:14:39 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-05-03 00:16:53 +02:00
|
|
|
struct SignedDigitStringIgnoreSpaces {
|
2018-04-03 23:14:39 +02:00
|
|
|
using resultType = std::int64_t;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<std::int64_t> Parse(ParseState &state) {
|
2018-05-03 00:16:53 +02:00
|
|
|
static constexpr auto getSign = space >> attempt("+-"_ch);
|
|
|
|
bool negate{false};
|
|
|
|
if (std::optional<const char *> sign{getSign.Parse(state)}) {
|
|
|
|
negate = **sign == '-';
|
2018-04-03 23:14:39 +02:00
|
|
|
}
|
2018-05-03 00:16:53 +02:00
|
|
|
Location at{state.GetLocation()};
|
2018-05-16 01:37:47 +02:00
|
|
|
return SignedInteger(
|
|
|
|
DigitStringIgnoreSpaces{}.Parse(state), at, negate, state);
|
2018-04-03 23:14:39 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-01-30 20:50:36 +01:00
|
|
|
// Legacy feature: Hollerith literal constants
|
|
|
|
struct HollerithLiteral {
|
|
|
|
using resultType = std::string;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<std::string> Parse(ParseState &state) {
|
2018-03-30 01:06:31 +02:00
|
|
|
space.Parse(state);
|
2018-04-20 20:23:51 +02:00
|
|
|
const char *start{state.GetLocation()};
|
2018-05-03 00:16:53 +02:00
|
|
|
std::optional<std::uint64_t> charCount{
|
|
|
|
DigitStringIgnoreSpaces{}.Parse(state)};
|
2018-01-30 20:50:36 +01:00
|
|
|
if (!charCount || *charCount < 1) {
|
|
|
|
return {};
|
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
static constexpr auto letterH = "h"_ch;
|
2018-04-18 20:26:17 +02:00
|
|
|
std::optional<const char *> h{letterH.Parse(state)};
|
|
|
|
if (!h.has_value()) {
|
2018-01-30 20:50:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
std::string content;
|
2018-02-05 23:29:26 +01:00
|
|
|
for (auto j = *charCount; j-- > 0;) {
|
2018-03-01 01:56:10 +01:00
|
|
|
int bytes{1};
|
2018-04-20 20:23:51 +02:00
|
|
|
const char *p{state.GetLocation()};
|
|
|
|
if (state.encoding() == Encoding::EUC_JP) {
|
2018-03-14 01:20:40 +01:00
|
|
|
std::optional<int> chBytes{EUC_JPCharacterBytes(p)};
|
|
|
|
if (!chBytes.has_value()) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(start, "bad EUC_JP characters in Hollerith"_err_en_US);
|
2018-03-01 01:56:10 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-14 01:20:40 +01:00
|
|
|
bytes = *chBytes;
|
2018-04-20 20:23:51 +02:00
|
|
|
} else if (state.encoding() == Encoding::UTF8) {
|
2018-03-14 01:20:40 +01:00
|
|
|
std::optional<int> chBytes{UTF8CharacterBytes(p)};
|
|
|
|
if (!chBytes.has_value()) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(start, "bad UTF-8 characters in Hollerith"_err_en_US);
|
2018-03-01 01:56:10 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-14 01:20:40 +01:00
|
|
|
bytes = *chBytes;
|
2018-03-01 01:56:10 +01:00
|
|
|
}
|
|
|
|
if (bytes == 1) {
|
2018-03-20 18:59:07 +01:00
|
|
|
std::optional<const char *> at{nextCh.Parse(state)};
|
|
|
|
if (!at.has_value() || !isprint(**at)) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.Say(
|
2018-04-03 00:51:04 +02:00
|
|
|
start, "insufficient or bad characters in Hollerith"_err_en_US);
|
2018-03-01 01:56:10 +01:00
|
|
|
return {};
|
|
|
|
}
|
2018-03-20 18:59:07 +01:00
|
|
|
content += **at;
|
2018-03-01 01:56:10 +01:00
|
|
|
} else {
|
|
|
|
// Multi-byte character
|
|
|
|
while (bytes-- > 0) {
|
2018-03-20 18:59:07 +01:00
|
|
|
std::optional<const char *> byte{nextCh.Parse(state)};
|
2018-03-01 01:56:10 +01:00
|
|
|
CHECK(byte.has_value());
|
2018-03-20 18:59:07 +01:00
|
|
|
content += **byte;
|
2018-03-01 01:56:10 +01:00
|
|
|
}
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return {content};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-21 00:19:20 +02:00
|
|
|
constexpr struct ConsumedAllInputParser {
|
2018-02-16 19:41:16 +01:00
|
|
|
using resultType = Success;
|
|
|
|
constexpr ConsumedAllInputParser() {}
|
2018-04-21 00:19:20 +02:00
|
|
|
static inline std::optional<Success> Parse(ParseState &state) {
|
2018-04-20 20:23:51 +02:00
|
|
|
if (state.IsAtEnd()) {
|
2018-02-16 19:41:16 +01:00
|
|
|
return {Success{}};
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
} consumedAllInput;
|
|
|
|
|
2018-02-16 19:58:17 +01:00
|
|
|
template<char goal> struct SkipPast {
|
2018-02-16 19:41:16 +01:00
|
|
|
using resultType = Success;
|
|
|
|
constexpr SkipPast() {}
|
|
|
|
constexpr SkipPast(const SkipPast &) {}
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Success> Parse(ParseState &state) {
|
|
|
|
while (std::optional<const char *> p{state.GetNextChar()}) {
|
2018-04-03 01:33:10 +02:00
|
|
|
if (**p == goal) {
|
2018-02-16 19:41:16 +01:00
|
|
|
return {Success{}};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-01 20:28:13 +01:00
|
|
|
template<char goal> struct SkipTo {
|
|
|
|
using resultType = Success;
|
|
|
|
constexpr SkipTo() {}
|
|
|
|
constexpr SkipTo(const SkipTo &) {}
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Success> Parse(ParseState &state) {
|
|
|
|
while (std::optional<const char *> p{state.PeekAtNextChar()}) {
|
2018-04-03 01:33:10 +02:00
|
|
|
if (**p == goal) {
|
2018-03-01 20:28:13 +01:00
|
|
|
return {Success{}};
|
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
2018-03-01 20:28:13 +01:00
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-01-30 20:50:36 +01:00
|
|
|
// A common idiom in the Fortran grammar is an optional item (usually
|
|
|
|
// a nonempty comma-separated list) that, if present, must follow a comma
|
|
|
|
// and precede a doubled colon. When the item is absent, the comma must
|
|
|
|
// not appear, and the doubled colons are optional.
|
|
|
|
// [[, xyz] ::] is optionalBeforeColons(xyz)
|
|
|
|
// [[, xyz]... ::] is optionalBeforeColons(nonemptyList(xyz))
|
2018-02-05 23:29:26 +01:00
|
|
|
template<typename PA> inline constexpr auto optionalBeforeColons(const PA &p) {
|
2018-04-24 01:53:16 +02:00
|
|
|
return "," >> construct<std::optional<typename PA::resultType>>(p) / "::" ||
|
2018-03-14 23:31:16 +01:00
|
|
|
("::"_tok || !","_tok) >> defaulted(cut >> maybe(p));
|
|
|
|
}
|
|
|
|
template<typename PA>
|
|
|
|
inline constexpr auto optionalListBeforeColons(const PA &p) {
|
|
|
|
return "," >> nonemptyList(p) / "::" ||
|
|
|
|
("::"_tok || !","_tok) >> defaulted(cut >> nonemptyList(p));
|
2018-01-30 20:50:36 +01:00
|
|
|
}
|
2018-03-23 23:14:52 +01:00
|
|
|
|
|
|
|
// Compiler directives can switch the parser between fixed and free form.
|
|
|
|
constexpr struct FormDirectivesAndEmptyLines {
|
|
|
|
using resultType = Success;
|
2018-04-20 20:23:51 +02:00
|
|
|
static std::optional<Success> Parse(ParseState &state) {
|
|
|
|
if (UserState * ustate{state.userState()}) {
|
2018-04-20 02:02:12 +02:00
|
|
|
if (ParsingLog * log{ustate->log()}) {
|
|
|
|
if (!ustate->instrumentedParse()) {
|
|
|
|
// Save memory; zap the parsing log before each statement, unless
|
|
|
|
// we're logging the whole parse for debugging.
|
|
|
|
log->clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-20 20:23:51 +02:00
|
|
|
while (std::optional<const char *> at{state.PeekAtNextChar()}) {
|
2018-04-03 01:44:34 +02:00
|
|
|
if (**at == '\n') {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.UncheckedAdvance();
|
2018-04-10 00:08:38 +02:00
|
|
|
} else if (**at == '!') {
|
|
|
|
static const char fixed[] = "!dir$ fixed\n", free[] = "!dir$ free\n";
|
|
|
|
static constexpr std::size_t fixedBytes{sizeof fixed - 1};
|
|
|
|
static constexpr std::size_t freeBytes{sizeof free - 1};
|
2018-04-20 20:23:51 +02:00
|
|
|
std::size_t remain{state.BytesRemaining()};
|
2018-04-10 00:08:38 +02:00
|
|
|
if (remain >= fixedBytes && std::memcmp(*at, fixed, fixedBytes) == 0) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.set_inFixedForm(true).UncheckedAdvance(fixedBytes);
|
2018-04-10 00:08:38 +02:00
|
|
|
} else if (remain >= freeBytes &&
|
|
|
|
std::memcmp(*at, free, freeBytes) == 0) {
|
2018-04-20 20:23:51 +02:00
|
|
|
state.set_inFixedForm(false).UncheckedAdvance(freeBytes);
|
2018-04-10 00:08:38 +02:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2018-03-23 23:14:52 +01:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {Success{}};
|
|
|
|
}
|
|
|
|
} skipEmptyLines;
|
2018-04-21 00:19:20 +02:00
|
|
|
|
|
|
|
// R602 underscore -> _
|
|
|
|
constexpr auto underscore = "_"_ch;
|
|
|
|
|
|
|
|
// R516 keyword -> name
|
|
|
|
// R601 alphanumeric-character -> letter | digit | underscore
|
|
|
|
// R603 name -> letter [alphanumeric-character]...
|
|
|
|
// N.B. Don't accept an underscore if it is immediately followed by a
|
|
|
|
// quotation mark, so that kindParameter_"character literal" is parsed properly.
|
|
|
|
// PGI and ifort accept '$' in identifiers, even as the initial character.
|
|
|
|
// Cray and gfortran accept '$', but not as the first character.
|
|
|
|
// Cray accepts '@' as well.
|
|
|
|
constexpr auto otherIdChar = underscore / !"'\""_ch || extension("$@"_ch);
|
|
|
|
constexpr auto nonDigitIdChar = letter || otherIdChar;
|
|
|
|
constexpr auto rawName = nonDigitIdChar >> many(nonDigitIdChar || digit);
|
2018-05-01 23:59:10 +02:00
|
|
|
TYPE_PARSER(space >> sourced(rawName >> construct<Name>()))
|
2018-04-24 01:53:16 +02:00
|
|
|
constexpr auto keyword = construct<Keyword>(name);
|
2018-04-21 00:19:20 +02:00
|
|
|
|
2018-05-01 23:59:10 +02:00
|
|
|
// R1003 defined-unary-op -> . letter [letter]... .
|
|
|
|
// R1023 defined-binary-op -> . letter [letter]... .
|
|
|
|
// 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.
|
|
|
|
constexpr auto definedOpNameChar = letter || extension("$@"_ch);
|
|
|
|
TYPE_PARSER(space >> "."_ch >>
|
|
|
|
construct<DefinedOpName>(
|
|
|
|
sourced(some(definedOpNameChar) >> construct<Name>())) /
|
|
|
|
"."_ch)
|
|
|
|
|
2018-05-02 22:48:12 +02:00
|
|
|
} // namespace Fortran::parser
|
2018-02-16 20:42:17 +01:00
|
|
|
#endif // FORTRAN_PARSER_TOKEN_PARSERS_H_
|