pretty printing -- copmlete for xo::ast::GeneralizedExpression
This commit is contained in:
parent
7ddb79fadd
commit
c2dcc84c2e
14 changed files with 572 additions and 292 deletions
|
|
@ -4,8 +4,23 @@ Glossary
|
||||||
--------
|
--------
|
||||||
|
|
||||||
.. glossary::
|
.. glossary::
|
||||||
|
iff
|
||||||
|
| Short for "if and only if"
|
||||||
|
|
||||||
pp
|
pp
|
||||||
| Short for `pretty printer`. For example in `ppdetail`, `toppstr`.
|
| Short for "pretty printer". For example in `ppdetail`, `toppstr`.
|
||||||
|
|
||||||
|
ppii
|
||||||
|
| Short for "pretty-print indent info"
|
||||||
|
|
||||||
|
pps
|
||||||
|
| Short for "pretty-print state"
|
||||||
|
|
||||||
|
rtag
|
||||||
|
| Shorthand for "raw tag"
|
||||||
|
|
||||||
xtag
|
xtag
|
||||||
| Shorthand for `tag with preceding space`
|
| Shorthand for "tag with preceding space"
|
||||||
|
|
||||||
|
xrtag
|
||||||
|
| Shorthand for "raw tag with preceding space"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,16 @@ namespace xo {
|
||||||
**/
|
**/
|
||||||
template <typename CharT, typename Traits = std::char_traits<CharT>>
|
template <typename CharT, typename Traits = std::char_traits<CharT>>
|
||||||
class log_streambuf : public std::streambuf {
|
class log_streambuf : public std::streambuf {
|
||||||
|
public:
|
||||||
|
struct rewind_state {
|
||||||
|
explicit rewind_state(std::size_t solpos, std::size_t color_esc, std::uint32_t p)
|
||||||
|
: solpos{solpos}, color_escape_chars{color_esc}, pos{p} {}
|
||||||
|
|
||||||
|
std::size_t solpos = 0;
|
||||||
|
std::size_t color_escape_chars = 0;
|
||||||
|
std::uint32_t pos = 0;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
log_streambuf(std::uint32_t buf_z, bool debug_flag = false) : debug_flag_{debug_flag} {
|
log_streambuf(std::uint32_t buf_z, bool debug_flag = false) : debug_flag_{debug_flag} {
|
||||||
this->buf_v_.resize(buf_z);
|
this->buf_v_.resize(buf_z);
|
||||||
|
|
@ -27,8 +37,15 @@ namespace xo {
|
||||||
char const * hi() const { return this->lo() + this->capacity(); }
|
char const * hi() const { return this->lo() + this->capacity(); }
|
||||||
std::uint32_t pos() const { return this->pptr() - this->pbase(); }
|
std::uint32_t pos() const { return this->pptr() - this->pbase(); }
|
||||||
|
|
||||||
/** number of characters since start of line (last \n or \r) **/
|
/** number of visible characters since start of line (last \n or \r) **/
|
||||||
std::uint32_t lpos() const { return pos() - solpos_; }
|
std::uint32_t lpos() const {
|
||||||
|
assert(pos() >= solpos_ + color_escape_chars_);
|
||||||
|
return pos() - solpos_ - color_escape_chars_;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewind_state checkpoint() const {
|
||||||
|
return rewind_state(solpos_, color_escape_chars_, pos());
|
||||||
|
}
|
||||||
|
|
||||||
bool debug_flag() const { return debug_flag_; }
|
bool debug_flag() const { return debug_flag_; }
|
||||||
|
|
||||||
|
|
@ -40,12 +57,24 @@ namespace xo {
|
||||||
this->setp(p_lo, p_hi);
|
this->setp(p_lo, p_hi);
|
||||||
|
|
||||||
this->solpos_ = 0;
|
this->solpos_ = 0;
|
||||||
|
this->color_escape_chars_ = 0;
|
||||||
|
this->color_escape_start_ = nullptr;
|
||||||
} /*reset_stream*/
|
} /*reset_stream*/
|
||||||
|
|
||||||
void rewind_to(std::uint32_t p) {
|
void rewind_to(rewind_state s) {
|
||||||
|
if (debug_flag_) {
|
||||||
|
std::cout << "rewind_to: pos " << pos() << "->" << s.pos
|
||||||
|
<< " solpos " << solpos_ << "->" << s.solpos
|
||||||
|
<< " color_esc " << color_escape_chars_ << "->" << s.color_escape_chars
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
/* .setp(): using for side effect: sets .pptr to .pbase */
|
/* .setp(): using for side effect: sets .pptr to .pbase */
|
||||||
this->setp(this->pbase(), this->epptr());
|
this->setp(this->pbase(), this->epptr());
|
||||||
this->pbump(p);
|
this->pbump(s.pos);
|
||||||
|
|
||||||
|
this->solpos_ = s.solpos;
|
||||||
|
this->color_escape_chars_ = s.color_escape_chars;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -65,7 +94,7 @@ namespace xo {
|
||||||
|
|
||||||
this->setp(p_base, p_hi);
|
this->setp(p_base, p_hi);
|
||||||
this->pbump(old_n);
|
this->pbump(old_n);
|
||||||
} /*expand*/
|
}
|
||||||
|
|
||||||
virtual std::streamsize
|
virtual std::streamsize
|
||||||
xsputn(char const * s, std::streamsize n) override {
|
xsputn(char const * s, std::streamsize n) override {
|
||||||
|
|
@ -94,7 +123,7 @@ namespace xo {
|
||||||
ncopied = n;
|
ncopied = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_flag_) {
|
if (false /*debug_flag_*/) {
|
||||||
std::cout << "xsputn: copying ncopied=" << ncopied << " (/n=" << n << ") bytes into range [lo,hi)"
|
std::cout << "xsputn: copying ncopied=" << ncopied << " (/n=" << n << ") bytes into range [lo,hi)"
|
||||||
<< ", lo=" << (void*)this->pptr()
|
<< ", lo=" << (void*)this->pptr()
|
||||||
<< ", hi=" << (void*)(this->pptr() + n)
|
<< ", hi=" << (void*)(this->pptr() + n)
|
||||||
|
|
@ -103,11 +132,39 @@ namespace xo {
|
||||||
|
|
||||||
std::memcpy(this->pptr(), s, ncopied);
|
std::memcpy(this->pptr(), s, ncopied);
|
||||||
|
|
||||||
/* scan range [pptr, pptr+n] backwards, to account for newline (if any) */
|
/* scan range [pptr, pptr+n] for:
|
||||||
for (char const * p_lo = this->pptr(), * p_hi = p_lo + n - 1, * p = p_hi; p >= p_lo; --p) {
|
* 1. completed color escape sequences \033..m
|
||||||
|
* - account for chars in these sequences, since non-printing
|
||||||
|
* 2. newlines (and carriage returns)
|
||||||
|
* - remember position of last {newline or carriage return)
|
||||||
|
*/
|
||||||
|
for (char const * p_lo = this->pptr(), * p_hi = p_lo + n, * p = p_lo; p < p_hi; ++p) {
|
||||||
if (*p == '\n' || *p == '\r') {
|
if (*p == '\n' || *p == '\r') {
|
||||||
this->solpos_ = (p+1 - this->pbase());
|
this->solpos_ = (p+1 - this->pbase());
|
||||||
break;
|
/* reset, since these chars relevant as correction to solpos */
|
||||||
|
this->color_escape_chars_ = 0;
|
||||||
|
/* -> incomplete color escape, broken by newline */
|
||||||
|
this->color_escape_start_ = nullptr;
|
||||||
|
} else if (*p == '\033') {
|
||||||
|
if (debug_flag_) {
|
||||||
|
std::cout << "xsputn: \\033 at p-p_lo=" << (p - p_lo) << std::endl;
|
||||||
|
}
|
||||||
|
this->color_escape_start_ = p;
|
||||||
|
} else if (this->color_escape_start_ != nullptr) {
|
||||||
|
if (*p == 'm') {
|
||||||
|
/* escape seq non-printing including both endpoints */
|
||||||
|
std::int64_t esc_chars = (p+1 - color_escape_start_);
|
||||||
|
this->color_escape_chars_ += esc_chars;
|
||||||
|
|
||||||
|
if (debug_flag_) {
|
||||||
|
std::cout << "xsputn: m at p-p_lo" << (p - p_lo) << " +" << esc_chars
|
||||||
|
<< " -> color_escape_chars=" << color_escape_chars_ << std::endl;
|
||||||
|
}
|
||||||
|
this->color_escape_start_ = nullptr;
|
||||||
|
} else if (!isdigit(*p) && (*p != '[') && (*p != ';')) {
|
||||||
|
/* not color escape after all */
|
||||||
|
this->color_escape_start_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,32 +174,32 @@ namespace xo {
|
||||||
} /*xsputn*/
|
} /*xsputn*/
|
||||||
|
|
||||||
virtual int_type
|
virtual int_type
|
||||||
overflow(int_type new_ch) override
|
overflow(int_type new_ch) override {
|
||||||
{
|
char * old_pptr = this->pptr();
|
||||||
char * old_pptr = this->pptr();
|
std::streamsize old_n = old_pptr - this->pbase();
|
||||||
std::streamsize old_n = old_pptr - this->pbase();
|
|
||||||
|
|
||||||
assert(old_n <= static_cast<std::streamsize>(this->buf_v_.size()));
|
assert(old_n <= static_cast<std::streamsize>(this->buf_v_.size()));
|
||||||
|
|
||||||
if (debug_flag_) {
|
if (debug_flag_) {
|
||||||
std::cout << "overflow: new_ch=" << quoted_char(new_ch) << std::endl;
|
std::cout << "overflow: new_ch=" << quoted_char(new_ch) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->expand_to(2 * buf_v_.size());
|
this->expand_to(2 * buf_v_.size());
|
||||||
|
|
||||||
this->buf_v_[old_n] = new_ch;
|
this->buf_v_[old_n] = new_ch;
|
||||||
this->pbump(1);
|
this->pbump(1);
|
||||||
|
|
||||||
if ((new_ch == static_cast<int_type>('\n')) || (new_ch == static_cast<int_type>('\r')))
|
if ((new_ch == static_cast<int_type>('\n')) || (new_ch == static_cast<int_type>('\r'))) {
|
||||||
this->solpos_ = this->pos();
|
this->solpos_ = this->pos();
|
||||||
|
}
|
||||||
|
|
||||||
if (new_ch == Traits::eof()) {
|
if (new_ch == Traits::eof()) {
|
||||||
/* reminder: returning eof sets badbit on ostream */
|
/* reminder: returning eof sets badbit on ostream */
|
||||||
return Traits::not_eof(new_ch);
|
return Traits::not_eof(new_ch);
|
||||||
} else {
|
} else {
|
||||||
return new_ch;
|
return new_ch;
|
||||||
}
|
}
|
||||||
} /*overflow*/
|
} /*overflow*/
|
||||||
|
|
||||||
/* off. offset, relative to starting point dir.
|
/* off. offset, relative to starting point dir.
|
||||||
* dir.
|
* dir.
|
||||||
|
|
@ -154,6 +211,9 @@ namespace xo {
|
||||||
std::ios_base::seekdir dir,
|
std::ios_base::seekdir dir,
|
||||||
std::ios_base::openmode which) override {
|
std::ios_base::openmode which) override {
|
||||||
//std::cout << "seekoff: off=" << off << ", dir=" << dir << ", which=" << which << std::endl;
|
//std::cout << "seekoff: off=" << off << ", dir=" << dir << ", which=" << which << std::endl;
|
||||||
|
if (debug_flag_) {
|
||||||
|
std::cout << "seekoff(off,dir,which)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// Only output stream is supported
|
// Only output stream is supported
|
||||||
if (which != std::ios_base::out)
|
if (which != std::ios_base::out)
|
||||||
|
|
@ -179,6 +239,13 @@ namespace xo {
|
||||||
* Use to drive @ref lpos
|
* Use to drive @ref lpos
|
||||||
**/
|
**/
|
||||||
std::size_t solpos_ = 0;
|
std::size_t solpos_ = 0;
|
||||||
|
/** number of non-printing chars after @ref solpos_, from
|
||||||
|
* completed color escape sequences.
|
||||||
|
* (ansi color escapes = text between '\033' and 'm')
|
||||||
|
**/
|
||||||
|
std::size_t color_escape_chars_ = 0;
|
||||||
|
/** non-null: start of incomplete color escape sequence **/
|
||||||
|
char const * color_escape_start_ = nullptr;
|
||||||
/** buffered output stored here **/
|
/** buffered output stored here **/
|
||||||
std::vector<char> buf_v_;
|
std::vector<char> buf_v_;
|
||||||
/** true to debug log_streambuf itself **/
|
/** true to debug log_streambuf itself **/
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ppdetail_atomic.hpp"
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
//#include <utility> // for std::move
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace xo {
|
namespace xo {
|
||||||
|
|
@ -17,11 +17,11 @@ namespace xo {
|
||||||
inline std::ostream &
|
inline std::ostream &
|
||||||
operator<< (std::ostream & os, color_encoding x) {
|
operator<< (std::ostream & os, color_encoding x) {
|
||||||
switch(x) {
|
switch(x) {
|
||||||
case color_encoding::none: os << "none"; break;
|
case color_encoding::none: os << "none"; break;
|
||||||
case color_encoding::ansi: os << "ansi"; break;
|
case color_encoding::ansi: os << "ansi"; break;
|
||||||
case color_encoding::xterm: os << "xterm"; break;
|
case color_encoding::xterm: os << "xterm"; break;
|
||||||
case color_encoding::rgb: os << "rgb"; break;
|
case color_encoding::rgb: os << "rgb"; break;
|
||||||
default: os << "???"; break;
|
default: os << "???"; break;
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
} /*operator<<*/
|
} /*operator<<*/
|
||||||
|
|
@ -125,11 +125,11 @@ namespace xo {
|
||||||
} /*operator<<*/
|
} /*operator<<*/
|
||||||
|
|
||||||
enum class coloring_control_flags : std::uint8_t {
|
enum class coloring_control_flags : std::uint8_t {
|
||||||
none = 0x0,
|
none = 0x0,
|
||||||
color_on = 0x01,
|
color_on = 0x01,
|
||||||
contents = 0x02,
|
contents = 0x02,
|
||||||
color_off = 0x04,
|
color_off = 0x04,
|
||||||
all = 0x07
|
all = 0x07
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::uint8_t operator& (coloring_control_flags x, coloring_control_flags y) {
|
inline std::uint8_t operator& (coloring_control_flags x, coloring_control_flags y) {
|
||||||
|
|
@ -177,25 +177,42 @@ namespace xo {
|
||||||
template <typename Contents>
|
template <typename Contents>
|
||||||
color_impl<Contents> with_color(color_spec_type const & spec, Contents && contents) {
|
color_impl<Contents> with_color(color_spec_type const & spec, Contents && contents) {
|
||||||
return color_impl<Contents>(coloring_control_flags::all, spec, std::forward<Contents>(contents));
|
return color_impl<Contents>(coloring_control_flags::all, spec, std::forward<Contents>(contents));
|
||||||
} /*with_color*/
|
}
|
||||||
|
|
||||||
inline color_impl<int>
|
inline color_impl<int>
|
||||||
color_on(color_spec_type const & spec) {
|
color_on(color_spec_type const & spec) {
|
||||||
return color_impl<int>(coloring_control_flags::color_on, spec, 0);
|
return color_impl<int>(coloring_control_flags::color_on, spec, 0);
|
||||||
} /*color_on*/
|
}
|
||||||
|
|
||||||
inline color_impl<int>
|
inline color_impl<int>
|
||||||
color_off(color_spec_type const & spec) {
|
color_off(color_spec_type const & spec) {
|
||||||
/* any spec other than color_spec_type::none() works here */
|
/* any spec other than color_spec_type::none() works here */
|
||||||
return color_impl<int>(coloring_control_flags::color_off, spec, 0);
|
return color_impl<int>(coloring_control_flags::color_off, spec, 0);
|
||||||
} /*color_off*/
|
}
|
||||||
|
|
||||||
template <typename Contents>
|
template <typename Contents>
|
||||||
inline std::ostream &
|
inline std::ostream &
|
||||||
operator<<(std::ostream & os, color_impl<Contents> const & x) {
|
operator<<(std::ostream & os, color_impl<Contents> const & x) {
|
||||||
x.print(os);
|
x.print(os);
|
||||||
return os;
|
return os;
|
||||||
} /*operator<<*/
|
}
|
||||||
|
|
||||||
|
#ifndef ppdetail_atomic
|
||||||
|
namespace print {
|
||||||
|
/* concat expected to be used on short string-like things.
|
||||||
|
* i.e. don't want structure visible to pretty-printer.
|
||||||
|
* could be using it like concat("boeing", 777)
|
||||||
|
*/
|
||||||
|
template <typename Contents>
|
||||||
|
struct ppdetail<color_impl<Contents>> {
|
||||||
|
using target_type = color_impl<Contents>;
|
||||||
|
|
||||||
|
static bool print_pretty(const ppindentinfo & ppii, const target_type & x) {
|
||||||
|
return ppdetail_atomic<target_type>::print_pretty(ppii, x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} /*namespace xo*/
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,8 @@ namespace xo {
|
||||||
struct ppdetail<concat_impl<T1,T2>> {
|
struct ppdetail<concat_impl<T1,T2>> {
|
||||||
using target_type = concat_impl<T1,T2>;
|
using target_type = concat_impl<T1,T2>;
|
||||||
|
|
||||||
static bool print_upto(ppstate * pps, const target_type & x) {
|
static bool print_pretty(const ppindentinfo & ppii, const target_type & x) {
|
||||||
return ppdetail_atomic<target_type>::print_upto(pps, x);
|
return ppdetail_atomic<target_type>::print_pretty(ppii, x);
|
||||||
}
|
|
||||||
static void print_pretty(ppstate * pps, const target_type & x) {
|
|
||||||
ppdetail_atomic<target_type>::print_pretty(pps, x);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ namespace xo {
|
||||||
* Pretty-printer will introduce newlines if needed
|
* Pretty-printer will introduce newlines if needed
|
||||||
* to stay to the left of this margin
|
* to stay to the left of this margin
|
||||||
**/
|
**/
|
||||||
std::uint32_t right_margin_ = 135;
|
std::uint32_t right_margin_ = 80;
|
||||||
|
|
||||||
/** amount of additional indent per nesting level **/
|
/** amount of additional indent per nesting level **/
|
||||||
std::uint32_t indent_width_ = 2;
|
std::uint32_t indent_width_ = 2;
|
||||||
|
|
||||||
|
/** assert if attempting this much indent **/
|
||||||
|
std::uint32_t assert_indent_threshold = 10000;
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
};
|
};
|
||||||
} /*namespace print*/
|
} /*namespace print*/
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,34 @@
|
||||||
namespace xo {
|
namespace xo {
|
||||||
namespace print {
|
namespace print {
|
||||||
struct ppstate; // see pretty.hpp
|
struct ppstate; // see pretty.hpp
|
||||||
|
struct ppindentinfo;
|
||||||
|
|
||||||
// Defining this means ppdetail_atomic is not used.
|
// Defining this means ppdetail_atomic is not used.
|
||||||
// In that case where not explicitly specialized ppdetail will revert to ordinary printing for a type,
|
// In that case where not explicitly specialized ppdetail will revert to ordinary printing for a type,
|
||||||
// instead of giving compile-time error about missing template specialization of ppdetail
|
// instead of giving compile-time error about missing template specialization of ppdetail.
|
||||||
|
|
||||||
#define ppdetail_atomic ppdetail
|
#define ppdetail_atomic ppdetail
|
||||||
|
|
||||||
|
struct ppindentinfo {
|
||||||
|
ppindentinfo(ppstate * pps, std::uint32_t ci0, std::uint32_t indent_width, bool upto)
|
||||||
|
: pps_{pps}, ci0_{ci0}, ci1_{ci0 + indent_width}, upto_{upto} {}
|
||||||
|
|
||||||
|
ppstate * pps() const { return pps_; }
|
||||||
|
std::uint32_t ci1() const { return ci1_; }
|
||||||
|
bool upto() const { return upto_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ppstate * pps_ = nullptr;
|
||||||
|
/** current indent **/
|
||||||
|
std::uint32_t ci0_ = 0;
|
||||||
|
/** ci0 +1 indent level **/
|
||||||
|
std::uint32_t ci1_ = 0;
|
||||||
|
/**
|
||||||
|
* true -> print on remainder of current line, unless past right margin
|
||||||
|
* false -> pretty across across multiple lines
|
||||||
|
**/
|
||||||
|
bool upto_;
|
||||||
|
};
|
||||||
|
|
||||||
/** @class ppdetail
|
/** @class ppdetail
|
||||||
* @brief template for opt-in to pretty-printer
|
* @brief template for opt-in to pretty-printer
|
||||||
*
|
*
|
||||||
|
|
@ -32,88 +53,76 @@ namespace xo {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct ppdetail;
|
struct ppdetail;
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct ppdetail_atomic;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct ppdetail_atomic {
|
struct ppdetail_atomic {
|
||||||
/** 1. print @p x to private stream @ref scratch_ss_
|
/** upto=true:
|
||||||
|
* 1. print @p x to private stream @ref scratch_ss_
|
||||||
* 2. return true iff N = number of characters printed satisfies N <= @p budget.
|
* 2. return true iff N = number of characters printed satisfies N <= @p budget.
|
||||||
* content actually printed to *sbuf may be used as-is in this case
|
* content actually printed to *sbuf may be used as-is in this case
|
||||||
* 3. return false otherwise. Will trigger non-degenerate pretty-printing.
|
* 3. return false otherwise. Will trigger non-degenerate pretty-printing.
|
||||||
* 4. in any case consume some of @ref scratch_sbuf_
|
* 4. in any case consume some of @ref scratch_sbuf_
|
||||||
|
*
|
||||||
|
* upto=false:
|
||||||
|
* print @p x to private stream @ref scratch_ss_
|
||||||
**/
|
**/
|
||||||
static bool print_upto(ppstate * pps, const T & x);
|
static bool print_pretty(const ppindentinfo & ppii, const T & x);
|
||||||
/** print @p x to private stream @ref scratch_ss_ **/
|
|
||||||
static void print_pretty(ppstate * pps, const T & x);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef ppdetail_atomic
|
#ifndef ppdetail_atomic
|
||||||
template <int N>
|
template <int N>
|
||||||
struct ppdetail<const char[N]> {
|
struct ppdetail<const char[N]> {
|
||||||
using target_type = const char[N];
|
using target_type = const char[N];
|
||||||
|
static bool print_pretty(const ppindentinfo & ppii, const target_type & x) {
|
||||||
static bool print_upto(ppstate * pps, const target_type & x) {
|
return ppdetail_atomic<target_type>::print_pretty(ppii, x);
|
||||||
return ppdetail_atomic<target_type>::print_upto(pps, x);
|
|
||||||
}
|
|
||||||
static void print_pretty(ppstate * pps, const target_type & x) {
|
|
||||||
ppdetail_atomic<target_type>::print_pretty(pps, x);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct ppdetail<const char *> {
|
struct ppdetail<const char *> {
|
||||||
static bool print_upto(ppstate * pps, const char * x) {
|
static bool print_pretty(const ppindentinfo & ppii, const char * x) {
|
||||||
return ppdetail_atomic<const char *>::print_upto(pps, x);
|
return ppdetail_atomic<const char *>::print_pretty(ppii, x);
|
||||||
}
|
|
||||||
static void print_pretty(ppstate * pps, const char * x) {
|
|
||||||
ppdetail_atomic<const char *>::print_pretty(pps, x);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#define PPDETAIL_ATOMIC_BODY(target_type) \
|
#define PPDETAIL_ATOMIC_BODY(target_type) \
|
||||||
struct ppdetail<target_type> { \
|
struct ppdetail<target_type> { \
|
||||||
static bool print_upto(ppstate * pps, const target_type & x) { \
|
static bool print_pretty(const ppindentinfo & ppii, \
|
||||||
return ppdetail_atomic<target_type>::print_upto(pps, x); \
|
const target_type & x) { \
|
||||||
} \
|
return ppdetail_atomic<target_type>::print_pretty(ppii, x); \
|
||||||
\
|
} \
|
||||||
static void print_pretty(ppstate * pps, const target_type & x) { \
|
|
||||||
ppdetail_atomic<target_type>::print_pretty(pps, x); \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PPDETAIL_ATOMIC_BODY_CONST(target_type) \
|
#define PPDETAIL_ATOMIC_BODY_CONST(target_type) \
|
||||||
struct ppdetail<const target_type> { \
|
struct ppdetail<const target_type> { \
|
||||||
static bool print_upto(ppstate * pps, const target_type & x) { \
|
static bool print_pretty(const ppindentinfo & ppii, \
|
||||||
return ppdetail_atomic<target_type>::print_upto(pps, x); \
|
const target_type & x) { \
|
||||||
} \
|
return ppdetail_atomic<target_type>::print_pretty(ppii, x); \
|
||||||
\
|
} \
|
||||||
static void print_pretty(ppstate * pps, const target_type & x) { \
|
|
||||||
ppdetail_atomic<target_type>::print_pretty(pps, x); \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PPDETAIL_ATOMIC(target_type) \
|
#define PPDETAIL_ATOMIC(target_type) \
|
||||||
template<> \
|
template<> \
|
||||||
PPDETAIL_ATOMIC_BODY(target_type)
|
PPDETAIL_ATOMIC_BODY(target_type)
|
||||||
|
|
||||||
#define PPDETAIL_ATOMIC_CONST(target_type) \
|
#define PPDETAIL_ATOMIC_CONST(target_type) \
|
||||||
template<> \
|
template<> \
|
||||||
PPDETAIL_ATOMIC_BODY_CONST(target_type)
|
PPDETAIL_ATOMIC_BODY_CONST(target_type)
|
||||||
|
|
||||||
PPDETAIL_ATOMIC(bool);
|
PPDETAIL_ATOMIC(bool);
|
||||||
PPDETAIL_ATOMIC(char);
|
PPDETAIL_ATOMIC(char);
|
||||||
// PPDETAIL_ATOMIC_CONST(char);
|
|
||||||
PPDETAIL_ATOMIC(std::int64_t);
|
PPDETAIL_ATOMIC(std::int64_t);
|
||||||
PPDETAIL_ATOMIC(std::uint64_t);
|
PPDETAIL_ATOMIC(std::uint64_t);
|
||||||
PPDETAIL_ATOMIC(std::int32_t);
|
PPDETAIL_ATOMIC(std::int32_t);
|
||||||
PPDETAIL_ATOMIC(std::uint32_t);
|
PPDETAIL_ATOMIC(std::uint32_t);
|
||||||
|
PPDETAIL_ATOMIC(float);
|
||||||
|
PPDETAIL_ATOMIC(double);
|
||||||
PPDETAIL_ATOMIC(std::string);
|
PPDETAIL_ATOMIC(std::string);
|
||||||
|
PPDETAIL_ATOMIC(std::string_view);
|
||||||
|
|
||||||
using voidptr = void*;
|
using voidptr = void*;
|
||||||
PPDETAIL_ATOMIC(voidptr);
|
PPDETAIL_ATOMIC(voidptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
} /*namespace print*/
|
||||||
}
|
} /*namespace xo*/
|
||||||
|
|
|
||||||
|
|
@ -94,44 +94,45 @@ namespace xo {
|
||||||
/** implement pretty-printing for template @c ppconcat r**/
|
/** implement pretty-printing for template @c ppconcat r**/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
struct ppdetail<ppconcat<Args...>> {
|
struct ppdetail<ppconcat<Args...>> {
|
||||||
/** try to print @p target on one line.
|
/** upto=true:
|
||||||
|
* try to print @p target on one line.
|
||||||
* return false if budget (space until right margin) exhausted
|
* return false if budget (space until right margin) exhausted
|
||||||
* or if an embedded newline appears
|
* or if an embedded newline appears
|
||||||
*
|
*
|
||||||
* @return true on success, otherwise false.
|
* @return true on success, otherwise false.
|
||||||
|
*
|
||||||
|
* upto=false:
|
||||||
|
* pretty-print @p target using multiple lines
|
||||||
**/
|
**/
|
||||||
static bool print_upto(ppstate * pps, ppconcat<Args...> target) {
|
static bool print_pretty(const ppindentinfo & ppii, ppconcat<Args...> target) {
|
||||||
std::uint32_t saved = pps->pos();
|
if (ppii.upto()) {
|
||||||
|
std::uint32_t saved = ppii.pps()->pos();
|
||||||
|
|
||||||
if (!pps->has_margin())
|
if (!ppii.pps()->has_margin())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (std::apply(
|
if (std::apply(
|
||||||
[pps](auto &&... args) {
|
[ppii](auto &&... args) {
|
||||||
return detail::ppconcat_printupto_aux(pps, std::forward<decltype(args)>(args)...);
|
return detail::ppconcat_printupto_aux(ppii.pps(), std::forward<decltype(args)>(args)...);
|
||||||
|
},
|
||||||
|
std::forward<std::tuple<Args...>>(target.contents_)
|
||||||
|
) == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ppii.pps()->scan_no_newline(saved);
|
||||||
|
} else {
|
||||||
|
std::apply(
|
||||||
|
[ppii](auto &&... args) {
|
||||||
|
detail::ppconcat_print_pretty_aux(ppii.pps(), ppii.ci1(),
|
||||||
|
std::forward<decltype(args)>(args)...);
|
||||||
},
|
},
|
||||||
std::forward<std::tuple<Args...>>(target.contents_)
|
std::forward<std::tuple<Args...>>(target.contents_)
|
||||||
) == false)
|
);
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pps->scan_no_newline(saved);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** pretty-print @p target using multiple lines
|
|
||||||
**/
|
|
||||||
static void print_pretty(ppstate * pps, ppconcat<Args...> target) {
|
|
||||||
std::uint32_t ci0 = pps->lpos();
|
|
||||||
std::uint32_t ci1 = ci0 + pps->indent_width();
|
|
||||||
|
|
||||||
std::apply(
|
|
||||||
[pps, ci1](auto &&... args) {
|
|
||||||
detail::ppconcat_print_pretty_aux(pps, ci1,
|
|
||||||
std::forward<decltype(args)>(args)...);
|
|
||||||
},
|
|
||||||
std::forward<std::tuple<Args...>>(target.contents_)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} /*namespace print*/
|
} /*namespace print*/
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "xo/indentlog/print/ppconfig.hpp"
|
#include "xo/indentlog/print/ppconfig.hpp"
|
||||||
#include "xo/indentlog/log_streambuf.hpp"
|
#include "xo/indentlog/log_streambuf.hpp"
|
||||||
#include "ppdetail_atomic.hpp"
|
#include "ppdetail_atomic.hpp"
|
||||||
|
#include "tag.hpp"
|
||||||
#include "pad.hpp"
|
#include "pad.hpp"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -16,12 +17,10 @@
|
||||||
|
|
||||||
namespace xo {
|
namespace xo {
|
||||||
namespace print {
|
namespace print {
|
||||||
|
|
||||||
/** @class ppstate
|
/** @class ppstate
|
||||||
* @brief hold pretty-printer state
|
* @brief hold pretty-printer state
|
||||||
*
|
*
|
||||||
* Use:
|
* Use:
|
||||||
*
|
|
||||||
* ppconfig ppc;
|
* ppconfig ppc;
|
||||||
* ppstate pps(&cout, 0, &ppc);
|
* ppstate pps(&cout, 0, &ppc);
|
||||||
*
|
*
|
||||||
|
|
@ -31,14 +30,19 @@ namespace xo {
|
||||||
struct ppstate {
|
struct ppstate {
|
||||||
using streambuf_type = log_streambuf<char, std::char_traits<char>>;
|
using streambuf_type = log_streambuf<char, std::char_traits<char>>;
|
||||||
|
|
||||||
explicit ppstate(std::uint32_t ci, const ppconfig * config, std::ostream * scratch_ss, streambuf_type * scratch_sbuf)
|
explicit ppstate(std::uint32_t ci, const ppconfig * config,
|
||||||
|
std::ostream * scratch_ss, streambuf_type * scratch_sbuf)
|
||||||
: scratch_sbuf_{scratch_sbuf}, scratch_ss_{scratch_ss},
|
: scratch_sbuf_{scratch_sbuf}, scratch_ss_{scratch_ss},
|
||||||
current_indent_{ci}, config_{config}
|
current_indent_{ci}, config_{config}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
uint32_t indent_width() const { return config_->indent_width_; }
|
uint32_t indent_width() const { return config_->indent_width_; }
|
||||||
|
ppindentinfo indent_info(bool upto) { return ppindentinfo(this, lpos(), indent_width(), upto); }
|
||||||
|
|
||||||
std::uint32_t pos() const { return scratch_sbuf_->pos(); }
|
std::uint32_t pos() const { return scratch_sbuf_->pos(); }
|
||||||
|
/** current position relative to start of line (last \n or \r),
|
||||||
|
* excluding color escape sequences
|
||||||
|
**/
|
||||||
std::uint32_t lpos() const { return scratch_sbuf_->lpos(); }
|
std::uint32_t lpos() const { return scratch_sbuf_->lpos(); }
|
||||||
|
|
||||||
/** space available from current position until @c ppconfig.right_margin_
|
/** space available from current position until @c ppconfig.right_margin_
|
||||||
|
|
@ -60,8 +64,8 @@ namespace xo {
|
||||||
return avail_margin() >= budget;
|
return avail_margin() >= budget;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** true if no newlines in range [@p lo, pos),
|
/** true if no newlines in range [s, pos),
|
||||||
* where pos is current position
|
* where s is @p start and pos is current position
|
||||||
**/
|
**/
|
||||||
bool scan_no_newline(std::uint32_t start) const {
|
bool scan_no_newline(std::uint32_t start) const {
|
||||||
char const * p = scratch_sbuf_->lo() + start;
|
char const * p = scratch_sbuf_->lo() + start;
|
||||||
|
|
@ -76,10 +80,13 @@ namespace xo {
|
||||||
}
|
}
|
||||||
|
|
||||||
void indent(std::uint32_t tab) {
|
void indent(std::uint32_t tab) {
|
||||||
|
assert(tab < config_->assert_indent_threshold);
|
||||||
(*scratch_ss_) << spaces(tab);
|
(*scratch_ss_) << spaces(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** write (to scratch stream) newline followed by @p tab spaces **/
|
||||||
void newline_indent(std::uint32_t tab) {
|
void newline_indent(std::uint32_t tab) {
|
||||||
|
|
||||||
(*scratch_ss_) << "\n";
|
(*scratch_ss_) << "\n";
|
||||||
this->indent(tab);
|
this->indent(tab);
|
||||||
}
|
}
|
||||||
|
|
@ -94,12 +101,50 @@ namespace xo {
|
||||||
|
|
||||||
virtual void commit() {}
|
virtual void commit() {}
|
||||||
|
|
||||||
|
// pretty-printing helpers
|
||||||
|
|
||||||
|
/** pretty-print empty struct **/
|
||||||
|
template <typename StructName, typename... Members>
|
||||||
|
std::uint32_t pretty_struct(const ppindentinfo & ppii, StructName && structname, Members&&...);
|
||||||
|
|
||||||
|
/** auxiliary function supporting @ref pretty_stuct .
|
||||||
|
* pretty-print a single member name on behalf of a struct/class.
|
||||||
|
* @return true iff printed content fits on remainder of line before right margin
|
||||||
|
**/
|
||||||
|
template <typename Member, typename... Rest>
|
||||||
|
std::uint32_t print_upto_struct_members(const ppindentinfo & ppii,
|
||||||
|
Member && member,
|
||||||
|
Rest&&... rest);
|
||||||
|
|
||||||
|
/** base-case overload **/
|
||||||
|
std::uint32_t print_upto_struct_members(const ppindentinfo &) { return true; }
|
||||||
|
|
||||||
|
/** pretty-print newline-and-indent separated member values.
|
||||||
|
* For struct members, use refrtag(name,value)
|
||||||
|
**/
|
||||||
|
template <typename Member, typename... Rest>
|
||||||
|
void pretty_struct_members(const ppindentinfo & ppii,
|
||||||
|
Member && member,
|
||||||
|
Rest&&... rest);
|
||||||
|
|
||||||
|
/** base-case overload **/
|
||||||
|
void pretty_struct_members(const ppindentinfo &) {}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool print_upto(T && x);
|
bool print_upto(T && x);
|
||||||
|
|
||||||
|
template <typename Name, typename Value>
|
||||||
|
bool print_upto_tag(Name && name, Value && value);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ppstate& pretty(T && x);
|
ppstate& pretty(T && x);
|
||||||
|
|
||||||
|
/** write (to internal scratch stream), in order:
|
||||||
|
* newline, indent to @p ci, tag @p name, space, @p value
|
||||||
|
**/
|
||||||
|
template <typename Name, typename Value>
|
||||||
|
ppstate& newline_pretty_tag(std::uint32_t ci, Name && name, Value && value);
|
||||||
|
|
||||||
/** like pretty(x), but follow with trailing \n **/
|
/** like pretty(x), but follow with trailing \n **/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ppstate& prettyn(T && x);
|
ppstate& prettyn(T && x);
|
||||||
|
|
@ -165,6 +210,7 @@ namespace xo {
|
||||||
|
|
||||||
void check_commit() {
|
void check_commit() {
|
||||||
if (pps_) {
|
if (pps_) {
|
||||||
|
/* nuke state to prevent duplicate commit */
|
||||||
ppstate * pps = pps_;
|
ppstate * pps = pps_;
|
||||||
pps_ = nullptr;
|
pps_ = nullptr;
|
||||||
if (pps->decr_nesting_level() == 0)
|
if (pps->decr_nesting_level() == 0)
|
||||||
|
|
@ -175,42 +221,59 @@ namespace xo {
|
||||||
ppstate * pps_ = nullptr;
|
ppstate * pps_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename StructName, typename... Members>
|
||||||
bool
|
std::uint32_t
|
||||||
ppdetail_atomic<T>::print_upto(ppstate * pps, const T & x)
|
ppstate::pretty_struct(const ppindentinfo & ppii, StructName && structname, Members&&... members)
|
||||||
{
|
{
|
||||||
/* position before we write */
|
if (ppii.upto()) {
|
||||||
if (!pps->has_margin())
|
return (this->print_upto("<")
|
||||||
|
&& this->print_upto(structname)
|
||||||
|
&& this->print_upto_struct_members(ppii, members...)
|
||||||
|
&& this->print_upto(">"));
|
||||||
|
} else {
|
||||||
|
this->write("<");
|
||||||
|
this->write(structname);
|
||||||
|
this->pretty_struct_members(ppii, members...);
|
||||||
|
this->write(">");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
std::uint32_t start = pps->pos();
|
|
||||||
|
|
||||||
/* if x is non-atomic may optimize by checking budget more often */
|
|
||||||
pps->write(x);
|
|
||||||
|
|
||||||
if (!pps->has_margin())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* scan characters [lo, hi) in line buffer.
|
|
||||||
* Newline -> caller should use print_pretty()
|
|
||||||
*/
|
|
||||||
return (pps->scan_no_newline(start));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename Member, typename... Rest>
|
||||||
void
|
std::uint32_t
|
||||||
ppdetail_atomic<T>::print_pretty(ppstate * pps, const T & x)
|
ppstate::print_upto_struct_members(const ppindentinfo & ppii,
|
||||||
|
Member && member,
|
||||||
|
Rest&&... rest)
|
||||||
{
|
{
|
||||||
/* In default implementation we don't know where to introduce newlines.
|
if (this->print_upto(" ") && this->print_upto(member))
|
||||||
* Still need to calculate lpos though
|
return this->print_upto_struct_members(ppii, rest...);
|
||||||
*/
|
|
||||||
pps->write(x);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Member, typename... Rest>
|
||||||
|
void
|
||||||
|
ppstate::pretty_struct_members(const ppindentinfo & ppii,
|
||||||
|
Member && member,
|
||||||
|
Rest&&... rest)
|
||||||
|
{
|
||||||
|
newline_indent(ppii.ci1());
|
||||||
|
this->pretty(member);
|
||||||
|
this->pretty_struct_members(ppii, rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool
|
bool
|
||||||
ppstate::print_upto(T && value) {
|
ppstate::print_upto(T && value) {
|
||||||
return ppdetail<std::decay_t<T>>::print_upto(this, value);
|
std::uint32_t saved = pos();
|
||||||
|
|
||||||
|
if (!has_margin())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ppdetail<std::decay_t<T>>::print_pretty(this->indent_info(true /*upto*/), value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return scan_no_newline(saved);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
@ -220,12 +283,13 @@ namespace xo {
|
||||||
* Decision depends on ancestor context
|
* Decision depends on ancestor context
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::uint32_t saved = pos();
|
auto saved = scratch_sbuf_->checkpoint();
|
||||||
|
|
||||||
ppcommitter ppc(this);
|
ppcommitter ppc(this);
|
||||||
|
|
||||||
/* print_upto() modifies @ref scratch_sbuf_ (and @ref scratch_ss_) */
|
/* print_pretty() modifies @ref scratch_sbuf_ (and @ref scratch_ss_) */
|
||||||
bool fits = ppdetail<std::decay_t<T>>::print_upto(this, static_cast<const T &>(value));
|
bool fits = ppdetail<std::decay_t<T>>::print_pretty(this->indent_info(true /*upto*/),
|
||||||
|
static_cast<const T &>(value));
|
||||||
|
|
||||||
if (fits) {
|
if (fits) {
|
||||||
/* fits on 1 line -> pretty-printing maybe not required */
|
/* fits on 1 line -> pretty-printing maybe not required */
|
||||||
|
|
@ -235,7 +299,7 @@ namespace xo {
|
||||||
/* here: didn't fit -> split over multiple lines */
|
/* here: didn't fit -> split over multiple lines */
|
||||||
|
|
||||||
this->scratch_sbuf_->rewind_to(saved);
|
this->scratch_sbuf_->rewind_to(saved);
|
||||||
ppdetail<std::decay_t<T>>::print_pretty(this, value);
|
ppdetail<std::decay_t<T>>::print_pretty(this->indent_info(false), value);
|
||||||
ppc.check_commit();
|
ppc.check_commit();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -248,12 +312,12 @@ namespace xo {
|
||||||
* Decision depends on ancestor context
|
* Decision depends on ancestor context
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::uint32_t saved = pos();
|
auto saved = scratch_sbuf_->checkpoint();
|
||||||
|
|
||||||
ppcommitter ppc(this);
|
ppcommitter ppc(this);
|
||||||
|
|
||||||
/* print_upto() modifies @ref scratch_sbuf_ (and @ref scratch_ss_) */
|
/* print_pretty() modifies @ref scratch_sbuf_ (and @ref scratch_ss_) */
|
||||||
bool fits = ppdetail<std::decay_t<T>>::print_upto(this, value);
|
bool fits = ppdetail<std::decay_t<T>>::print_pretty(this->indent_info(true), value);
|
||||||
|
|
||||||
if (fits) {
|
if (fits) {
|
||||||
this->newline_indent(0);
|
this->newline_indent(0);
|
||||||
|
|
@ -264,12 +328,121 @@ namespace xo {
|
||||||
/* here: didn't fit -> split over multiple lines */
|
/* here: didn't fit -> split over multiple lines */
|
||||||
|
|
||||||
this->scratch_sbuf_->rewind_to(saved);
|
this->scratch_sbuf_->rewind_to(saved);
|
||||||
ppdetail<std::remove_reference_t<T>>::print_pretty(this, value);
|
ppdetail<std::remove_reference_t<T>>::print_pretty(this->indent_info(false), value);
|
||||||
this->newline_indent(0);
|
this->newline_indent(0);
|
||||||
ppc.check_commit();
|
ppc.check_commit();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool
|
||||||
|
ppdetail_atomic<T>::print_pretty(const ppindentinfo & ppii, const T & x)
|
||||||
|
{
|
||||||
|
if (ppii.upto()) {
|
||||||
|
std::uint32_t start = ppii.pps()->pos();
|
||||||
|
|
||||||
|
/* if x is non-atomic may optimize by checking budget more often */
|
||||||
|
ppii.pps()->write(x);
|
||||||
|
|
||||||
|
if (!ppii.pps()->has_margin())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* scan characters [lo, hi) in line buffer.
|
||||||
|
* Newline -> caller should use print_pretty()
|
||||||
|
*/
|
||||||
|
return (ppii.pps()->scan_no_newline(start));
|
||||||
|
} else {
|
||||||
|
/* In default implementation we don't know where to introduce newlines.
|
||||||
|
* Still need to calculate lpos though
|
||||||
|
*/
|
||||||
|
ppii.pps()->write(x);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename Tag>
|
||||||
|
struct ppdetail_tag {
|
||||||
|
static bool print_pretty(const ppindentinfo & ppii,
|
||||||
|
const Tag & tag)
|
||||||
|
{
|
||||||
|
if (ppii.upto()) {
|
||||||
|
if (tag.prefix_space())
|
||||||
|
ppii.pps()->write(" ");
|
||||||
|
|
||||||
|
/* must color here, because we may keep the output if it fits! */
|
||||||
|
if (!ppii.pps()->print_upto(with_color(color_spec_type::yellow(), // tag_config::tag_color,
|
||||||
|
concat((char const *)":", tag.name()))))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ppii.pps()->write(" ");
|
||||||
|
|
||||||
|
if (!ppii.pps()->print_upto(tag.value()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
/* pretty-print like
|
||||||
|
* :somename
|
||||||
|
* pretty(value)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (tag.prefix_space())
|
||||||
|
ppii.pps()->write(" ");
|
||||||
|
ppii.pps()->write(with_color(color_spec_type::yellow(), //tag_config::tag_color,
|
||||||
|
concat((char const *)":", tag.name())));
|
||||||
|
|
||||||
|
ppii.pps()->newline_indent(ppii.ci1());
|
||||||
|
ppii.pps()->pretty(tag.value());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
||||||
|
struct ppdetail<tag_impl<PrefixSpace, TagStyle, Name, Value>> {
|
||||||
|
static bool print_pretty(const ppindentinfo & ppii,
|
||||||
|
const tag_impl<PrefixSpace, TagStyle, Name, Value> & tag)
|
||||||
|
{
|
||||||
|
using tag_type = tag_impl<PrefixSpace, TagStyle, Name, Value>;
|
||||||
|
|
||||||
|
return detail::ppdetail_tag<tag_type>::print_pretty(ppii, tag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
||||||
|
struct ppdetail<ref_tag_impl<PrefixSpace, TagStyle, Name, Value>> {
|
||||||
|
static bool print_pretty(const ppindentinfo & ppii,
|
||||||
|
const ref_tag_impl<PrefixSpace, TagStyle, Name, Value> & tag)
|
||||||
|
{
|
||||||
|
using tag_type = ref_tag_impl<PrefixSpace, TagStyle, Name, Value>;
|
||||||
|
|
||||||
|
return detail::ppdetail_tag<tag_type>::print_pretty(ppii, tag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Name, typename Value>
|
||||||
|
bool
|
||||||
|
ppstate::print_upto_tag(Name && name, Value && value)
|
||||||
|
{
|
||||||
|
return this->print_upto(xrtag(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Name, typename Value>
|
||||||
|
ppstate &
|
||||||
|
ppstate::newline_pretty_tag(std::uint32_t ci, Name && name, Value && value)
|
||||||
|
{
|
||||||
|
newline_indent(ci);
|
||||||
|
this->pretty(rtag(name, value));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
} /*namespace print*/
|
} /*namespace print*/
|
||||||
} /*namespace xo*/
|
} /*namespace xo*/
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
/* @file pretty_concat.hpp
|
|
||||||
*
|
|
||||||
* author: Roland Conybeare, Jul 2025
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "concat.hpp"
|
|
||||||
#include "pretty.hpp"
|
|
||||||
|
|
||||||
namespace xo {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* end pretty_concat.hpp */
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
/* file pretty_tag.hpp
|
|
||||||
*
|
|
||||||
* author: Roland Conybeare, Jul 2025
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pretty.hpp"
|
|
||||||
#include "tag.hpp"
|
|
||||||
|
|
||||||
namespace xo {
|
|
||||||
namespace print {
|
|
||||||
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
|
||||||
struct ppdetail<tag_impl<PrefixSpace, TagStyle, Name, Value>> {
|
|
||||||
static bool print_upto(ppstate * pps, const tag_impl<PrefixSpace, TagStyle, Name, Value> & tag) {
|
|
||||||
std::uint32_t saved = pps->pos();
|
|
||||||
if (!pps->has_margin())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (PrefixSpace)
|
|
||||||
pps->write(" ");
|
|
||||||
|
|
||||||
/* skil colorizing here, since doesn't consume visible space */
|
|
||||||
if (!pps->print_upto(concat((char const *)":", concat(tag.name(), (char const *)" "))))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!pps->print_upto(quot_impl(TagStyle == tagstyle::autoescape, tag.value())))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return pps->scan_no_newline(saved);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_pretty(ppstate * pps, const tag_impl<PrefixSpace, TagStyle, Name, Value> & tag) {
|
|
||||||
/* pretty-print like
|
|
||||||
* :somename
|
|
||||||
* pretty(value)
|
|
||||||
*/
|
|
||||||
std::uint32_t ci0 = pps->lpos();
|
|
||||||
std::uint32_t ci1 = ci0 + pps->indent_width();
|
|
||||||
|
|
||||||
if (PrefixSpace)
|
|
||||||
pps->write(" ");
|
|
||||||
pps->write(with_color(tag_config::tag_color,
|
|
||||||
concat((char const *)":", tag.name())));
|
|
||||||
|
|
||||||
pps->newline_indent(ci1);
|
|
||||||
pps->pretty(tag.value());
|
|
||||||
|
|
||||||
pps->newline_indent(ci0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} /*namespace print*/
|
|
||||||
} /*namespace xo*/
|
|
||||||
|
|
||||||
/* end pretty_tag.hpp */
|
|
||||||
|
|
@ -11,56 +11,47 @@
|
||||||
|
|
||||||
namespace xo {
|
namespace xo {
|
||||||
namespace print {
|
namespace print {
|
||||||
template <typename T>
|
template <typename Vector>
|
||||||
struct ppdetail<std::vector<T>> {
|
struct ppdetail_vector {
|
||||||
static bool print_upto(ppstate * pps, const std::vector<T> & x) {
|
static bool print_pretty(const ppindentinfo & ppii, const Vector & x) {
|
||||||
std::uint32_t saved = pps->pos();
|
if (ppii.upto()) {
|
||||||
if (!pps->has_margin())
|
ppii.pps()->write("[");
|
||||||
|
for (size_t i = 0, z = x.size(); i < z; ++i) {
|
||||||
|
if (i > 0)
|
||||||
|
ppii.pps()->write(", ");
|
||||||
|
|
||||||
|
if (!ppii.pps()->print_upto(x[i]))
|
||||||
|
return false;
|
||||||
|
if (!ppii.pps()->has_margin())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ppii.pps()->write("]");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
ppii.pps()->write('[');
|
||||||
|
|
||||||
|
for (size_t i = 0, z = x.size(); i < z; ++i) {
|
||||||
|
if (i == 0)
|
||||||
|
ppii.pps()->indent(std::max(ppii.pps()->indent_width(), 1u) - 1);
|
||||||
|
else
|
||||||
|
ppii.pps()->newline_indent(ppii.ci1());
|
||||||
|
ppii.pps()->pretty(x[i]);
|
||||||
|
|
||||||
|
if (i+1 < z)
|
||||||
|
ppii.pps()->write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
ppii.pps()->write(" ]");
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
pps->write("[");
|
|
||||||
for (size_t i = 0, z = x.size(); i < z; ++i) {
|
|
||||||
if (i > 0)
|
|
||||||
pps->write(", ");
|
|
||||||
|
|
||||||
if (!pps->print_upto(x[i]))
|
|
||||||
return false;
|
|
||||||
if (!pps->has_margin())
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
pps->write("]");
|
|
||||||
if (!pps->has_margin())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return pps->scan_no_newline(saved);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_pretty(ppstate * pps, const std::vector<T> & x) {
|
|
||||||
std::uint32_t ci0 = pps->lpos();
|
|
||||||
|
|
||||||
pps->write('[');
|
|
||||||
|
|
||||||
std::uint32_t ci1 = ci0 + pps->indent_width();
|
|
||||||
for (size_t i = 0, z = x.size(); i < z; ++i) {
|
|
||||||
pps->newline_indent(ci1);
|
|
||||||
pps->pretty(x[i]);
|
|
||||||
|
|
||||||
if (i+1 < z)
|
|
||||||
pps->write(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
pps->newline_indent(ci0);
|
|
||||||
pps->write(']');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct ppdetail<const std::vector<T>> {
|
struct ppdetail<std::vector<T>> {
|
||||||
static bool print_upto(ppstate * pps, const std::vector<T> & x) {
|
static bool print_pretty(const ppindentinfo & ppii, const std::vector<T> & x) {
|
||||||
return ppdetail<std::vector<T>>::print_upto(pps, x);
|
return ppdetail_vector<std::vector<T>>::print_pretty(ppii, x);
|
||||||
}
|
|
||||||
static void print_pretty(ppstate * pps, const std::vector<T> & x) {
|
|
||||||
ppdetail<std::vector<T>>::print_pretty(pps, x);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,13 +103,13 @@ namespace xo {
|
||||||
} /*print*/
|
} /*print*/
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* .unq_flag: if true, omit surrounding " chars
|
/** .unq_flag: if true, omit surrounding " chars
|
||||||
* whenever printed value satisfies both:
|
* whenever printed value satisfies both:
|
||||||
* - no escaped chars
|
* - no escaped chars
|
||||||
* - no spaces
|
* - no spaces
|
||||||
*/
|
**/
|
||||||
bool unq_flag_ = false;
|
bool unq_flag_ = false;
|
||||||
/* .value: value to be printed */
|
/** .value: value to be printed **/
|
||||||
T value_;
|
T value_;
|
||||||
}; /*quot_impl*/
|
}; /*quot_impl*/
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ namespace xo {
|
||||||
return os;
|
return os;
|
||||||
} /*operator*/
|
} /*operator*/
|
||||||
|
|
||||||
/* writing out std::forward<T> behavior for completeness' sake:
|
/** writing out std::forward<T> behavior for completeness' sake:
|
||||||
*
|
*
|
||||||
* 1. call quoted(x) with rvalue std::string x, then:
|
* 1. call quoted(x) with rvalue std::string x, then:
|
||||||
* - T will be deduced to [std::string]
|
* - T will be deduced to [std::string]
|
||||||
|
|
@ -134,7 +134,7 @@ namespace xo {
|
||||||
* 2b. call quoted(x) with std::string const & x, then:
|
* 2b. call quoted(x) with std::string const & x, then:
|
||||||
* - T deduced to [std::string const &]
|
* - T deduced to [std::string const &]
|
||||||
* - std::string const & passed to quot_impl ctor
|
* - std::string const & passed to quot_impl ctor
|
||||||
*/
|
**/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto quot(T && x) {
|
auto quot(T && x) {
|
||||||
return quot_impl(false /*unq_flag*/, std::forward<T>(x));
|
return quot_impl(false /*unq_flag*/, std::forward<T>(x));
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace xo {
|
||||||
raw,
|
raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** K,V pair for printing.
|
/** key-value pair for printing.
|
||||||
*
|
*
|
||||||
* @tparam PrefixSpace if true print one space before :K
|
* @tparam PrefixSpace if true print one space before :K
|
||||||
* @tparam TagStyle controls printing format
|
* @tparam TagStyle controls printing format
|
||||||
|
|
@ -63,6 +63,8 @@ namespace xo {
|
||||||
tag_impl(Name && n, Value && v)
|
tag_impl(Name && n, Value && v)
|
||||||
: name_{std::forward<Name>(n)}, value_{std::forward<Value>(v)} {}
|
: name_{std::forward<Name>(n)}, value_{std::forward<Value>(v)} {}
|
||||||
|
|
||||||
|
constexpr bool prefix_space() const { return PrefixSpace; }
|
||||||
|
|
||||||
Name const & name() const { return name_; }
|
Name const & name() const { return name_; }
|
||||||
Value const & value() const { return value_; }
|
Value const & value() const { return value_; }
|
||||||
|
|
||||||
|
|
@ -71,20 +73,52 @@ namespace xo {
|
||||||
Value value_;
|
Value value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** key-value pair for printing.
|
||||||
|
* Like tag_impl<..> but uses reference for value.
|
||||||
|
* Use in context where value can't be a temporary rvalue.
|
||||||
|
**/
|
||||||
|
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
||||||
|
struct ref_tag_impl {
|
||||||
|
ref_tag_impl(Name const & n, Value const & v) : name_{n}, value_{v} {}
|
||||||
|
ref_tag_impl(Name && n, Value && v) : name_{std::forward<Name>(n)}, value_{std::forward<Value>(v)} {}
|
||||||
|
|
||||||
|
constexpr bool prefix_space() const { return PrefixSpace; }
|
||||||
|
|
||||||
|
Name const & name() const { return name_; }
|
||||||
|
Value const & value() const { return value_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Name name_;
|
||||||
|
const Value& value_;
|
||||||
|
};
|
||||||
|
|
||||||
// ----- xtag -----
|
// ----- xtag -----
|
||||||
|
|
||||||
|
/** Write name,value pair @p n, @p v with format like
|
||||||
|
* :name value
|
||||||
|
* Precede with initial space
|
||||||
|
* Escape whitespace/special characters in @p v
|
||||||
|
**/
|
||||||
template<typename Name, typename Value>
|
template<typename Name, typename Value>
|
||||||
auto //tag_impl<true, tagstyle::autoescape, Name, std::decay_t<Value>>
|
auto
|
||||||
xtag(Name && n, Value && v)
|
xtag(Name && n, Value && v)
|
||||||
{
|
{
|
||||||
return tag_impl<true, tagstyle::autoescape, Name, std::decay_t<Value>>(n, v);
|
return tag_impl<true, tagstyle::autoescape, std::decay_t<Name>, std::decay_t<Value>>(n, v);
|
||||||
} /*xtag*/
|
} /*xtag*/
|
||||||
|
|
||||||
template<typename Value>
|
// ----- xrtag ----
|
||||||
auto //tag_impl<true, tagstyle::autoescape, char const *, std::decay_t<Value>>
|
|
||||||
xtag(char const * n, Value && v) {
|
/** Write name,value pair @p n, @p v with format like
|
||||||
return tag_impl<true, tagstyle::autoescape, char const *, std::decay_t<Value>>(n, v);
|
* :name value
|
||||||
} /*xtag*/
|
* Precede with initial space.
|
||||||
|
* Do not escape whitespace/special characters in @p v.
|
||||||
|
**/
|
||||||
|
template<typename Name, typename Value>
|
||||||
|
auto
|
||||||
|
xrtag(Name && n, Value && v)
|
||||||
|
{
|
||||||
|
return tag_impl<true, tagstyle::raw, std::decay_t<Name>, std::decay_t<Value>>(n, v);
|
||||||
|
}
|
||||||
|
|
||||||
// ----- tag -----
|
// ----- tag -----
|
||||||
|
|
||||||
|
|
@ -102,6 +136,28 @@ namespace xo {
|
||||||
return tag_impl<false, tagstyle::autoescape, char const *, Value>(n, v);
|
return tag_impl<false, tagstyle::autoescape, char const *, Value>(n, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- rtag -----
|
||||||
|
|
||||||
|
template<typename Name, typename Value>
|
||||||
|
auto
|
||||||
|
rtag(Name && n, Value && v)
|
||||||
|
{
|
||||||
|
return tag_impl<false, tagstyle::raw, std::decay_t<Name>, std::decay_t<Value>>(n, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- refrtag -----
|
||||||
|
|
||||||
|
/** 'reference raw tag'.
|
||||||
|
* 1. @p v must survive until refrtag is used (i.e. until tag inserted into some stream)
|
||||||
|
* 2. don't escape whitespace when printing value (pretty-printing with parseable structure)
|
||||||
|
**/
|
||||||
|
template <typename Name, typename Value>
|
||||||
|
auto
|
||||||
|
refrtag(Name && n, Value && v)
|
||||||
|
{
|
||||||
|
return ref_tag_impl<false, tagstyle::raw, std::decay_t<Name>, std::decay_t<Value>>(n, v);
|
||||||
|
}
|
||||||
|
|
||||||
// ----- operator<< on tag_impl -----
|
// ----- operator<< on tag_impl -----
|
||||||
|
|
||||||
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
||||||
|
|
@ -124,6 +180,29 @@ namespace xo {
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
} /*operator<<*/
|
} /*operator<<*/
|
||||||
|
|
||||||
|
// ----- operator<< on ref_tag_impl -----
|
||||||
|
|
||||||
|
template <bool PrefixSpace, tagstyle TagStyle, typename Name, typename Value>
|
||||||
|
inline std::ostream &
|
||||||
|
operator<<(std::ostream &s,
|
||||||
|
ref_tag_impl<PrefixSpace, TagStyle, Name, Value> const & tag)
|
||||||
|
{
|
||||||
|
using xo::print::unq;
|
||||||
|
|
||||||
|
if (PrefixSpace)
|
||||||
|
s << " ";
|
||||||
|
|
||||||
|
s << with_color(tag_config::tag_color, concat((char const *)":", tag.name()))
|
||||||
|
<< " ";
|
||||||
|
|
||||||
|
if (TagStyle == tagstyle::autoescape)
|
||||||
|
s << unq(tag.value());
|
||||||
|
else
|
||||||
|
s << tag.value();
|
||||||
|
|
||||||
|
return s;
|
||||||
|
} /*operator<<*/
|
||||||
} /*namespace xo*/
|
} /*namespace xo*/
|
||||||
|
|
||||||
/* end tag.hpp */
|
/* end tag.hpp */
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
#include "print/filename.hpp"
|
#include "print/filename.hpp"
|
||||||
#include "print/ppstr.hpp"
|
#include "print/ppstr.hpp"
|
||||||
#include "print/tostr.hpp"
|
#include "print/tostr.hpp"
|
||||||
#include "print/pretty_concat.hpp"
|
|
||||||
#include "print/pretty_tag.hpp"
|
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue