format: redesign how grouping behaves so it's more sensible
This commit is contained in:
@@ -64,7 +64,8 @@ expressions<CharT> parseBracket(const string<CharT> &s,
|
||||
string<CharT> token;
|
||||
expressions<CharT> tmp, result;
|
||||
auto push_token = [&] {
|
||||
result.push_back(std::move(token));
|
||||
if (!token.empty())
|
||||
result.push_back(std::move(token));
|
||||
};
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
@@ -72,7 +73,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
|
||||
{
|
||||
push_token();
|
||||
bool done;
|
||||
Format::Any<CharT> any;
|
||||
Format::FirstOf<CharT> first_of;
|
||||
do
|
||||
{
|
||||
auto jt = it;
|
||||
@@ -95,11 +96,11 @@ expressions<CharT> parseBracket(const string<CharT> &s,
|
||||
// recursively parse the bracket
|
||||
tmp = parseBracket(s, it, jt, flags);
|
||||
// if the inner bracket contains only one expression,
|
||||
// put it as is. otherwise require all of them.
|
||||
// put it as is. otherwise make a group out of them.
|
||||
if (tmp.size() == 1)
|
||||
any.base().push_back(std::move(tmp[0]));
|
||||
first_of.base().push_back(std::move(tmp[0]));
|
||||
else
|
||||
any.base().push_back(Format::All<CharT>(std::move(tmp)));
|
||||
first_of.base().push_back(Format::Group<CharT>(std::move(tmp)));
|
||||
it = jt;
|
||||
// check for the alternative
|
||||
++jt;
|
||||
@@ -114,13 +115,8 @@ expressions<CharT> parseBracket(const string<CharT> &s,
|
||||
}
|
||||
}
|
||||
while (!done);
|
||||
assert(!any.base().empty());
|
||||
// if there was only one bracket, append empty branch
|
||||
// so that it always evaluates to true. otherwise put
|
||||
// it as is.
|
||||
if (any.base().size() == 1)
|
||||
any.base().push_back(string<CharT>());
|
||||
result.push_back(std::move(any));
|
||||
assert(!first_of.base().empty());
|
||||
result.push_back(std::move(first_of));
|
||||
}
|
||||
else if (flags & Format::Flags::Tag && *it == '%')
|
||||
{
|
||||
|
||||
120
src/format.h
120
src/format.h
@@ -40,11 +40,11 @@ const unsigned Tag = 8;
|
||||
const unsigned All = Color | Format | OutputSwitch | Tag;
|
||||
}
|
||||
|
||||
enum class ListType { All, Any, AST };
|
||||
enum class ListType { Group, FirstOf, AST };
|
||||
|
||||
template <ListType, typename> struct List;
|
||||
template <typename CharT> using All = List<ListType::All, CharT>;
|
||||
template <typename CharT> using Any = List<ListType::Any, CharT>;
|
||||
template <typename CharT> using Group = List<ListType::Group, CharT>;
|
||||
template <typename CharT> using FirstOf = List<ListType::FirstOf, CharT>;
|
||||
template <typename CharT> using AST = List<ListType::AST, CharT>;
|
||||
|
||||
struct OutputSwitch { };
|
||||
@@ -63,6 +63,41 @@ private:
|
||||
unsigned m_delimiter;
|
||||
};
|
||||
|
||||
enum class Result { Empty, Missing, Ok };
|
||||
|
||||
// Commutative binary operation such that:
|
||||
// - Empty + Empty = Empty
|
||||
// - Empty + Missing = Missing
|
||||
// - Empty + Ok = Ok
|
||||
// - Missing + Missing = Missing
|
||||
// - Missing + Ok = Missing
|
||||
// - Ok + Ok = Ok
|
||||
inline Result &operator+=(Result &base, Result result)
|
||||
{
|
||||
if (base == Result::Missing || result == Result::Missing)
|
||||
base = Result::Missing;
|
||||
else if (base == Result::Ok || result == Result::Ok)
|
||||
base = Result::Ok;
|
||||
return base;
|
||||
}
|
||||
|
||||
/*inline std::ostream &operator<<(std::ostream &os, Result r)
|
||||
{
|
||||
switch (r)
|
||||
{
|
||||
case Result::Empty:
|
||||
os << "empty";
|
||||
break;
|
||||
case Result::Missing:
|
||||
os << "missing";
|
||||
break;
|
||||
case Result::Ok:
|
||||
os << "ok";
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}*/
|
||||
|
||||
template <typename CharT>
|
||||
using Expression = boost::variant<
|
||||
std::basic_string<CharT>,
|
||||
@@ -70,8 +105,8 @@ using Expression = boost::variant<
|
||||
NC::Format,
|
||||
OutputSwitch,
|
||||
SongTag,
|
||||
boost::recursive_wrapper<Any<CharT>>,
|
||||
boost::recursive_wrapper<All<CharT>>
|
||||
boost::recursive_wrapper<FirstOf<CharT>>,
|
||||
boost::recursive_wrapper<Group<CharT>>
|
||||
>;
|
||||
|
||||
template <ListType Type, typename CharT>
|
||||
@@ -92,7 +127,7 @@ private:
|
||||
};
|
||||
|
||||
template <typename CharT, typename OutputT, typename SecondOutputT = OutputT>
|
||||
struct Printer: boost::static_visitor<bool>
|
||||
struct Printer: boost::static_visitor<Result>
|
||||
{
|
||||
typedef std::basic_string<CharT> StringT;
|
||||
|
||||
@@ -105,34 +140,39 @@ struct Printer: boost::static_visitor<bool>
|
||||
, m_flags(flags)
|
||||
{ }
|
||||
|
||||
bool operator()(const StringT &s)
|
||||
Result operator()(const StringT &s)
|
||||
{
|
||||
output(s);
|
||||
return true;
|
||||
if (!s.empty())
|
||||
{
|
||||
output(s);
|
||||
return Result::Ok;
|
||||
}
|
||||
else
|
||||
return Result::Empty;
|
||||
}
|
||||
|
||||
bool operator()(const NC::Color &c)
|
||||
Result operator()(const NC::Color &c)
|
||||
{
|
||||
if (m_flags & Flags::Color)
|
||||
output(c);
|
||||
return true;
|
||||
return Result::Empty;
|
||||
}
|
||||
|
||||
bool operator()(NC::Format fmt)
|
||||
Result operator()(NC::Format fmt)
|
||||
{
|
||||
if (m_flags & Flags::Format)
|
||||
output(fmt);
|
||||
return true;
|
||||
return Result::Empty;
|
||||
}
|
||||
|
||||
bool operator()(OutputSwitch)
|
||||
Result operator()(OutputSwitch)
|
||||
{
|
||||
if (!m_no_output)
|
||||
m_output_switched = true;
|
||||
return true;
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
bool operator()(const SongTag &st)
|
||||
Result operator()(const SongTag &st)
|
||||
{
|
||||
StringT tags;
|
||||
if (m_flags & Flags::Tag && m_song != nullptr)
|
||||
@@ -152,40 +192,46 @@ struct Printer: boost::static_visitor<bool>
|
||||
tags = wideShorten(tags, st.delimiter());
|
||||
}
|
||||
output(tags);
|
||||
return true;
|
||||
return Result::Ok;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return Result::Missing;
|
||||
}
|
||||
|
||||
bool operator()(const All<CharT> &all)
|
||||
// If all Empty -> Empty, if any Ok -> continue with Ok, if any Missing -> stop with Empty.
|
||||
Result operator()(const Group<CharT> &group)
|
||||
{
|
||||
auto visit = [this, &all] {
|
||||
return std::all_of(
|
||||
all.base().begin(),
|
||||
all.base().end(),
|
||||
[this](const Expression<CharT> &ex) {
|
||||
return boost::apply_visitor(*this, ex);
|
||||
auto visit = [this, &group] {
|
||||
Result result = Result::Empty;
|
||||
for (const auto &ex : group.base())
|
||||
{
|
||||
result += boost::apply_visitor(*this, ex);
|
||||
if (result == Result::Missing)
|
||||
{
|
||||
result = Result::Empty;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
++m_no_output;
|
||||
bool all_ok = visit();
|
||||
Result result = visit();
|
||||
--m_no_output;
|
||||
if (!m_no_output && all_ok)
|
||||
if (!m_no_output && result == Result::Ok)
|
||||
visit();
|
||||
return all_ok;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator()(const Any<CharT> &any)
|
||||
// If all Empty or Missing -> Empty, if any Ok -> stop with Ok.
|
||||
Result operator()(const FirstOf<CharT> &first_of)
|
||||
{
|
||||
return std::any_of(
|
||||
any.base().begin(),
|
||||
any.base().end(),
|
||||
[this](const Expression<CharT> &ex) {
|
||||
return boost::apply_visitor(*this, ex);
|
||||
}
|
||||
);
|
||||
for (const auto &ex : first_of.base())
|
||||
{
|
||||
if (boost::apply_visitor(*this, ex) == Result::Ok)
|
||||
return Result::Ok;
|
||||
}
|
||||
return Result::Empty;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -125,14 +125,14 @@ Format::AST<char> columns_to_format(const std::vector<Column> &columns)
|
||||
auto column = columns.begin();
|
||||
while (true)
|
||||
{
|
||||
Format::Any<char> any;
|
||||
Format::FirstOf<char> first_of;
|
||||
for (const auto &type : column->type)
|
||||
{
|
||||
auto f = charToGetFunction(type);
|
||||
assert(f != nullptr);
|
||||
any.base().push_back(f);
|
||||
first_of.base().push_back(f);
|
||||
}
|
||||
result.push_back(std::move(any));
|
||||
result.push_back(std::move(first_of));
|
||||
|
||||
if (++column != columns.end())
|
||||
result.push_back(" ");
|
||||
|
||||
Reference in New Issue
Block a user