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();
83 Loc loc()
const {
return {curr_.src, start_, curr_.end}; }
85 operator Loc()
const {
return loc(); }
105 auto result =
ahead();
106 curr_ = result.loc();
113 if (tag !=
ahead().tag())
return {};
119 Tok
expect(Tag tag, std::string_view ctxt) {
120 if (
ahead().tag() == tag)
return lex();
121 self().syntax_err(tag, ctxt);
126 template<
class... Args>
127 Tok
expect(Tag tag, std::format_string<Args...> fmt, Args&&... args) {
128 if (
ahead().tag() == tag)
return lex();
129 self().syntax_err(tag, std::format(fmt, std::forward<Args>(args)...));
134 Tok
eat([[maybe_unused]] Tag tag) {
135 assert(tag ==
ahead().tag() &&
"internal parser error");
148 parser_.anchors_.emplace_back(tag);
183 template<std::predicate<Tag> P>
186 self().unanchored_err(
lex(), ctxt);
190 void recover(Tag tag, std::string_view ctxt) {
191 recover([tag](Tag t) {
return t == tag; }, ctxt);
203 void syntax_err(std::string_view what, Tok tok, std::string_view ctxt) {
205 requires(S& s) { s.driver(); },
206 "provide `fe::Driver& driver()` in your parser - or a `syntax_err` of your own");
207 self().driver().error(tok.loc(),
"expected {}, got `{}` while parsing {}", what, tok, ctxt);
211 void syntax_err(std::string_view what, std::string_view ctxt) { self().syntax_err(what,
ahead(), ctxt); }
219 requires(S& s) { s.driver(); },
220 "provide `fe::Driver& driver()` in your parser - or an `unanchored_err` of your own");
221 self().driver().error(tok.loc(),
"ignoring unmatched `{}` while parsing {}", tok, ctxt);
227 if constexpr (
requires { Tok::tag2str(tag); })
228 return std::format(
"`{}`", Tok::tag2str(tag));
230 return std::format(
"`{}`", tag);
Collects diagnostics and hands each to the Diag that lays it out.
Error & error(Loc loc, std::format_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)
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 syntax_err(Tag tag, std::string_view ctxt)
As above but spells tag out via Parser::tag2str_.
void recover(P pred, std::string_view ctxt)
Parser::lex all Tokens whose Tag satisfies pred and that are not Parser::anchored; report each one as...
bool anchored(Tag tag) const
Is tag anchored by an enclosing context?
Tracker tracker(Loc begin)
std::deque< Tag > anchors_
void syntax_err(std::string_view what, std::string_view ctxt)
As above but uses Parser::ahead as tok.
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.
void unanchored_err(Tok tok, std::string_view ctxt)
Parser::recover discarded tok while parsing ctxt.
Tok eat(Tag tag)
Consume Parser::ahead which must be a tag; asserts otherwise.
Tok expect(Tag tag, std::string_view ctxt)
Parser::lex Parser::ahead() which must be a 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.
void syntax_err(std::string_view what, Tok tok, std::string_view ctxt)
Parser::expect did not find what while parsing ctxt.
Tracker tracker()
Factory method to build a Parser::Tracker.
Tok expect(Tag tag, std::format_string< Args... > fmt, Args &&... args)
As above but builds the context via std::format.
Tok ahead(size_t i=0) const
void recover(Tag tag, std::string_view ctxt)
As above but only recovers from tag.
A ring buffer with N elements.
Location within a Src: the half-open byte range [Loc::begin, Loc::end).
Byte offset into a Src; pass around as value.