/* file tokenizer_error.hpp * * author: Roland Conybeare, Jun 2025 */ #pragma once #include "input_state.hpp" #include "tokentype.hpp" #include "span.hpp" #include namespace xo { namespace scm { /** @class tokenizer_error * @brief represent a lexing error, with context * * @tparam CharT representation for single characters **/ template class tokenizer_error { public: using input_state_type = input_state; using span_type = span; public: /** @defgroup tokenizer-error-ctors **/ ///@{ /** Default ctor represents a not-an-error sentinel object **/ tokenizer_error() = default; /** Constructor to capture parsing error context * @p tk_start current position on entry to scanner * @p error_pos error location relative to token start **/ tokenizer_error(const char * src_function, std::string error_description, const input_state_type & input_state, size_t error_pos) : src_function_{src_function}, error_description_{std::move(error_description)}, input_state_{input_state}, error_pos_{error_pos} { scope log(XO_DEBUG(input_state.debug_flag())); log && log(xtag("input_state.current_pos", input_state.current_pos()), xtag("error_pos", error_pos)); } ///@} /** @defgroup tokenizer-error-access-methods **/ ///@{ const char * src_function() const { return src_function_; } const std::string & error_description() const { return error_description_; } #pragma GCC diagnostic push #ifndef __APPLE__ #pragma GCC diagnostic ignored "-Wchanges-meaning" #endif const input_state_type & input_state() const { return input_state_; } #pragma GCC diagnostic pop size_t tk_start() const { return input_state_.current_pos(); } size_t whitespace() const { return input_state_.whitespace(); } size_t error_pos() const { return error_pos_; } ///@} /** @defgroup tokenizer-error-general-methods **/ ///@{ /** true, except for a sentinel error object **/ bool is_error() const { return !error_description_.empty(); } /** false except for object in sentinel state **/ bool is_not_an_error() const { return error_description_.empty(); } /** Print representation to stream @p os. Intended for tokenizer diagnostics. * For Schematika errors prefer @ref report **/ void print(std::ostream & os) const; /** Print human-oriented error report on @p os. **/ void report(std::ostream & os) const; ///@} private: /** @defgroup tokenizer-error-vars **/ ///@{ /** source location (in tokenizer) at which error identified **/ char const * src_function_ = nullptr; /** static error description **/ std::string error_description_; /** input state associated with this error. * Sufficient to precisely locate it with context. **/ input_state_type input_state_; /** position (relative to @ref tk_entry_) of error **/ size_t error_pos_ = 0; ///@} }; /*error_token*/ template void tokenizer_error::print(std::ostream & os) const { os << ""; } template void tokenizer_error::report(std::ostream & os) const { using namespace std; if (!error_description_.empty()) { const char * prefix = "input: "; /* input_state.tk_start: position of first character in token * input_state.current_pos: position of first character following preceding token. * error_pos: position (relative to start) at which failure detected */ const size_t tk_start = input_state_.tk_start(); const size_t tk_indent = (strlen(prefix) + tk_start); const size_t error_pos = 1 + tk_start + error_pos_; os << "token col: " << tk_start << ", error col: " << error_pos << "\n"; os << prefix; for (const char *p = input_state_.current_line().lo(), *e = input_state_.current_line().hi(); p < e; ++p) { os << *p; } //os << endl; os << std::setw(tk_indent) << " "; for (size_t i = 0; i < error_pos_; ++i) { os << '_'; } os << '^' << endl; os << error_description_ << endl; } } template inline std::ostream & operator<< (std::ostream & os, const tokenizer_error & tkerr) { tkerr.print(os); return os; } } /*namespace scm*/ } /*namespace xo*/ /* end tokenizer_error.hpp */