22template<
class Tok,
class Tag>
23concept Token =
requires(
const Tok tok) {
25 { tok.tag() } -> std::convertible_to<Tag>;
26 { tok.loc() } -> std::convertible_to<Loc>;
27 requires std::equality_comparable<Tag>;
28 requires std::semiregular<Tok>;
29 requires std::constructible_from<bool, Tok>;
30 requires !
requires { std::bool_constant<Tok().tag() == Tag::Nil>(); } || Tok().tag() == Tag::Nil;
34template<
class S,
class Tok>
36 { s.lexer().lex() } -> std::convertible_to<Tok>;
72template<
class Tok,
class Tag,
size_t K,
class S>
75 S& self() {
return *
static_cast<S*
>(
this); }
76 const S& self()
const {
return *
static_cast<const S*
>(
this); }
82 static_assert(
Lexable<S, Tok>,
"provide `Lexer& lexer()` in your parser - its `lex` is what feeds the Parser");
83 assert(!Tok() && Tok().tag() == Tag::Nil &&
"a default-constructed Tok must be the Nil token");
85 for (
size_t i = 0; i != K; ++i)
86 ahead_[i] = self().lexer().lex();
109 Loc loc()
const {
return {curr_.src, start_, start_ <= curr_.end ? curr_.end : start_}; }
131 auto result =
ahead();
132 curr_ = result.loc();
139 if (tag !=
ahead().tag())
return {};
146 if (
ahead().tag() == tag)
return lex();
147 self().syntax_err(tag, ctxt);
152 template<
class... Args>
154 if (
ahead().tag() == tag)
return lex();
155 self().syntax_err(tag,
format_cite(fmt, std::forward<Args>(args)...));
160 Tok
eat([[maybe_unused]] Tag tag) {
161 assert(tag ==
ahead().tag() &&
"internal parser error");
180 parser_.anchors_.emplace_back(l_tok, r_tag);
213 if (r_tag == Tag::Nil)
return nullptr;
215 = std::find_if(
anchors_.rbegin(),
anchors_.rend(), [r_tag](
const Anchor& a) { return a.r_tag == r_tag; });
216 return i ==
anchors_.rend() ? nullptr : &*i;
226 template<std::predicate<Tag> P>
229 if (!discard())
return;
232 auto loc = first.loc();
234 for (; discard(); ++n)
235 loc.end =
lex().loc().end;
237 self().unanchored_err(first, loc, n, ctxt);
242 recover([tag](Tag t) {
return t == tag; }, ctxt);
277 static_assert(
Diagnosable<S>,
"provide `fe::Driver& driver()` in your parser - or a `syntax_err` of your own");
278 static_assert(
Formattable<Tok>,
"provide a `std::formatter` for your Tok - or a `syntax_err` of your own");
279 if (!got) got = ahead();
280 auto& err = error().e(got.loc(),
"expected {}, got `{}` while parsing {}", what.
what, got, ctxt);
281 if (!anchors_.empty() && anchors_.back().r_tag == what.
tag) {
282 const auto& a = anchors_.back();
283 err.n(a.l_tok.loc(),
"to match this `{}`", a.l_tok);
291 "provide `fe::Driver& driver()` in your parser - or an `unanchored_err` of your own");
292 static_assert(
Formattable<Tok>,
"provide a `std::formatter` for your Tok - or an `unanchored_err` of your own");
293 if (n == 1)
return error().
e(loc,
"ignoring stray `{}` while parsing {}", tok, ctxt);
294 return error().
e(loc,
"ignoring {} stray tokens starting with `{}` while parsing {}", n, tok, ctxt);
300 if constexpr (
requires { Tok::tag2str(tag); }) {
303 static_assert(
Formattable<Tag>,
"provide `Tok::tag2str` - or a `std::formatter` for your 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 pushes a Parser::Anchor for its lifetime; use Parser::anchor to build one.
ScopedAnchor(const ScopedAnchor &)=delete
ScopedAnchor & operator=(const ScopedAnchor &)=delete
ScopedAnchor(Parser &parser, Tok l_tok, Tag r_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.
void recover(P pred, Cite ctxt)
Parser::lex all Tokens whose Tag satisfies pred and that are not Parser::is_anchored; report the whol...
ScopedAnchor anchor(Tok l_tok, Tag r_tag)
Factory method to build a Parser::ScopedAnchor; Parser::expect r_tag yourself at the end of the scope...
Tracker tracker(Loc begin)
fe::Error & syntax_err(Expected what, Cite ctxt, Tok got={})
Parser::expect did not find what while parsing ctxt; got defaults to Parser::ahead.
Tok lex()
Invoke Lexer to retrieve next Token.
Tok l_tok
The Token that opened the context.
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 & 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.
Vector< Anchor, 8 > anchors_
const fe::Error & error() const
const Anchor * find_anchor(Tag r_tag) const
The innermost Anchor waiting for r_tag - nullptr if no enclosing context is.
bool is_anchored(Tag r_tag) const
Is r_tag anchored by an enclosing context?
Tag r_tag
The closing Tag that context is waiting for.
Tok accept(Tag tag)
If Parser::ahead() is a tag, consume and return it, otherwise yield the Nil Tok().
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
One anchor; the left end opened the context, the right one is what it is still waiting for.
A ring buffer with N elements.
This is a thin wrapper for ankerl::svector<T, N, A> which is a drop-in replacement for std::vector<T,...
A borrowed fragment whose backticks stay markup; format_cite escapes every other argument.
An owning message fragment that spells `citations` of its own; what format_cite yields.
Every frontend building block reports into the Error of S::driver.
What fe::Parser needs of its CRTP child S beyond fe::Diagnosable: the Lexer to pull Tokens from.
What fe::Parser needs of its Token type: a Tag and a Loc, copied in and out of the lookahead by value...
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).
What Parser::expect was after - a Tag, or free-form markup.
Expected(const char(&what)[N])
Cited what
Already rendered as markup - Parser::tag2str_ backticks a Tag.
Tag tag
Tag::Nil, if the expectation was not a Tag.
Byte offset into a Src; pass around as value.