49template<
class Tok,
class Tag,
size_t K,
class S>
50requires std::is_default_constructible_v<Tok>
51 && (std::is_convertible_v<Tok, bool> || std::is_constructible_v<bool, Tok>)
class Parser {
53 S& self() {
return *
static_cast<S*
>(
this); }
54 const S& self()
const {
return *
static_cast<const S*
>(
this); }
61 for (
size_t i = 0; i != K; ++i)
62 ahead_[i] = self().lexer().lex();
85 Loc loc()
const {
return {curr_.src, start_, start_ <= curr_.end ? curr_.end : start_}; }
87 operator Loc()
const {
return loc(); }
107 auto result =
ahead();
108 curr_ = result.loc();
115 if (tag !=
ahead().tag())
return {};
122 if (
ahead().tag() == tag)
return lex();
123 self().syntax_err(tag, ctxt);
128 template<
class... Args>
130 if (
ahead().tag() == tag)
return lex();
131 self().syntax_err(tag,
format_cite(fmt, std::forward<Args>(args)...));
136 Tok
eat([[maybe_unused]] Tag tag) {
137 assert(tag ==
ahead().tag() &&
"internal parser error");
150 parser_.anchors_.emplace_back(tag);
186 template<std::predicate<Tag> P>
188 auto discard = [
this, &pred] {
return pred(
ahead().tag()) && !
anchored(
ahead().tag()); };
189 if (!discard())
return;
192 auto loc = first.loc();
194 for (; discard(); ++n)
195 loc.end =
lex().loc().end;
197 self().unanchored_err(first, loc, n, ctxt);
202 recover([tag](Tag t) {
return t == tag; }, ctxt);
217 requires(S& s) { s.driver(); },
218 "provide `fe::Driver& driver()` in your parser - or a `syntax_err` of your own");
219 return error().
e(tok.loc(),
"expected {}, got `{}` while parsing {}", what, tok, ctxt);
232 requires(S& s) { s.driver(); },
233 "provide `fe::Driver& driver()` in your parser - or an `unanchored_err` of your own");
234 if (n == 1)
return error().
e(loc,
"ignoring unmatched `{}` while parsing {}", tok, ctxt);
235 return error().
e(loc,
"ignoring {} unmatched tokens starting with `{}` while parsing {}", n, tok, ctxt);
241 if constexpr (
requires { Tok::tag2str(tag); })
Collects diagnostics and hands each to the Diag that lays it out.
Error & e(Loc loc, cite_string< Args... > s, Args &&... args)
RAII helper that anchors a Tag for its lifetime; use Parser::anchor to build one.
Anchor & operator=(const Anchor &)=delete
Anchor(const Anchor &)=delete
Anchor(Parser &parser, Tag tag)
Tracker(Pos start, Loc &curr)
Loc loc() const
If nothing was consumed since this Tracker started (a total parse failure for whatever it was trackin...
The blueprint for a recursive descent/ ascent parser using a K lookahead of Tokens.
Tracker tracker(Pos begin)
As above but start tracking at begin.
decltype(auto) syntax_err(Cite what, Cite ctxt)
As above but uses Parser::ahead as tok.
void recover(P pred, Cite ctxt)
Parser::lex all Tokens whose Tag satisfies pred and that are not Parser::anchored; report the whole r...
bool anchored(Tag tag) const
Is tag anchored by an enclosing context?
Tracker tracker(Loc begin)
decltype(auto) syntax_err(Tag tag, Cite ctxt)
As above but spells tag out via Parser::tag2str_.
std::deque< Tag > anchors_
Tok lex()
Invoke Lexer to retrieve next Token.
static auto tag2str_(Tag tag)
Spells tag out via Tok::tag2str if there is one - a bare enumerator would render as its number.
Tok eat(Tag tag)
Consume Parser::ahead which must be a tag; asserts otherwise.
Tok expect(Tag tag, Cite ctxt)
Parser::lex Parser::ahead() which must be a tag.
fe::Error & syntax_err(Cite what, Tok tok, Cite ctxt)
Parser::expect did not find what while parsing ctxt.
fe::Error & unanchored_err(Tok tok, Loc loc, size_t n, Cite ctxt)
Parser::recover discarded a run of n Tokens starting with tok and spanning loc while parsing ctxt.
void recover(Tag tag, Cite ctxt)
As above but only recovers from tag.
Anchor anchor(Tag tag)
Factory method to build a Parser::Anchor; Parser::expect tag yourself at the end of the scope.
const fe::Error & error() const
Tok accept(Tag tag)
If Parser::ahead() is a tag, consume and return it, otherwise yield std::nullopt.
Tok expect(Tag tag, cite_string< Args... > fmt, Args &&... args)
As above but builds the context via fe::format_cite.
Tracker tracker()
Factory method to build a Parser::Tracker.
Tok ahead(size_t i=0) const
A ring buffer with N elements.
A borrowed fragment whose backticks stay markup; format_cite escapes every other argument.
std::format_string< detail::cite_arg_t< Args >... > cite_string
A std::format_string whose backticks delimit a `citation` while those of its arguments are data.
Cited format_cite(cite_string< Args... > fmt, Args &&... args)
<
Location within a Src: the half-open byte range [Loc::begin, Loc::end).
Byte offset into a Src; pass around as value.